Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Continue Java 9 migration to full support via multiple separate Jars #647

Closed
JordanMartinez opened this issue Nov 17, 2017 · 3 comments
Closed

Comments

@JordanMartinez
Copy link
Contributor

Coming from #270, below is the table of different approaches we identified to initially support Java 9.

Reflection Freeze 8, Continue 9 "Boilerplate" directories/modules
Continuous Java 8 support Frozen Java 8 support (or developer forks) Continuous Java 8 support
Java 9 support Java 9 support Java 9 support
--- module-info.java included module-info.java included
may require executable jar flags --- ---
--- --- requires "boilerplate" directories / modules
--- --- requires modifying build.gradle's compileJava step to account for which JDK is compiling it
--- --- requires java8/java9 to be included in version string OR requires a multi-release jar
--- --- requires modifying how releases are built and released via Gradle

This issue focuses on how we should continue our migration to full Java 9 support. Currently, we are using the reflection approach to make a jar that works on both Java 8 and 9. However, this does not fully utilize the advantages of modularization.

The talk in the aforementioned issue was to migrate to a multi-release jar (MRJars). In melix/mrjar-gradle#1, it is noted that MRJars are a bad solution to a real problem, but I still have yet to hear an explanation why. Also, Gradle does not yet have a fully supported plugin to handle these things correctly, although the experimental Chainsaw plugin does exist.

@JordanMartinez
Copy link
Contributor Author

The other concerns in this issue are RichTextFX's dependencies: Flowless, ReactFX, UndoFX, and WellBehavedFX. I have access to Flowless, so I could make a Java 9 release that includes modules. However, ReactFX, UndooFX, and WellBehavedFX are all outside of my control. Tomas admitted to no longer using them in an email to me, and a PR I submitted a long time ago that documents more of ReactFX has hung there without any interaction from Tomas for quite a while despite my reminders (maybe Tomas would describe this as "nagging."). In a worst-case scenario, I suppose I could fork those repos, but I don't have the privileges to add them to the FXMisc organization.

UndoFX and WellBehavedFX could probably be quickly turned into modules. However, ReactFX is a different matter. The question is whether the project itself should be reformulated to implement Subscriber and Publisher APIs in Java 9.

@JordanMartinez
Copy link
Contributor Author

The issue mentioned above (melix/mrjar-gradle#1) was answered by pointing one to the blog post on it. I think it makes sense and the multi-release jar should not be the way forward. However, that means we'll need to add a JDK version prefix/suffix to either the Java 8 version or Java 9 version going forward if we don't want to continue using the reflection approach. I think the reflection approach works, but is still not the best because it doesn't allow us to use Java 9 features in the code base. So, I propose either duplicating the project structure and using the java8 prefix/suffix on the Java 8 version and keeping the original package/module name as the Java 9 version or freezing the java 8 version and simply continuing forward with Java 9+.

Still, the private Text class' API will ruin any long-term migration efforts to Jigsaw.

@JordanMartinez JordanMartinez changed the title Continue Java 9 migration to full support via Multi-Release Jar Continue Java 9 migration to full support via multiple separate Jars Feb 9, 2018
@JordanMartinez
Copy link
Contributor Author

JordanMartinez commented Feb 9, 2018

I wrote this in response to Michael in the TestFX Gitter chat. I think it's worth including it here as well:


Let me summarize the MRJars link you mentioned previously, at least as I understand it. Feel free to correct me where you think I'm misunderstanding them:

There are two issues that arise when trying to target 2+ platforms. In the first (optimized runtime), one platform (e.g. Java 9) may do the same thing faster than the other (e.g. Java 8), so one wants to use that faster implementation when running on the platform that supports it. Second, one platform may have changed the API for doing something (think KeyEvent#impl_getKeyCode [Java 8] vs KeyEvent#getKeyCode [Java 9]), so one needs to insure the correct API is being used so that the code actually compiles and/or runs.

The solution could be to use separate jars for each platform. However, separate jars are impractical to produce and consume largely because the build tools we use failed at handling that. It seems that one needs to use the "very poor classifier feature of Maven" to do something along these lines. However, the classifiers themselves cannot be used to specify the dependency tree of the project itself. If one created two separate Jars using classifiers (e.g. testfx-java8 and testfx-java9), Maven would think they have the same dependency trees. Thus, a Java 9 Jar could be built incorrectly with a dependency on a Java 8 Jar and then call an expected Java 9 API that does not exist in the Java 8 Jar, or vice versa.

This issue did not seem prioritized until the recent Java 8 / 9 split because of modularization. Thus, MRJars (one JAR that can be used by both Java 8 and Java 9 runtimes to achieve the same result/feature) are appealing because they seem to solve the above problem in a quick and convenient way.

However, this solution comes with its own downsides:

  • It can slow down a large and complex build. Having to build a Jar before running some other task in an enterprise build prevents parallelization in the build and thus becomes a bottleneck that slows down the build (e.g. for testing purposes, etc.). I think this isn't a big issue for TestFX
  • Your Java-9-specific class does not necessarily depend on the same runtime dependencies of your Java-8-specific class. As a result, "it can (and will) pollute the classpath, possibly creating conflicts for consumers." This issue seems to parallel the classifier issue raised above. This could be an issue for TestFX...?
  • "Maven Central will soon be bloated with libraries that do not declare their dependencies properly." +1 for dependency hell...

The MRJar idea only exists because of flaws in Maven and therefore Gradle because it uses Maven. If Gradle could change how it handled this situation, then Gradle could make the separate Jars approach convenient and more popular in usage. The blog then explains that the solution is through "variant-aware dependency management." This idea would allow one to specify many things to insure that an artifact was built correctly (the Java version to use when running Gradle, the Java version to use when compiling the source files, and other additional info needed to insure it is built and consumed correctly). They declare that Maven can never support such a complex feature but that Gradle can. They have enabled such a thing for Android development (perhaps as a testing grounds...?), but have not yet finished developing that up for Java yet.

Thus, our options for solving this problem are:

  • use one Jar that can target both platforms via reflection (currently being done in RichTextFX)
  • use multiple separate Jars (general direction I'll be going in RichTextFX when Gradle better supports this)
  • use multi-release Jars.

The rest of the article explains how to create multi-release jars.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants