Coming Up for Air

Compiling for Java 8 and Java 9

In a project I’m working on for my book, I need to share classes between two applications. One, an Android project, requires Java 8. The other, a desktop JavaFX application, needs to run under Java 9, complete with module support. The problem with this is that the Maven tooling isn’t quite ready for Java 9, so it’s not as simple as I would like. I have, however, found a solution that seems to work.

In this setup, I have three projects: the shared module, the Android project, and the JavaFX project. The shared module looks roughly like this:

src
    main
        java
            module-info.java
            com
                steeplesoft
                    foo
                        model
                            Message.java

Our module is defined this way:

module foo.shared {
    exports com.steeplesoft.foo.model;
}

We want to compile everything so that the bytecode is usable via the Java 8 JVM, but module-info.java won’t compile for Java 8. Here, we apply some Maven magic:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.6.1</version>
            <executions>
                <execution>
                    <id>default-compile</id>
                    <configuration>
                        <excludes>
                            <exclude>**/module-info.java</exclude>
                        </excludes>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </execution>
                <execution>
                    <id>module-infos</id>
                    <phase>compile</phase>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                    <configuration>
                        <excludes>
                            <exclude>**/*</exclude>
                        </excludes>
                        <includes>
                            <include>**/module-info.java</include>
                        </includes>
                        <source>1.9</source>
                        <target>1.9</target>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

We accomplish our goal by configuring the Maven compiler plugin with two different executions. In the first, we compile everything using a source and target of 1.8. For this pass, though, we exclude module-info.java. For the next pass, we compile with a source and target of 9. We also exclude everything so that our Java 8-compatible .class files aren’t overwritten, but we also explicitly include our module-info.java source file. Once this build runs, we’ll have mostly Java 8 .class files in our output directory, with a single Java 9-specific file, module-info.class. The normal Maven process than jars everything up and installs it in our local repository.

Over in our Android application, we can then declare the dependency on this jar:

    repositories {
        jcenter()
        mavenCentral()
        mavenLocal()
    }
// ...

dependencies {
    compile 'com.steeplesoft:foo-shared:1.0-SNAPSHOT'
}

We can then import and use our model class, Message, and build and run our application.

On the Java 9 side, we declare a dependency on our module in the POM, and can configure our application’s Java module like this:

module foo.desktop {
    requires foo.shared;

    requires javafx.graphics;
    requires javafx.controls;
    requires javafx.fxml;
}

And we can successfully build and run our JavaFX application. Both Android and Java 9 are, as they say, fat, dumb and happy, and we have our shared code in a single project that can be used by both. It’s a bit more XML than we’d probably like, but, as Maven users, we’re probably used to that by now. :)

tags: Java 9

Quotes

Sample quote

Quote source