jpype icon indicating copy to clipboard operation
jpype copied to clipboard

jdeps does not detect jdk.zipfs dependency

Open Thrameos opened this issue 3 years ago • 3 comments

One of the tools required for jlink to build a limited jre distribution is jdeps. Jdeps scans a jar file for module dependencies. Unfortunately it can't detect any dependency which is invoked using reflection. That included filesystem dependencies. This means that trying to get the dependency list of org.jpype will give an incomplete dependency list and the resulting jre will not work.

I have searched the documentation and tried adding a module-info.class and a MANIFEST entry. Unfortunately there is no clear way to expose a hidden dependency in a jar file. Nor can I find a method of exposing the requirement without manually importing a private internal class. There has to be a way to handle this properly, but so far it eludes me. Until then jdk.zipfs will have to be added manually.

Thrameos avatar Dec 14 '20 20:12 Thrameos

Seems like a solution would simply be to add a private class called JPypeDeps somewhere in jpype module. It doesn't contain anything but a reference to the internal class name jdk.nio.zipfs.ZipFileSystemProvider. As it is a private class which never gets called, it can't cause a failure even if the class does not exist as it is never created. But when jdeps scans it sees the requirement and adds it to the jlink as it should.

See: http://openjdk.java.net/jeps/261

Build pattern: javac test.java --add-modules jdk.zipfs --add-exports jdk.zipfs/jdk.nio.zipfs=ALL-UNNAMED

We would likely just want to include this class manually in the jar as it may not build properly on all systems. I would place the build pattern under projects in case someone ever needed to recreate it.

@marscher Does this sound like an okay solution?

Thrameos avatar Dec 14 '20 21:12 Thrameos

Why would building this class be a problem for certain systems? I actually do not like the inclusion of binary code in the repository, even if there is an instruction how to recreate it for various reasons.

As far as I understand this is mandatory to get jdeps to work. Aren't there any alternatives, which inspect reflections as well? I guess you already researched that option. If we have to rely on jdeps, if this mentioned private class is even missing (e.g. could not be build for a given system), it shouldn't fail according to your upper statement. So I fail to understand, why we want to include binary code in the first place.

marscher avatar Dec 21 '20 01:12 marscher

The problem here is the we are still supporting Java 8. We can't build the class file on anything before Java 9 because it requires tools only included in Java 9 and the classes that were included are not in Java 8. This is a totally dummy class as it is private and just includes an object request to a private class. You can't even actually load it as the module system will prevent access unless you ask modules to explicitly allow it with command line options. Also it doesn't apply to android and will likely break dex builder as it requires internal classes that don't exist on android.

So what the heck does it do if the class can't even be loaded? JDeps scans class files to find dependencies. It only looks at the class files and not the manifest. So you can't just add a line to the META-INF/MANIFEST to add stuff that is missing because of reflection. If we don't have this then jdeps will fail to find all the stuff that we do with reflection (which we do to avoid having to use Java 9 to build MR Jars) the jlink step builds a JRE which can't actually be used. Instead it fails with an obscure error about JPypePackage initialization failure which looks like an internal failure. This resulted in one of my coworkers spending two days trying to figure out why his build pattern for an app wouldn't work.

As far as alternatives,

  1. Documentation that someone has to add jdk.zipfs to jdeps/jlink manually. Very likely to get missed and still cause issues. At best someone will try using the standard procedure, fail, then get lucky and find it in the documentation. More likely it will just generate another bug report.
  2. Find some version of zipfs with an apache license. Possible, but doesn't solve the next one that comes up.
  3. Drop support for Java 8. I seriously considered this option again. Unfortunately Oracle has extended the life of Java 8 out as far as 2022 with extended support to 2030. So if we go by their schedule then I will be retired before we can dump it.
  4. Next use their dependency adding method .... well I looked for 4 hours at the source code and documentation and I didn't see anything that looked like a hook to add reflection based dependencies. They even go so far as to document that jdeps can't find reflection based dependencies, but they gave no alternative.

I have no clue why the filesystem API doesn't have a formal method to specify that it requires some dependency. It seems like a gross oversight. But after a lot of searching I haven't found anything that looks like a proper method to accomplish this.

So that left the option of building a class file that fools java into including the dependency. Not my favorite option, but that is all I can come up with.

Thrameos avatar Dec 21 '20 01:12 Thrameos