Getting started with JAX-RS & Gradle

Managed to waste an hour of my life today, trying to set up a JAX-RS + Gradle project. Gradle wasn’t really the problem, nor was JAX-RS, but putting it all together in a way that works took longer than expected.

So without further ado, I present to you a simple quickstart project you can use to kickstart JAX-RS + Gradle projects.

The Gradle build file is pretty minimalistic. There’s no need for web.xml. And I’ve included an example resource.

build.gradle:

description = 'JAX-RS Quickstart'
group = 'org.lick.me.jaxrs'
version = '1.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'eclipse'
sourceCompatibility = 1.8
 
repositories {
        mavenCentral()
}
 
 
buildscript {
        repositories {
                jcenter()
        }
        dependencies {
                // Embedded Jetty 9 Gradle Plugin
                classpath (group: 'com.sahlbach.gradle', name: 'gradle-jetty-eclipse-plugin', version: '1.9.+')
        }
}
apply plugin: 'jettyEclipse'
apply plugin: 'war'
 
dependencies {
        // JAX-RS 2.0(.1)
        compile 'javax.ws.rs:javax.ws.rs-api:2.0.1'
 
        // Jersey 2.15
        compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.15'
 
        // Servlet 3
        compile 'javax.servlet:javax.servlet-api:3.1.0'
}

Extra quick quickstart:

git clone git@github.com:Nimlhug/jax-rs-quickstart.git
cd jax-rs-quickstart
gradle jettyEclipseRun

Gradle gotcha: duplicate project names

Assume the following project structure:

Product
├──build.gradle
├──settings.gradle
├──FeatureA
│   ├── Foo
│   │   ├── build.gradle
│   ├── Bar
│   │   ├── build.gradle
├──FeatureB
│   ├── Foo
│   │   ├── build.gradle
│   ├── Quf
│   │   ├── build.gradle

I don’t think this is a terribly unreasonable structure: we have a product with a bunch of features, and each feature is split into a couple of modules. This works just fine in gradle. As long as FeatureB/Foo doesn’t have a dependency on FeatureA/Foo. Gradle doesn’t seem to be terribly fond of multi-module projects. At least not when you have a deeper, nested structure.

As soon as FeatureB/Foo has a dependency on FeatureA/Foo, gradle goes belly up. It won’t tell you, though. It’ll just quietly carry on. It won’t resolve the dependency, and you’ll end up with compile errors.

FeatureB/Foo/build.gradle:

dependencies {
	compile project(':FeatureA:Foo')
}

The reason is actually quite simple. Gradle uses the directory name as the project name. Instead of looking at the whole path in the tree, which would be a lot more sensible, it just looks at the last leaf in the tree. So we have two projects named Foo, and gradle chokes. Not giving an error/warning strikes me as a bug, though, but still. Not terribly unreasonable.

Thankfully, there’s a fix. It involves renaming your projects via settings.gradle in the root project:

settings.gradle:

// List our projects/subprojects
include ':FeatureA:Foo'
include ':FeatureA:Bar'
include ':FeatureB:Foo'
include ':FeatureB:Quf'
 
// Rename each child project to its path: Foo becomes FeatureA-Foo or FeatureB-Foo, depending on the Foo.
rootProject.children.each { p ->
        p.children.each { module ->
                module.name = p.name + '-' + module.name
        }
}

Now you can add a project dependency and things will Just Work™:

dependencies {
	compile project(':FeatureA:FeatureA-Foo')
}

All fixed. Much better.