Our SaaS application is built with Java, managed with Gradle and runs on Google App Engine. That makes a surprisingly lean and agile combination. Since we do like to work with the best tools available the folks responsible for the back-end love IntelliJ IDE. So of course we utilize the IntelliJ plugin for Gradle. It’s amazingly simple to configure:
apply plugin: 'idea'
That’s it. When we run
gradle idea
it generates all IntelliJ files for us:
- small-improvements.ipr (project configuration),
- small-improvements.iml (module configuration)
- and small-improvements.iws (workspace configuration).
We now have an IntelliJ project synchronised to our Gradle configuration. Great.
The Problem
Let’s get coding: You open IntelliJ. Eager to kill the most recent bug (it does happen). But wait! Before you can start the app you need to create an artifact from the generated module. After that you need to create a run configuration for ‘Google AppEngine Dev Server’. You then start the app only to realise that you forgot to add the correct JVM flags that enable debug functionality. So you copy the correct flags from the Gradle file.
Now you can get started. You fix the bug and restart the server. But the bug is still there! How can this be? After questioning your own sanity you notice that you forgot to check the option “Build on make”. This is just the tip of the iceberg, don’t get me started on what happens when the version of a dependency is changed …
The Change
We could just accept this annoying process and continue business as usual. But one thing our CEO brought with him from working at Atlassian, besides the sophisticated hiring philosophy, is the company motto: Be the change you seek.
So we investigated and came across the adequately titled article The Hell of IntelliJ and Gradle. This inspired us to extend the available plugin and generate the project files completely! (imagine manic laughter and growling thunders)
After a few hours of tinkering and getting to know Groovy’s DSL for creating and manipulating XML nodes we came up with roughly 120 lines of code to automate the entire IntelliJ setup almost completely. The following code snippet contains the part for the artifact generation.
def IDEA_MODULE_NAME = 'mymodule'
def IDEA_ARTIFACT_NAME = 'myproject'
def artifactManager = xmlFile.asNode().component.find { it.@name == 'ArtifactManager' } as Node
if (artifactManager) {
Node artifact = artifactManager.artifact.find { it.@type == 'exploded-war' }
if (artifact)
artifactManager.remove(artifact)
} else {
artifactManager = xmlFile.asNode().appendNode('component', [name: 'ArtifactManager']);
}
def artifact = new NodeBuilder().artifact(type: 'exploded-war', 'build-on-make': "true", name: "$IDEA_ARTIFACT_NAME") {
'output-path'("\$PROJECT_DIR\$/$IDEA_BUILD_APP_DIR")
root(id: 'root') {
element(id: 'directory', name: 'WEB-INF') {
element(id: 'directory', name: 'classes') {
element(id: 'module-output', name: IDEA_MODULE_NAME)
}
element(id: 'directory', name: 'lib') {
this.project.configurations.runtime.each {
element(id: 'file-copy', path: it)
}
}
}
element(id: 'javaee-facet-resources', facet: "$IDEA_MODULE_NAME/web/Web")
}
}
artifactManager.append artifact
This snippet creates a new node with the name ‘ArtifactManager’ (or resets the existing one) and adds a new exploded WAR artifact for the module – with “Build on make” checked. The complete script also
- enables annotation processing,
- sets up the Git root,
- defines the compiler output directory,
- adds the Web and App Engine facet,
- and configures the default JUnit and App Engine run configuration.
Make sure to have a look at the complete code to see how it is all done.
The Result
We have a tutorial in our company wiki that describes how to setup IntelliJ step by step. After the above changes it shrunk from roughly 1,000 words and 5 screenshots to just 400 words.
Back-end developers are now much more eager to update application dependencies since the team will not hate them anymore for making them go through the manual setup again (upon learning about the automation one developer spontaneously got up and issued an ecstatic scream, others just smiled and nodded in affirmation). It is safe to say it made their day.
Please note: The latest release of IntelliJ IDEA 13 promised improvements to the Gradle integration. But for some reason it didn’t work so well for us. Maybe our Gradle configuration is special. Your mileage may vary.