15

My Java library should be compatible with Java 8 and Java 9. For running with Java 9 we need some of the Java 9 modules.

I know that I can add it via command line with --add-modules. But it is a library and I can't control the command line.

Is there an equivalent of --add-modules in MANIFEST.MF? Or is there any other solution that is compatible with Java 8?

ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155
Horcrux7
  • 23,758
  • 21
  • 98
  • 156
  • What exact Java 9 module do you need? – ZhekaKozlov Apr 25 '17 at 09:57
  • 1
    Current we need java.activation and java.xml.bind. – Horcrux7 Apr 25 '17 at 10:39
  • @Horcrux7 If the two answers address your question you should accept one of them. Otherwise please comment on them. – Nicolai Parlog May 09 '17 at 09:28
  • 1
    @Nicolai Because we need to be compatible with Java 8 we can not use a module path. Currently many of our customer use Java 7 that I thing we will need compatible with Java 8 for many years. – Horcrux7 May 12 '17 at 08:32
  • Just to clarify: If done right, your Java 9 modules will continue to function on Java 8 but you would need a different command to launch them. – Nicolai Parlog May 12 '17 at 10:16
  • 1
    The use case is a program A that use a library B. It will be installed on a computer. The program run with the installed Java 8 on the computer. After some time the Java runtime will update to Java 9 (approx. a year after release). There is no change in the installed program/library and now it is break. The program user will not understand this breakage. That it is important that a program that is distribute today must be compatible with the future Java 9. – Horcrux7 May 12 '17 at 12:02
  • Most installable Java programs have a script or binary that launches them. It should make provisions which Java version it is on and act accordingly. – Nicolai Parlog May 12 '17 at 12:57

3 Answers3

9

Unfortunately, there is no equivalent of --add-modules in MANIFEST.MF. However, you can create module-info.java and declare your dependency there:

module <module-name> {
    requires <dependency>;
    ...
}

However, if you compile module-info.java and simply put module-info.class to your JAR, it may not work on some platforms (e.g. Android). So what to do? There is a new feature in Java 9: multi-release JAR files (JEP 238). The idea is that you can put Java 9 class files to a special directory (META-INF/version/9/) and Java 9 will properly handle them (while Java 8 will ignore them).

So, these are the steps that you should perform:

  • Compile all classes except module-info.java with javac --release 8
  • Compile module-info.java with javac --release 9.
  • Build a JAR file so it will have the following structure:
JAR root
  - A.class
  - B.class
  - C.class
   ...
  - META-INF
    - versions
      - 9
        - module-info.class

The resulting JAR should be compatible with Java 8 and Java 9.

Also, you can just put module-info.class to the META-INF folder. This will have the same effect. This, however, may not work for some tools and platforms. So I think the first way is more preferable.

ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155
  • I have try with it with module-infoin the root and in the versions 9 path and it seems not to work. Can it be that the module-info is only read if you start a module with command line parameter -m? – Horcrux7 Apr 25 '17 at 11:21
  • 2
    @Horcrux7 Yes, your JAR must be included into the module path. Otherwise, Java will ignore `module-info`. – ZhekaKozlov Apr 25 '17 at 11:27
  • 8
    The above advice is incorrect. There's no need to create a multi-release JAR file here. You can just put the `module-info.class` file in the root directory of the JAR. Java 8, and even Java 9, will ignore that file when the JAR is placed on the class path. Java 9 will read that file, and resolve any dependences it declares, when the JAR is placed on the module path. – Mark Reinhold Apr 26 '17 at 14:46
  • 2
    @MarkReinhold You are right, thank you. I updated my answer. – ZhekaKozlov Apr 27 '17 at 01:46
6

Modules express dependencies in their module declaration, so you have to create a module-info.java, define your module's name, dependencies (in your case with requires java.activation and requires java.xml.bind) and exports (more on that later).

The module declaration must be compiled by a Java 9 compiler to create a module descriptor module-info.class that belongs into the JAR's root folder.

Java 8 and 9

Java versions prior to 9 will ignore the module-info.class, which means if you compile the rest of your code for Java 8 (either by using javac 8 or by using the new --release flag on javac 9), your library still functions on that version.

Will it solve your problem?

Even Java 9 will only process your JAR as a module if it ends up on the module path. Only then will it see the requires clauses and include the Java EE modules you are using in the module graph. This means client using your library on Java 9's class path will still have to manually add the two modules via command line.

Full modularization

If your module is used on the module path, the accessibility rules make sure that:

  1. your clients can only use public types in packages you exported (at compile and at run time)
  2. your code only sees modules you depend on

This means:

  1. you must include exports in your module declaration
  2. you must declare all dependencies, not just on the two JDK modules

Particularly the second point can be tough if you depend on projects that are not yet modularized but that's another question.

Nicolai Parlog
  • 47,972
  • 24
  • 125
  • 255
  • A Java 8 compatible command line like "java -jar app.jar" will not use the module path. That all based on the module path will not work. – Horcrux7 May 12 '17 at 08:33
  • If there is no way to use a different command on different Java versions, then your problem can not be solved. :( – Nicolai Parlog May 12 '17 at 10:07
  • 1
    It is library. How should a library control the command line? – Horcrux7 May 12 '17 at 11:54
  • Well, _someone_ writes that command - they put the library on the class path after all. ;) They just have to be aware of what they're doing. and get used to Java 9 requiring some changes. – Nicolai Parlog May 12 '17 at 12:59
  • On Windows systems, the user has no control over the command line. Running an executable JAR file as Horcrux listed is done by double-clicking the JAR file, and Windows runs "java -jar name_of_file.jar", with no way for the user to change that. Developers would need to also deploy a launcher or JNLP or similar, but those don't always have the capacity to change actions based on installed Java versions. It's a terrible mess. – Ti Strga Oct 26 '17 at 14:58
4

Can be that there will be the needed manifest parameters in the last minutes of Jigsaw. The disadvantages, it will only work for the main jar file.

Sample:

Add-Exports: java.base/java.lang java.base/java.lang.invoke

or

Add-Exports-Private: java.base/java.lang java.base/java.lang.invoke

http://openjdk.java.net/projects/jigsaw/spec/issues/#AddExportsInManifest http://mail.openjdk.java.net/pipermail/jpms-spec-experts/2016-September/000391.html

Horcrux7
  • 23,758
  • 21
  • 98
  • 156