I am a experienced maven user. Sometime I love it, sometimes I hate it. I like it a lot better than ant, but in some situation you would like maven to be easier and more descriptive. The past year there was a lot of fuzz about Gradle. This would give you the best of ant as well as the best of maven, and even more. My first encounter was not very positive, but I kept it in my mind to try it later with a more serious project. This blogpost is about that second more serious try. It will not teach the gradle experts anything, but it will be an easy introduction in to what gradle has to bring for people that are just interested in gradle.

Project structure

The structure of the project is fairly straightforward, but big enough to demonstrate some of the features that gradle has to offer. It consists of two modules:

  • App
  • Web-ui

We are creating an Axonframework sample, the app contains the command handling and for now the query part as well. The web-ui contains the spring mvc app with the controllers and the authentication using the spring-security framework. For now we make use of an in memory database and we want to be able to start the application without a lot of hassle. Using jetty in this case.

The modules have some shared dependencies for testing and logging. We also want to centralize the definition of some dependency versions.

Multi module gradle build

Gradle was build with multi module support from the start. So what do you need to create a multi-module build. We need a settings.gradle that defines the available modules. Next to that we need the main build.gradle as well as the modules build.gradle. Oke you can do without the per module configuration, but in my case that was not the intention. Let us focus first on the main build.gradle.

Main build.gradle

The main build.gradle is used to define some of the versions of the dependencies. The following code block shows the complete script.

axonVersion = "0.6"
springVersion = "3.0.4.RELEASE"
springSecurityVersion = "3.0.3.RELEASE"
slf4jVersion = "1.5.8"
sourceCompatibility = 1.6

subprojects {
    apply plugin: 'java'

    configurations {
        all*.exclude group: "commons-logging", module: "commons-logging"
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        compile "org.slf4j:jcl-over-slf4j:$slf4jVersion",
                "org.slf4j:jul-to-slf4j:$slf4jVersion"
        runtime "org.slf4j:slf4j-log4j12:$slf4jVersion"

        compile("log4j:log4j:1.2.15") {
            exclude group: "com.sun.jdmk", module: "jmxtools"
            exclude group: "com.sun.jmx", module: "jmxri"
            exclude group: "javax.mail", module: "mail"
            exclude group: "javax.jms", module: "jms"
        }

        testCompile 'junit:junit:4.7'
    }

    group = 'org.axonframework.samples.trader'
    version = '1.0-SNAPSHOT'
    sourceCompatibility = 1.6
}

As you can see, the script starts with the definition of the versions of dependencies. Than the script contains information for all the subprojects. You see we define the java plugin for all subprojects. Next to that the repositories and the dependencies are defined. Have a look at the mechanism to exclude transitive dependencies. For log4j we explicitly exclude some transitive dependencies. We also use a mechanism to exclude commons-logging from the complete build. Using the configurations hook we exclude it for all phases.

Finally we also define the group, version and source compatibility in the build configuration.

That is it, the following code block shows the required settings.gradle and after that we move on with the build.gradle of the app and web-ui subprojects.

include "app", "web-ui"

Subprojects build.gradle

The build.gradle for the app project is almost to easy to show. But I promised I would, so here we go:

dependencies {
    compile "org.axonframework:axon-core:$axonVersion"

    compile "org.hibernate:hibernate-entitymanager:3.4.0.GA",
            "c3p0:c3p0:0.9.1",
            "org.hsqldb:hsqldb:1.8.0.10"

    compile "org.springframework:spring-tx:$springVersion",
            "org.springframework:spring-orm:$springVersion",
            "org.springframework:spring-jdbc:$springVersion"

    compile "com.thoughtworks.xstream:xstream:1.3.1"

    testCompile "org.axonframework:axon-test:$axonVersion"
}

The configuration only contains dependencies. I would like to be able to simplify the dependencies on the spring projects. Would be nice if I could group them somehow, like:

compile group:”org.springframework”, version:”$springVersion”, module:[“spring-webmvc”, “spring-aop”]

But the build config looks pretty clean. Now we move on to the creation of the war for the web project.

apply plugin: 'war'

dependencies {
    compile project(':app')

    compile "org.springframework:spring-webmvc:$springVersion",
            "org.springframework:spring-aop:$springVersion"

    compile "org.springframework.security:spring-security-web:$springSecurityVersion",
            "org.springframework.security:spring-security-config:$springSecurityVersion"

    compile "javax.validation:validation-api:1.0.0.GA",
            "org.hibernate:hibernate-validator:4.0.2.GA"

    runtime "javax.servlet:jstl:1.1.2",
            "taglibs:standard:1.1.2",
            "opensymphony:sitemesh:2.4.2"


    providedCompile "javax.servlet:servlet-api:2.5"
}

Again a pretty straightforward config. We use more types of dependencies. We now introduce the providedCompile. Jars that need not be included in the war should have this config for the dependency. Do mark that we have an apply plugin line at the top for creating the war. Finally have a look at the mechanism to show we have a dependency on another module of the project. We use the compile project (‘:app’) to make the war include this created jar as well.

Running with Jetty

You think the jetty plugin for maven is easy, well check this out. We have to change only one word in the web project config. Look for the first line with apply plugin. Change the word war into jetty and you are done. Now you can run jetty with the following command from the root of the project:

gradle :web-ui:jettyRun

The wrapper

People that have gradle installed on their system can easily build the project and run jetty. But some people might not have gradle installed. You can help them to make it very easy. Gradle comes with a wrapper that downloads gradle and makes sure it’s on your path etc. The only thing the user needs to do is the following in the root of the project:

gradlew build

Pay attention to the ‘w’ at the end of the gradle command. Next step is to start jetty. This is done with the same command as before with one change, the w at the end. To help your users with this, you need to do one thing. You have to add the following task to the main build.gradle. Than run gradle wrapper, checkin the generated files into your source control system. The gradlew script can be used by the people to build the project. Good stuff or not?

task wrapper(type: Wrapper) {
    gradleVersion = '0.9-rc-1'
    jarPath = 'wrapper'
}

Intellij integration

Oke, this kinda hurts to say, but intellij support is not at a level that you would like to. At least that is what I could find out. You can easily run gradle builds from intellij. That is all nice. But I am really missing the dependency management part. Would be great to have support like for maven to setup your complete project. Wouldn’t it be great to have the import from external model feature for gradle as well? For now let us all vote for this issue, it might help:

http://youtrack.jetbrains.net/issue/IDEA-57755

I hope someone from jetbrains reeds this at thinks I am important enough to start creating this plugin 🙂

Performance

The last remarks I want to make are about the performance of gradle. At the moment I have this build for maven as well as gradle and maven2 is twice as fast as gradle. I know work is being done on this. Gradle 0.9.1 will support a new feature that at least will speed up building the project of you have to do it a lot during the day. It will do something with a running gradle service that you can call each time you start a build. There are some items on the mailing list about this specific topic if you are interested.

http://gradle.markmail.org/thread/xbqna6lowvsmm4ve

Concluding

Well that was part one of the experiment. I have not touched everything by a long run. Still I found out I like the configuration of gradle a lot better than I like maven. It is a lot cleaner and let’s you focus on the things that are important. I prefer groovy for this kind of work over xml. Still gradle has some ground to cover. For starters, tool integration. We must have something that makes working with intellij a lot better. In the future I will have a better look at plugins for release management and other stuff that I think is necessary for a good project build.

My first steps with Gradle: creating a multi module java web project and running it with jetty.
Tagged on: