JavaOne – Building and Testing Java 9 Applications with Gradle

“Building and Testing Java 9 Applications with Gradle”

Speaker: Cédric Champeau
Deck on GitHub

For more blog posts from JavaOne, see the table of contents


General

  • First version of Java that is not backward compatible
  • Did review of Java 9 modules
  • Gradle can run on Java 8 or 9.
  • No built in support for module path
  • Some plugins don’t work – ex: FindBugs. Will be hard to figure out if plugin or Gradle’s fault. Some mocking frameworks will require upgrade as well.
  • Gradle 3.4 introduce a Java library plugin so can separate API and implementations dependencies. Just like modules!

Migrating to Java 9

  • Sample app with 6 modules
  • Uses reflection and service loader code so non trivial
  • implementation (requires) vs api (transitive ok) – paves way for Java 9
  • Replace META-INF/services with “provides” in module-info

Testing

  • want to compile main and tests separately. is that one or two modules? if two modules, prevent from seeing each other.
  • hack the java compiler task to give it the classpath as module path
  • use a patch module so main and test code are one module and the tests can access the package private classes. Also gives JUnit the right to read the classes.
  • so still compile separately but create one module

Automatic modules

  • “How many of you have a jar named util.jar?”
  • Reserve automatic module name now by setting Automatic-Module-Name in the manifest
  • They when upgrade to Java 9, use that name

Multi-Release Jars

  • Said multi-release jars are a bad idea because odds are low the dependencies are the same
  • Old way was to use reflection to determine which version available
  • /src/main/java – shared sources
  • /src/main/java9 – java 9 specific sources
  • Tell gradle which versions of Java to compile each with and differences in dependencies
  • ex: java9Implementation for dependencies – points it to shared sources

Future

  • Working on experimental jigsaw plugin which takes care of various tweaks demoed today. org.gradle.java.experimental-jigsaw

My take: This was interesting. I was a little distracted because I realized I needed to figure out a few presentation things for a larger room. So I was doing a little setup on my computer for tomorrow. It was hard to multi-task though. Cédric’s presentation was good and kept pulling me in :). Also the story teller app was cute. So I wasn’t satisfied in my level of paying attention or my level of getting ready for tomorrow. Oh well. I proved the downsides of “multi-tasking”.

JavaOne – Project Jigsaw – Integration with Tools

“Project Jigsaw – Integration with Tools”

Speaker: Alexandru Jecan (author of Java 9 Modularity Revealed)

For more blog posts from JavaOne, see the table of contents


What expect build tools and IDEs to do

  • Code completion (IDEs only)
  • Make things easier
  • Work with class path and module path

Maven

  • Only Maven 3+ supports Java 9.
  • No changes to Maven core to run on Java 9
  • presence of module-info.java decides if Maven uses module path
  • Need Maven compiler plugin 3.6.1 to compile Java 9 code
  • Discourages using automatic module name
  • GAV not related to module name. Continue to use GAV.
  • Use module name in module-info.java – double work
  • Maven does not generate “requires” clauses for module-info.java
  • Can add <compilerArgs> options for -add-modules or -add-exports
  • New tag <release> which takes precedence over source/target tags. Recomended compiling twice or backward compatbility. once for java 9 modules and once for java 8. [why? isn’t that what multi release ars are for?]
  • maven-exec-plugin – can configure module path
  • taked abut updating maven toolchain file [not clear how this differs than prior versions of java]
  • maven-jdeps-plugin – can automatically generate module descriptor [but said earlier couldn’t]
  • maven-jmod-plugin – new plugin; not yet released. Creates and lists content of jmod files. Merge native code into jar
  • maven-jlink-plugin – new plugin; not yet released. Creates runtime images
  • maven-depdendency-plugin – said lists GAV [not clear how changed

Gradle
No first class support for Java 9 yet. Coming soon. [but said using; puzled?]

Ant
Ant 1.9.8+ support JDK 9 modules for java/javac/junit. [yet Ant doesn’t support JUnit 5]

Loom

  • New build tool for Java 9
  • Uses YAML config. Uses Maaven repo.
  • Supports JUnit 5,

loom.builders

Moditect
Maven plugin that generates module-info.java based on dependencies. Says helps a little but doesn’t do all the work. [how?]

SonarJava
SonarJava 4.11+ recognizes module keywords

Graphviz
Open source visualization software
Reads jdeps input to generate graphical representation of Jigsaw. As can IDES

IDEs

  • NetBeans, Eclipse and IntelliJ all understand modules clauses [if you count pre-release versions]
  • Also offer autocompletion, syntax higlighting, etc
  • NetBeans 9 – not yet released; must build yourself. Showed editing module path, visual of module dependencies, jshell, jlink
  • Eclipse Oxygen with Java 9 support – came out 9/27/17. Ten second demo
  • IntelliJ 2017.1+ – Mark directory as source root so directory name matches module name. Errors on common module errors, Showed module editing, module dependencies and other features

Open source readiness

How to modularize your app

  • Introduce modue-info.java by creating manually or running jdeps to generate
  • Problems: using JDK internal APIs (ignore warning or fix), using odule not availalbe such as xml bindings (add-modules at compile and runtime), cyclic dependencies (can have at runtime but not compile time), split packages (Oracle plans to make change in later version of Java to deal with this)
  • [Note: He recommended renaming packags as a solution to spit packages. That sounds like a horrible idea unless you can guarantee only you call that code]

    My take before session: You know how they say that first impressions matter? The speaker is wearing a suit. 90% of the people in the room are wearing jeans. Two people in the room are wearing a suit The speaker and someone he knows. Then he showed the table of contents. There are 29 points in 45 minutes in the outline. Preparing to have my head spin!

    My take after session: He is in fact technical. The suit was misleading! The pace was way too fast though; drinking from a fire hose. Not enough time to understand/process many of the points. He talks fast (as do I), but key is to *pause* if you talk fast so people can catch up. Also the side transitions were distracting. A cube transition is cute. But if you are reading when it moves, it is disorienting. And due to the speed, there was a good chance of being reading when transitions started. This was good information, but should have been two sessions so split up and a decent pace. And omiting how to migrate your libraries; that’s a talk on its own There is a Maven BOF tonight; maybe folks can discuss more then!

    One minute after the official end time, he asked if there were questions. My head was spinning with questions. [I didn’t even have time to process which was most important]. Another attendee asked if having JUnit tests in a parallel directory with the same package name is a split package. The speaker said yes and went on to say to wait for Java 10 or rename. I interjected at that point. Unless you are distributing a test jar, I don’t think this is a problem. In fact, most IDEs compile both the /src/main/java and /test/main/java directories to the same folder. I stated this and asked the speaker if he agreed. He said yes.

building smart dashboard with gradle

FRC (FIRST Robotics Challenge) uses Ant to build the robot code. I think this is a wise choice since some teams are “internet challenged.” This hasn’t changed. What has changed since last year is that SmartDashboard uses Gradle to build. Our team is thinking about customizing Smart Dashboard this year which means we need to be able to build it.

Yesterday in the FRC lab, one of the StuyPulse team members asked me about Gradle. (I gave a really good overview of Ant last year so they remembered I knew build tools.) I explained the concepts of Maven/Gradle especially the local repository and dependencies. And I verbally explained how to run it. We didn’t get to do so together as there were other priorities for the day. And a lot of people weren’t at the meeting due to a competing school event. So I’m writing everything down in this post.

Building Smart Dashboard

The discussion was centered around building SmartDashboard. If you aren’t involved with a FIRST Robotics Competition team, this is a Java based UI used when running the robot. Conveniently that project comes with Gradle right in the project. So all you have to is:

  1. cd to directory you cloned SmartDashboard in
  2. chmod 700 gradlew
  3. ./gradlew build

Make sure you have internet before running this the first time. If you want to build directly from Eclipse, see my Eclipse Gradle post

Where is the output when building SmartDashboard

Running gradle creates a build directory. If you are using Eclipse, remember to hit refresh before looking for it. This build directory  contains a number of things:

  • distributions – a zipped up and tarred up version of SmartDashboard which includes the jar file, all need dependencies and the script to kick it off. This is what you need
  • classes – the compiled .class files
  • libs – a jar file with the compiled code in this project
  • reports – how well did you follow the coding standards established for this project
  • scripts – to launch SmartDashboard
  • temp – would you really expect to find something important in a directory named temp?

What does the Gradle build file do?
Gradle is a build tool. The build.gradle file in SmartDashboard contains Groovy code that declares what the build should do. Both Gradle and Maven follow “convention over configuration” which means that a lot is implied! I went over most (but not all) of this out loud in the classroom.

A plugin is like a helper tool that Gradle is going to use in order to build. For example, ‘java’ is going to compile among other things.  Most of these plugins are in common use. I hadn’t heard of the shadow before. A quick look tells me that it builds an uber jar (which is a distribution file that contains all the dependencies). That’s really useful. In fact, I have used the shade plugin which does the same thing in Maven. The WPILibVersioningPlugin is obviously specialized code for FIRST.

plugins {
    id 'java'
    id 'application'
    id 'com.github.johnrengelman.shadow' version '1.2.4'
    id 'maven-publish'
    id 'idea'
    id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '1.6'
    id 'checkstyle'
}

Gradle needs to download files in order to run. This includes the plugins that we just saw along with dependencies – jars that the program needs. These files are hosted in a repository for binary files. A really common one is Maven Central. This is used by both Maven and Gradle builds to obtain binaries.

repositories {
    mavenCentral()
}

Those dependencies I just mentioned? Here they are. In this case, the dependencies are WPI code and common open source tools like JUnit (a testing library). Notice how all of these except the WPI one use the same format – three strings separated by colons? There’s a reason for this. When accessing Maven Central, you specify a GAV. This stands for group id/artifact id/version. The colons separate them. For example, jfree is a group id, jfreechart is an artifact id and 1.0.13 is a version.

Note that none of these four exhibits a “normal” GAV. You are supposed to use a fully qualified name for the group id. Just like we do for Java package names. An artifact id is lower case “words” separated by dashes if there is more than one word. Finally, you have an optional version number.  There can also be some optional information like a classifier that tells you what file extension you need. The WPI example does have a good group id. And the others have good artifact ids and versions. For more on the WPILib group id see this post.

dependencies {
    compile 'edu.wpi.first.wpilib.networktables.java:NetworkTables:+:desktop'
    compile 'junit:junit:4.12'
    compile 'jfree:jcommon:1.0.16'
    compile 'jfree:jfreechart:1.0.13'
}

Next the script specifies that we should create a jar file and an uber jar file named SmartDashboard.

jar {
    baseName = 'SmartDashboard'
}
shadowJar {
    baseName = 'SmartDashboard'
}

Now you see that this really is a Groovy script; it even has an if statement.

// Ensure that the WPILibVersioningPlugin is setup by setting the release type, if releaseType wasn't
// already specified on the command line
if (!hasProperty('releaseType')) {
    WPILibVersion {
        releaseType = 'dev'
    }
}

If the artifact is being published, details are here. WPI is publishing to this FIRST WPI Maven repo. They use the WPI Version Plugin and publish to the local Maven repo – which is the FIRST WPI Maven repo on their build server. (clarified in comment 65)

publishing {
    publications {
        maven(MavenPublication) {
            artifact(shadowJar) {
                classifier null
            }
            groupId 'edu.wpi.first.wpilib'
            artifactId 'SmartDashboard'
            version WPILibVersion.version
        }
    }
}

Getting close to the end. Now the build configuration says to use the coding conventions in the checkstyle.xml file. This is good. It means that people will have to follow coding conventions if they contribute back to SmartDashboard.

checkstyle {
    configFile = new File(rootDir, "checkstyle.xml")
    toolVersion = '6.19'
    if (project.hasProperty("ignoreCheckstyle")) {
        ignoreFailures = true
    }
}

In Java, you can automatically configure a Jar to run a certain class’ main method when you run the jar. This is specified in the manifest. Gradle can generate this manifest for you.

mainClassName = "edu.wpi.first.smartdashboard.SmartDashboard"

SmartDashboard comes with a fakeRobot test project. This is a submodule from Gradle’s point of view so it needs a build config too. Luckily you already know what plugins, dependencies and a main class are so this should be clear.

project(':fakeRobot') {
  apply plugin: 'java'
  apply plugin: 'application'

  dependencies {
      compile 'edu.wpi.first.wpilib.networktables.java:NetworkTables:+:desktop'
  }

  mainClassName = "edu.wpi.livewindowfakerobot.LiveWindowFakeRobot"
}

The end! The very last thing is to declare which version of Gradle this entire script should be run with.

task wrapper(type: Wrapper) {
    gradleVersion = '3.3'
}
  1.  

Output from building in gradle

I’m including the output from a “good” run in case you have problems and want to compare. This is for the first time you run it. After that the download steps won’t be in the output and it will run much faster.

$ ./gradlew build
Downloading https://services.gradle.org/distributions/gradle-3.3-bin.zip
..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Unzipping /Users/jeanne/.gradle/wrapper/dists/gradle-3.3-bin/64bhckfm0iuu9gap9hg3r7ev2/gradle-3.3-bin.zip to /Users/jeanne/.gradle/wrapper/dists/gradle-3.3-bin/64bhckfm0iuu9gap9hg3r7ev2
Set executable permissions for: /Users/jeanne/.gradle/wrapper/dists/gradle-3.3-bin/64bhckfm0iuu9gap9hg3r7ev2/gradle-3.3/bin/gradle
Starting a Gradle Daemon (subsequent builds will be faster)
Download https://plugins.gradle.org/m2/com/github/jengelman/gradle/plugins/shadow/1.2.4/shadow-1.2.4.pom
Download https://plugins.gradle.org/m2/gradle/plugin/edu/wpi/first/wpilib/versioning/wpilib-version-plugin/1.6/wpilib-version-plugin-1.6.pom
Download https://plugins.gradle.org/m2/org/ow2/asm/asm-commons/5.0.3/asm-commons-5.0.3.pom
Download https://plugins.gradle.org/m2/org/apache/ant/ant/1.9.4/ant-1.9.4.pom
Download https://plugins.gradle.org/m2/org/apache/ant/ant-parent/1.9.4/ant-parent-1.9.4.pom
Download https://plugins.gradle.org/m2/org/codehaus/groovy/groovy-backports-compat23/2.4.4/groovy-backports-compat23-2.4.4.pom
Download https://plugins.gradle.org/m2/org/ajoberstar/grgit/1.7.0/grgit-1.7.0.pom
Download https://plugins.gradle.org/m2/org/apache/ant/ant-launcher/1.9.4/ant-launcher-1.9.4.pom
Download https://plugins.gradle.org/m2/org/eclipse/jgit/org.eclipse.jgit/4.3.1.201605051710-r/org.eclipse.jgit-4.3.1.201605051710-r.pom
Download https://plugins.gradle.org/m2/org/eclipse/jgit/org.eclipse.jgit-parent/4.3.1.201605051710-r/org.eclipse.jgit-parent-4.3.1.201605051710-r.pom
Download https://plugins.gradle.org/m2/org/eclipse/jgit/org.eclipse.jgit.ui/4.3.1.201605051710-r/org.eclipse.jgit.ui-4.3.1.201605051710-r.pom
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.jsch/0.0.9/jsch.agentproxy.jsch-0.0.9.pom
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy/0.0.9/jsch.agentproxy-0.0.9.pom
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.pageant/0.0.9/jsch.agentproxy.pageant-0.0.9.pom
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.sshagent/0.0.9/jsch.agentproxy.sshagent-0.0.9.pom
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.usocket-jna/0.0.9/jsch.agentproxy.usocket-jna-0.0.9.pom
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.usocket-nc/0.0.9/jsch.agentproxy.usocket-nc-0.0.9.pom
Download https://plugins.gradle.org/m2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.pom
Download https://plugins.gradle.org/m2/org/slf4j/slf4j-parent/1.7.21/slf4j-parent-1.7.21.pom
Download https://plugins.gradle.org/m2/com/jcraft/jsch/0.1.53/jsch-0.1.53.pom
Download https://plugins.gradle.org/m2/com/googlecode/javaewah/JavaEWAH/0.7.9/JavaEWAH-0.7.9.pom
Download https://plugins.gradle.org/m2/org/sonatype/oss/oss-parent/5/oss-parent-5.pom
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.core/0.0.9/jsch.agentproxy.core-0.0.9.pom
Download https://plugins.gradle.org/m2/com/github/jengelman/gradle/plugins/shadow/1.2.4/shadow-1.2.4.jar
Download https://plugins.gradle.org/m2/gradle/plugin/edu/wpi/first/wpilib/versioning/wpilib-version-plugin/1.6/wpilib-version-plugin-1.6.jar
Download https://plugins.gradle.org/m2/org/ow2/asm/asm-commons/5.0.3/asm-commons-5.0.3.jar
Download https://plugins.gradle.org/m2/org/apache/ant/ant/1.9.4/ant-1.9.4.jar
Download https://plugins.gradle.org/m2/org/codehaus/groovy/groovy-backports-compat23/2.4.4/groovy-backports-compat23-2.4.4.jar
Download https://plugins.gradle.org/m2/org/ajoberstar/grgit/1.7.0/grgit-1.7.0.jar
Download https://plugins.gradle.org/m2/org/apache/ant/ant-launcher/1.9.4/ant-launcher-1.9.4.jar
Download https://plugins.gradle.org/m2/org/eclipse/jgit/org.eclipse.jgit/4.3.1.201605051710-r/org.eclipse.jgit-4.3.1.201605051710-r.jar
Download https://plugins.gradle.org/m2/org/eclipse/jgit/org.eclipse.jgit.ui/4.3.1.201605051710-r/org.eclipse.jgit.ui-4.3.1.201605051710-r.jar
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.jsch/0.0.9/jsch.agentproxy.jsch-0.0.9.jar
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.pageant/0.0.9/jsch.agentproxy.pageant-0.0.9.jar
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.sshagent/0.0.9/jsch.agentproxy.sshagent-0.0.9.jar
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.usocket-jna/0.0.9/jsch.agentproxy.usocket-jna-0.0.9.jar
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.usocket-nc/0.0.9/jsch.agentproxy.usocket-nc-0.0.9.jar
Download https://plugins.gradle.org/m2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar
Download https://plugins.gradle.org/m2/com/jcraft/jsch/0.1.53/jsch-0.1.53.jar
Download https://plugins.gradle.org/m2/com/googlecode/javaewah/JavaEWAH/0.7.9/JavaEWAH-0.7.9.jar
Download https://plugins.gradle.org/m2/com/jcraft/jsch.agentproxy.core/0.0.9/jsch.agentproxy.core-0.0.9.jar
No .git was found in /Users/jeanne/Documents/workspace/SmartDashboard, or any parent directories of that directory.
No version number generated.
:compileJava
Download http://first.wpi.edu/FRC/roborio/maven/development/edu/wpi/first/wpilib/networktables/java/NetworkTables/3.1.5-20170105171843-1-g3e2631f/NetworkTables-3.1.5-20170105171843-1-g3e2631f.pom
Download https://repo1.maven.org/maven2/jfree/jcommon/1.0.16/jcommon-1.0.16.pom
Download https://repo1.maven.org/maven2/jfree/jfreechart/1.0.13/jfreechart-1.0.13.pom
Download http://first.wpi.edu/FRC/roborio/maven/development/edu/wpi/first/wpilib/networktables/java/NetworkTables/3.1.5-20170105171843-1-g3e2631f/NetworkTables-3.1.5-20170105171843-1-g3e2631f-desktop.jar
Download https://repo1.maven.org/maven2/jfree/jcommon/1.0.16/jcommon-1.0.16.jar
Download https://repo1.maven.org/maven2/jfree/jfreechart/1.0.13/jfreechart-1.0.13.jar
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
:processResources UP-TO-DATE
:classes
:jar
:startScripts
:distTar
:distZip
:assemble
:checkstyleMain
Download https://repo1.maven.org/maven2/com/puppycrawl/tools/checkstyle/6.19/checkstyle-6.19.pom
Download https://repo1.maven.org/maven2/org/antlr/antlr4-runtime/4.5.3/antlr4-runtime-4.5.3.pom
Download https://repo1.maven.org/maven2/org/antlr/antlr4-master/4.5.3/antlr4-master-4.5.3.pom
Download https://repo1.maven.org/maven2/commons-cli/commons-cli/1.3.1/commons-cli-1.3.1.pom
Download https://repo1.maven.org/maven2/com/puppycrawl/tools/checkstyle/6.19/checkstyle-6.19.jar
Download https://repo1.maven.org/maven2/org/antlr/antlr4-runtime/4.5.3/antlr4-runtime-4.5.3.jar
Download https://repo1.maven.org/maven2/commons-cli/commons-cli/1.3.1/commons-cli-1.3.1.jar
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:checkstyleTest UP-TO-DATE
:test UP-TO-DATE
:check
:build
:fakeRobot:compileJava
:fakeRobot:processResources UP-TO-DATE
:fakeRobot:classes
:fakeRobot:jar
:fakeRobot:startScripts
:fakeRobot:distTar
:fakeRobot:distZip
:fakeRobot:assemble
:fakeRobot:compileTestJava UP-TO-DATE
:fakeRobot:processTestResources UP-TO-DATE
:fakeRobot:testClasses UP-TO-DATE
:fakeRobot:test UP-TO-DATE
:fakeRobot:check UP-TO-DATE
:fakeRobot:build

BUILD SUCCESSFUL

Total time: 1 mins 9.099 secs