24

Suppose I have some library lib.jar for which I do not have the source code (or it is written in some non-Java language which is unaware of modules yet). lib.jar does not have module-info.class and I do not want to use it as an automatic module, so I would like to inject module-info.class into it.

I first generate module-info.java with the following command:

jdeps --generate-module-info . lib.jar

Suppose this generated something like that:

module lib {
    exports package1;
    exports package2;
}

Then I try to compile it but javac fails because the packages package1 and package2 do not exist:

> javac module-info.java
module-info.java:4: error: package is empty or does not exist: package1

Of course, I can create directories package1 and package2 with dummy classes in them, but is there some better approach?

ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155

3 Answers3

23

Yes, this is possible with the --patch-module option. This option is most often used at runtime, but it also works at compile time:

javac --patch-module <module name>=<path to jar> module-info.java
ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155
  • when I use this command, I still get the error: > javac module-info.java module-info.java:4: error: package is empty or does not exist: package1 Any ideia why it might not working for me? – Tiago Santos Nov 22 '17 at 08:52
  • @TiagoSantos Does the package exist in the JAR? – ZhekaKozlov Nov 22 '17 at 10:33
  • yes, everything seems fine, I managed to have it working using the solution above from nullpointer, I do agree that your solution is more elegant, the only problem is that isn't working with my .jars :/ tried in 2 different ones – Tiago Santos Nov 22 '17 at 10:36
  • @TiagoSantos Have you replaced `lib` with the actual module name? – ZhekaKozlov Nov 22 '17 at 11:39
  • @TiagoSantos I modified my answer because that was indeed unobvious that `lib` should be substituted with the module name. – ZhekaKozlov Nov 22 '17 at 11:47
  • ahhh! indeed was not obvious :D thanks for the update – Tiago Santos Nov 23 '17 at 14:21
  • 26
    For anyone attempting to update a legacy JAR with a generated module-info, this sequence of commands is what I got to work: `jdeps --generate-module-info . ` `javac --patch-module = /module-info.java` `jar uf -C module-info.class` – Philip Guin Dec 23 '17 at 09:57
  • 1
    The addition by Philip is important, as the command in the answer does not inject anything but only compiles module-info.java into module-info.class for a jar. The class is then added to the jar using jar command. – Андрей Вахрушев Nov 12 '21 at 16:11
  • This worked for me also. I used it for JSoup jar and the module-info.java was effectively empty, other the the name of org.jsoup. I using this with JPackage. – Peter Jerkewitz Jul 31 '23 at 15:28
8

Alternatively, to compile the module-info.java generated you need to also extract the contents of the JAR to a directory.

Then compile the module-info.java with the output directory (-d) set to the directory where you extracted the contents.

Credits :- Alan

Naman
  • 27,789
  • 26
  • 218
  • 353
  • 1
    I accepted my own answer because I think my way is slightly more elegant. Extracting files is an extra step which is a bit inconvenient (especially for tools). – ZhekaKozlov Nov 15 '17 at 07:23
0

If you are using Maven and the original library is available there, you can use the add-module-info goal of the moditect plugin. Below is a snippet of pom.xml I used to patch the h2 jdbc driver and engine. The plugin creates a patched copy of the plugin in ${project.build.directory}/modules which can then be used e.g. jlinked in.

<plugin>
    <groupId>org.moditect</groupId>
    <artifactId>moditect-maven-plugin</artifactId>
    <version>1.0.0.RC2</version>
    <executions>
        <execution>
            <id>add-module-infos</id>
            <phase>generate-resources</phase>
            <goals>
                <goal>add-module-info</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/modules</outputDirectory>
                <modules>
                    <module>
                        <artifact>
                            <groupId>com.h2database</groupId>
                            <artifactId>h2</artifactId>
                            <version>2.1.214</version>
                        </artifact>
                        <moduleInfoSource>
                            module com.h2database {
                            requires java.compiler;
                            requires jdk.net;
                            requires static lucene.core;
                            requires static lucene.queryparser;
                            requires static slf4j.api;
                            requires static jakarta.servlet;
                            requires transitive java.desktop;
                            requires transitive java.instrument;
                            requires java.logging;
                            requires transitive java.management;
                            requires static java.naming;
                            requires transitive java.scripting;
                            requires java.sql;
                            requires transitive java.transaction.xa;
                            requires transitive java.xml;
                            requires static javax.servlet.api;
                            requires static org.locationtech.jts;
                            requires static org.osgi.service.jdbc;
                            requires static osgi.core;
                            provides java.sql.Driver with org.h2.Driver;
                            }
                        </moduleInfoSource>
                    </module>
                </modules>
            </configuration>
        </execution>
    </executions>
</plugin>