FXMisc / RichTextFX

Rich-text area for JavaFX
BSD 2-Clause "Simplified" License
1.22k stars 236 forks source link

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

Closed JordanMartinez closed 5 years ago

JordanMartinez commented 7 years ago

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 commented 7 years ago

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 commented 6 years ago

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 commented 6 years ago

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:

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:

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