Using legacy jar files with jlink

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Using legacy jar files with jlink

Thomas Brand
Hi List,

this is my first post here, I hope this is the right place to ask a
question about how to use jlink.

For a given scenario with modules a and b, a using methods of b, following
instructions I managed to create a runnable image that can be called (from
inside exploded image) like

$ ./bin/java a.A
A.main() called
B() called
string from B:test()

this works nicely, also both modules are shown with
$ ./bin/java --list-modules
a
b
java.base@10-internal

My question is if it's possible to create that image if module b would be
a legacy jar with just the .class files in it. I understand it's possible
to auto-generate the module-info.java for this legacy b.jar.

I was thinking to be smart and just compile the generated module-info.java
for b.jar and then put it to the legacy b.jar or put the module-info.class
to the classpath in order to make it modular and then use for the jlink
step.
However that doesn't work:

$JAVAC -d compile_out src/modb/module-info.java
src/modb/module-info.java:2: error: package is empty or does not exist: b
    exports b;
            ^
1 error

How can a legacy jar file without access to source files be used as a
module, is it possible at all? The idea is to use legacy jar files in
conjunction with jlink to be used in self-contained runtime images.

Thanks for suggestions,
best regards
Thomas Brand



Reply | Threaded
Open this post in threaded view
|

Re: Using legacy jar files with jlink

mark.reinhold
2017/11/16 15:03:37 -0800, Thomas Brand <[hidden email]>:
> this is my first post here, I hope this is the right place to ask a
> question about how to use jlink.

Yes, it is!

> For a given scenario with modules a and b, a using methods of b, following
> instructions I managed to create a runnable image that can be called (from
> inside exploded image) like
>
> $ ./bin/java a.A
> A.main() called
> B() called
> string from B:test()
>
> this works nicely, also both modules are shown with
> $ ./bin/java --list-modules
> a
> b
> java.base@10-internal
>
> My question is if it's possible to create that image if module b would be
> a legacy jar with just the .class files in it.

No.  There's no place in the image to store arbitrary JAR files, and
even if there were there'd be no way to guarantee that all the other
JAR files required by those JAR files are present, since they're just
JAR files rather than modules.  An image built by jlink is intended to
be complete, consistent, and runnable as-is rather than something that
might need additional JAR files just in order to launch.

>                                                I understand it's possible
> to auto-generate the module-info.java for this legacy b.jar.
>
> I was thinking to be smart and just compile the generated module-info.java
> for b.jar and then put it to the legacy b.jar or put the module-info.class
> to the classpath in order to make it modular and then use for the jlink
> step.
> However that doesn't work:
>
> $JAVAC -d compile_out src/modb/module-info.java
> src/modb/module-info.java:2: error: package is empty or does not exist: b
>     exports b;
>             ^
> 1 error

To make this work, copy the content of b.jar into compile_out before you
run javac so that the compiler can detect that the package is not empty.

> How can a legacy jar file without access to source files be used as a
> module, is it possible at all? The idea is to use legacy jar files in
> conjunction with jlink to be used in self-contained runtime images.

It's possible, as above, but it can fail in many ways.  If the legacy
JAR files contain conflicting packages then the module system won't be
able to load them.  Your generated module-info.java files could have
missing dependences, especially if any of the legacy code uses
reflection.

It's really best to modularize an existing component properly, at the
source.  If you can't do that, for whatever reason, then it's generally
safer to leave legacy JAR files on the class path.  You can still use
jlink to generate a custom image containing just the modules that you
need; you'll just have to run that image with a non-empty class path.

- Mark
Reply | Threaded
Open this post in threaded view
|

Re: Using legacy jar files with jlink

Thomas Brand

Thanks for the quick answer!

On Fri, November 17, 2017 00:35, [hidden email] wrote:

> 2017/11/16 15:03:37 -0800, Thomas Brand <[hidden email]>:
>
>> this is my first post here, I hope this is the right place to ask a
>> question about how to use jlink.
>
> Yes, it is!
>
>
>> For a given scenario with modules a and b, a using methods of b,
>> following instructions I managed to create a runnable image that can be
>> called (from inside exploded image) like
>>
>> $ ./bin/java a.A
>> A.main() called
>> B() called
>> string from B:test()
>>
>> this works nicely, also both modules are shown with $ ./bin/java
>> --list-modules
>> abjava.base@10-internal
>>
>> My question is if it's possible to create that image if module b would
>> be a legacy jar with just the .class files in it.
>
> No.  There's no place in the image to store arbitrary JAR files, and
> even if there were there'd be no way to guarantee that all the other JAR
> files required by those JAR files are present, since they're just JAR
> files rather than modules.  An image built by jlink is intended to be
> complete, consistent, and runnable as-is rather than something that might
> need additional JAR files just in order to launch.
>
>> I understand it's possible
>> to auto-generate the module-info.java for this legacy b.jar.
>>
>> I was thinking to be smart and just compile the generated
>> module-info.java for b.jar and then put it to the legacy b.jar or put
>> the module-info.class to the classpath in order to make it modular and
>> then use for the jlink step. However that doesn't work:
>>
>>
>> $JAVAC -d compile_out src/modb/module-info.java
>> src/modb/module-info.java:2: error: package is empty or does not exist:
>> b exports b; ^
>> 1 error
>>
>
> To make this work, copy the content of b.jar into compile_out before you
> run javac so that the compiler can detect that the package is not empty.
>

That was the issue, it works now. I also tried using -cp to point to the
legacy jar but obviously it's different when putting the contents to
compile_out.

>> How can a legacy jar file without access to source files be used as a
>> module, is it possible at all? The idea is to use legacy jar files in
>> conjunction with jlink to be used in self-contained runtime images.
>
> It's possible, as above, but it can fail in many ways.  If the legacy
> JAR files contain conflicting packages then the module system won't be
> able to load them.  Your generated module-info.java files could have
> missing dependences, especially if any of the legacy code uses reflection.
>

Good to know to keep an eye on that.

>
> It's really best to modularize an existing component properly, at the
> source.  If you can't do that, for whatever reason, then it's generally
> safer to leave legacy JAR files on the class path.  You can still use
> jlink to generate a custom image containing just the modules that you
> need; you'll just have to run that image with a non-empty class path.
>

That's another welcome hint to explore. It could well be interesting to
set a system library path for .so files that are packed to the image and
loaded at runtime by modules IIUC.

I read on https://jnbridge.com/blog/legacy-developers-guide-java-9:

-----%<-----
It’s also possible that you might not want to modularize a JAR file, or
that the JAR file belongs to someone else, so you can’t modularize it
yourself. In that case, you can still put the JAR file into the module
path; it becomes an automatic module. An automatic module is considered a
module even though it doesn’t have a module-info.class file.
...
This means that it’s possible to make an unmodularized classpath JAR file
into a module with no work at all: Legacy JAR files become modules
automatically, albeit without some of the information needed to determine
whether all required modules are really there, and to determine what is
missing.
----->%-----

So I tried
$ $JLINK --module-path jar:legacy_jar:$JAVA_HOME/jmods --add-modules a
--output distimage
Error: module-info.class not found for b module

When compiling module-info.java with the extracted b.jar as suggested
above, repacking b.jar with included module-info.class doesn't show an
error:

$ $JLINK --module-path jar:legacy_pimped_jar/:$JAVA_HOME/jmods
--add-modules a --output distimage

And the image works.

Thanks again for your valuable hints. I might come back again later.
Best regards
Thomas


> - Mark
>
>