Coming Up for Air

Gradle + Arquillian + GlassFish Embedded

I’ve recently been migrating all of my personal projects to Gradle. Since I use Arquillian, that means migrating that part of the build as well. However, being still fairly new to Gradle, how to handle that integration wasn’t immediately obvious. Thanks to Benjamin Muschko and Aslak Knutsen, I’ve finally gotten a working setup.

While there is a Gradle plugin, as I understand things, it only supports the container lifecycle. All of the test deployment, etc. is work yet to be done. Fortunately, you don’t need the plugin if you don’t mind a little more work in your build. Here is my current build file, which only supports GlassFish Embedded and JBoss AS 7 Managed (paritally) at the moment:

build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
apply plugin: 'war'
apply plugin: 'idea'

version = '1.0-SNAPSHOT'

ext.libraryVersions = [
    arquillian: '1.1.1.Final',
    arquillian_glassfish: '1.0.0.CR4',
    glassfish: '4.0',
    hamcrest: '1.2',
    javaee: '7.0',
    jbossAS7: '7.1.1.Final',
    jbossJavaeeSpec: '1.0.0.Final',
    junit: '4.11',
    postgresql: '9.2-1003-jdbc4',
    shrinkwrapDesc: '2.0.0-alpha-3'
]

repositories {
    mavenCentral()
    mavenLocal()
    mavenRepo url: 'http://repository.jboss.org/nexus/content/groups/public'
    mavenRepo url: 'http://repository.jboss.org/nexus/content/repositories/deprecated'
}

configurations {
    provided
    integrationTestCompile.extendsFrom testCompile
    integrationTestRuntime.extendsFrom testRuntime

    // [1]
    jbossAS7ManagedTestRuntime { extendsFrom integrationTestRuntime, provided }
    glassfishEmbeddedTestRuntime { extendsFrom integrationTestRuntime }
}

sourceSets {
    main {
        compileClasspath = configurations.compile + configurations.provided
    }
    integrationTest { // [2]
        java {
            srcDir 'src/integrationTest/java'
        }
        resources {
            srcDir 'src/integrationTest/resources'
        }
        compileClasspath += main.output + configurations.provided
        runtimeClasspath += main.output + configurations.provided
    }
}

dependencies {
    provided "javax:javaee-api:$libraryVersions.javaee"

    testCompile "junit:junit:$libraryVersions.junit"
    testCompile "org.hamcrest:hamcrest-core:$libraryVersions.hamcrest"

    // [3]
    integrationTestCompile "org.jboss.arquillian.junit:arquillian-junit-container:$libraryVersions.arquillian"
    integrationTestCompile "org.jboss.shrinkwrap.descriptors:shrinkwrap-descriptors-api-javaee:$libraryVersions.shrinkwrapDesc"

    integrationTestRuntime "org.jboss.shrinkwrap.descriptors:shrinkwrap-descriptors-impl-javaee:$libraryVersions.shrinkwrapDesc"

    glassfishEmbeddedTestRuntime "org.jboss.arquillian.container:arquillian-glassfish-embedded-3.1:$libraryVersions.arquillian_glassfish"
    glassfishEmbeddedTestRuntime "org.glassfish.main.extras:glassfish-embedded-all:$libraryVersions.glassfish"
    glassfishEmbeddedTestRuntime "org.postgresql:postgresql:$libraryVersions.postgresql"

    jbossAS7ManagedTestRuntime "org.jboss.as:jboss-as-arquillian-container-managed:$libraryVersions.jbossAS7"
    jbossAS7ManagedTestRuntime "org.jboss.spec:jboss-javaee-6.0:$libraryVersions.jbossJavaeeSpec"
}

// [4]
task glassfishEmbeddedTest(type: Test)
task jbossAS7ManagedTest(type: Test)

tasks.withType(Test).matching({ t-> t.name.endsWith('Test') } as Spec).each { t ->
    t.testClassesDir = project.sourceSets.integrationTest.output.classesDir
    t.classpath = project.configurations.getByName(t.name + 'Runtime') +
        project.sourceSets.main.output +
        project.sourceSets.integrationTest.output
}

There’s quite a bit going on here — and I’ll be honest here — I understand about half of it. :) I will do my best, though, to explain what I think is going on.

We start at [1] by declaring a few new configurations, one for a generic integrat test, one for GlassFish Embedded, and the other for JBoss AS 7 Managed. The first new configuration we’ll use for defining dependencies shared across the containers, specifically, the Arquillian dependencies. With the last two, we extend integrationTestRuntime to pick up those dependencies and to which we’ll add container-specific dependencies later.

I wanted to keep my Arquillian-based integration tests separate from any conventional unit tests I may write, so I put them all in a new directory, src/integrationTest. At [2], I tell Gradle about his new directory with a custom SourceSet, integrationTest. We have to add the output from the main configuration to this one, so that it picks up our application classes.

Next, at [3], we declare the dependencies for the integration tests. The first three entries, as I noted, declare the dependencies each of the containers we’ll use will share. Next, we declare the container-specific dependencies. The project from which I pulled this build uses a Postgresql database. While I should probably use something in-memory (which I’ll eventually do), at the moment, I’m using a local pgsql install, so I need to declare the dependency on the Postgresql JDBC driver. The JBoss dependencies are fairly straightforward.

Finally, at [4], we declare two new tasks, one for each supported container. Immediately after that, we loop over each task of type Test and configure them. This could be done in the task declaration, of course, but this approach, of which I can’t claim authorship, allows us to code it once for all test tasks.

The rest of the project is a fairly straightforward Arquillian setup. For the curious, I’ll share a couple more files in case it helps anyone out. The first is my arquillian.xml:

src/integrationTest/resources/arquillian.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
    <engine>
         <property name="deploymentExportPath">build/libs</property>
    </engine>
    <container qualifier="glassfish-embedded" default="true">
         <configuration>
             <property name="resourcesXml">
                src/integrationTest/resources/glassfish-resources.xml
            </property>
         </configuration>
    </container>

    <container qualifier="jbossas-managed-7">
        <configuration>
            <property name="jbossHome">jboss7</property>
        </configuration>
    </container>
</arquillian>
src/integrationTest/resources/glassfish-resources.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC
    "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN"
    "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
    <jdbc-connection-pool
            datasource-classname="org.postgresql.xa.PGXADataSource"
            res-type="javax.sql.XADataSource"
            name="FrenchPressPool">
        <property name="user" value="frenchpress"></property>
        <property name="password" value="fp"></property>
        <property name="databaseName" value="frenchpress"></property>
        <property name="serverName" value="localhost"></property>
    </jdbc-connection-pool>
    <jdbc-resource pool-name="FrenchPressPool" jndi-name="jdbc/frenchpress"/>
</resources>

You can find the entire app in my BitBucket repo. If you have any questions, suggestions, improvements, etc. in either this Gradle build or the app itself, I’ll happily take pull requests. :)

Quotes

Sample quote

Quote source