Building a web application with Gradle

duration 15 minutes
Git clone to get going right away:
git clone https://github.com/OpenLiberty/guide-gradle-intro.git
Copy Github clone command

Learn how to build and test a simple web application using Gradle and Open Liberty.

What you’ll learn

You will learn how to build and test a simple web servlet application using the Gradle war plug-in and the Liberty Gradle plug-in. The war plug-in compiles and builds the application code. The liberty Gradle plug-in installs the Open Liberty runtime, creates a server, and installs the application to run and test. The application displays a simple web page with a link. When you click that link, the application calls the servlet to return a simple response of Hello! Is Gradle working for you?.

One benefit of using a build tool like Gradle is that after you define the details of the project and any dependencies it has, Gradle automatically downloads and installs the dependencies.

Another benefit of using Gradle is that it can run repeatable, automated tests on the application. You could, of course, test your application manually by starting a server and pointing a web browser at the application URL. Automated tests are a much better approach because you can easily rerun the same tests each time the application is built. If the tests don’t pass after you’ve made a change to the application, the build fails, and you know that you introduced a regression that requires a fix to your code.

Using this guide, you will create a Gradle build definition file (build.gradle) for the web application project, and use it to build the application. You will then create a simple, automated test, and configure Gradle to run it after building the application.

Getting started

The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:

git clone https://github.com/openliberty/guide-gradle-intro.git
cd guide-gradle-intro

The start directory contains the starting project that you will build upon.

The finish directory contains the finished project that you will build.

Creating the application

The web application that you will build using Gradle and Open Liberty is provided for you in the start directory so that you can focus on learning about Gradle. The application uses the standard Gradle directory structure. Using this directory structure saves you from customizing the build.gradle file later.

All the application source code, including the Open Liberty server configuration (server.xml), is in the start/src directory:

└── src
    └── main
        └── java
        └── liberty
            └── config
        └── webapp
            └── WEB-INF

Installing Gradle

If you don’t have Gradle Version 3 or higher installed, follow the instructions on the Gradle installation page for your operating system. Configure the environment variables so that you can run the gradle command.

Test that Gradle is installed properly by running the following command in a command line:

gradle -v

You should see information about the Gradle installation similar to this example:

------------------------------------------------------------
Gradle 4.1
------------------------------------------------------------

Build time:   2017-08-07 14:38:48 UTC
Revision:     941559e020f6c357ebb08d5c67acdb858a3defc2

Groovy:       2.4.11
Ant:          Apache Ant(TM) Version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_144 (Oracle Corporation 25.144-b01)
OS:           Mac OS X 10.12.6 x86_64

You can also view the default tasks available by running the following command:

gradle tasks

Configure your project

The project configuration is defined in the Gradle settings and build files. You will create these project configurations one section at a time.

Gradle settings are used to instantiate and configure the project. This sample uses the settings.gradle to name the project GradleSample.

Create the Gradle settings file.
settings.gradle

settings.gradle

1rootProject.name = 'GradleSample'

This settings.gradle file isn’t required for a single-module Gradle project. Without this definition, by default, the project name is set as the name of the folder in which it is contained (start for this example).

Let’s go through the build.gradle file so that you understand each part.

Configuration

Purpose

Plug-ins used

The first part of the build file specifies the plug-ins required to build the project and some basic project configuration.

buildscript

Where to find plug-ins for download.

repositories

Where to find dependencies for download.

dependencies

Java dependencies that are required for compiling, testing, and running the application are included here. You can also define your Liberty runtime dependency in this section.

ext

Gradle extra properties extension for project level properties.

liberty

Configuration for the Liberty plug-in.

war

War plug-in configuration.

test

Unit test and integration test configuration.

Create the build file.
build.gradle

build.gradle

 1apply plugin: 'war'
 2apply plugin: 'liberty'
 3
 4group = 'io.openliberty.guides'
 5version = '1.0-SNAPSHOT'
 6description = "Gradle guide"
 7
 8sourceCompatibility = 1.8
 9targetCompatibility = 1.8
10tasks.withType(JavaCompile) {
11    options.encoding = 'UTF-8'
12}
13
14buildscript {
15    repositories {
16        mavenCentral()
17    }
18    dependencies {
19        classpath 'net.wasdev.wlp.gradle.plugins:liberty-gradle-plugin:2.6.3'
20    }
21}
22
23repositories {
24    mavenCentral()
25}
26
27dependencies {
28    providedCompile group:'javax.servlet', name:'javax.servlet-api', version:'3.1.0'
29    testCompile group:'commons-httpclient', name:'commons-httpclient', version:'3.1'
30    testCompile group:'junit', name:'junit', version:'4.12'
31    libertyRuntime group:'io.openliberty', name:'openliberty-runtime', version:'[17.0.0.4,)'
32}
33
34ext {
35    appName = project.name
36    testServerHttpPort = 9080
37    testServerHttpsPort = 9443
38    warContext = appName
39}
40
41liberty {
42    server {
43        name = "${appName}Server"
44        configFile = file("src/main/liberty/config/server.xml")
45        bootstrapProperties = ['default.http.port': testServerHttpPort,
46                               'default.https.port': testServerHttpsPort,
47                               'app.context.root': warContext]
48        packageLiberty {
49            archive = "$buildDir/${appName}.zip"
50            include = "usr"
51        }
52    }
53}
54
55war {
56    archiveName = "${baseName}.${extension}"
57}
58
59test {
60    reports.html.destination = file("$buildDir/reports/unit")
61    reports.junitXml.destination = file("$buildDir/test-results/unit")
62    exclude '**/it/**'
63}
64
65task integrationTest(type: Test) {
66    group 'Verification'
67    description 'Runs the integration tests.'
68    reports.html.destination = file("$buildDir/reports/it")
69    reports.junitXml.destination = file("$buildDir/test-results/it")
70    include '**/it/**'
71    exclude '**/unit/**'
72
73    systemProperties = ['liberty.test.port': testServerHttpPort, 'war.name': warContext]
74}
75
76task openBrowser {
77    description = 'Open browser to http://localhost:8080/${warContext}'
78    doLast {
79        java.awt.Desktop.desktop.browse
80            "http://localhost:${testServerHttpPort}/${appName}".toURI()
81    }
82}
83
84task openTestReport {
85    description = 'Open browser to the test report'
86    doLast {
87        java.awt.Desktop.desktop.browse file("$buildDir/reports/it/index.html").toURI()
88    }
89}
90
91clean.dependsOn 'libertyStop'
92check.dependsOn 'integrationTest'
93integrationTest.dependsOn 'libertyStart'
94integrationTest.finalizedBy 'libertyStop'
95integrationTest.finalizedBy 'openTestReport'
96libertyPackage.dependsOn 'libertyStop'

The first section of code defines the war and liberty plug-ins that you want to use. The war plug-in contains all the tasks to compile Java files, build the WAR file structure, and assemble the archive. The liberty plug-in contains the tasks used to install the Liberty runtime and create and manage servers. The group, version, and description define this sample project. The compatibility and encoding settings are for Java.

The buildscript section defines plug-in versions to use in the build and where to find them. This guide uses the liberty plug-in, which is available from the Maven Central Repository.

The repositories section defines where to find the dependencies that you are using in the build. For this build, everything you need is in Maven Central.

The dependencies section defines what is needed to compile and test the code. This section also defines how to run the application. The providedCompile dependencies are APIs that are needed to compile the application, but they do not need to be packaged with the application because Open Liberty provides their implementation at run time. For example, the src/main/java/io/openliberty/guides/hello/HelloServlet.java file depends on javax.servlet-api to compile, but since Open Liberty provides that API, it does not need to be packaged with your application. The testCompile dependencies are needed to compile and run tests.

HelloServlet.java

 1package io.openliberty.guides.hello;
 2
 3import java.io.IOException;
 4
 5import javax.servlet.ServletException;
 6import javax.servlet.annotation.WebServlet;
 7import javax.servlet.http.HttpServlet;
 8import javax.servlet.http.HttpServletRequest;
 9import javax.servlet.http.HttpServletResponse;
10
11@WebServlet(urlPatterns="/servlet")
12public class HelloServlet extends HttpServlet {
13    private static final long serialVersionUID = 1L;
14
15    /**
16     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
17     */
18    protected void doGet(HttpServletRequest request, HttpServletResponse response)
19        throws ServletException, IOException {
20        response.getWriter().append("Hello! Is Gradle working for you?\n");
21    }
22
23    /**
24     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
25     */
26    protected void doPost(HttpServletRequest request, HttpServletResponse response)
27        throws ServletException, IOException {
28        doGet(request, response);
29    }
30}

The liberty plug-in provides the libertyRuntime dependency definition that specifies the version of the Liberty runtime you want to use. This project uses Open Liberty 17.0.0.4 or later.

The Gradle extra properties extension allows you to add properties to a Gradle project. If you use a value more than once in your build file, you can simplify updates by defining it as a variable here and referring to the variable later in the build file. This project defines variables for the application name, ports, and the context-root.

The liberty extension configures the Liberty server, which includes the following settings:

Server attribute

Purpose

name

The server name.

configFile

The server configuration file.

bootstrapProperties

Bootstrap properties that are written to the server’s bootstrap.properties file.

packageLiberty

Specifies to only package the usr directory in the build/GradleSample.zip file.

So what’s not configured for the liberty plug-in? By default, when the liberty and the war plug-ins are applied, the default WAR file is installed to the apps folder. Omitting this definition is the same as including the following line:

apps = [war]

You can also install applications to the dropins folder.

Also, by default, a loose application is installed to your Liberty server. Instead of installing a WAR archive, an XML file (GradleSample.war.xml) is installed that describes the location of the application classes, resources, and libraries. This is useful for development because you only need to recompile your code, rather than repackaging and redeploy your entire WAR file to see application updates live on the server. If you want to turn off loose application support, add the following line:

looseApplication = false

The war configuration names the WAR file GradleSample.war without a version in the name.

Testing the web application

One of the benefits of building an application with a build system like Gradle is that it can be configured to run a set of automated tests. The war plug-in extends the Java plug-in, which provides test tasks. You can write tests for the individual units of code outside of a running application server (unit tests), or you can write them to call the application that runs on the server (integration tests). In this example, you will create a simple integration test that checks that the web page opens and that the correct response is returned when the link is clicked.

The Gradle project is configured to start the server with the application installed before running the test. After running the test, the server is stopped.

Create the EndpointIT test class.
src/test/java/io/openliberty/guides/hello/it/EndpointIT.java

EndpointIT.java

 1package io.openliberty.guides.hello.it;
 2
 3import static org.junit.Assert.*;
 4import org.junit.BeforeClass;
 5import org.junit.Test;
 6import org.apache.commons.httpclient.HttpClient;
 7import org.apache.commons.httpclient.HttpStatus;
 8import org.apache.commons.httpclient.methods.GetMethod;
 9
10public class EndpointIT {
11    private static String URL;
12
13    @BeforeClass
14    public static void init() {
15        String port = System.getProperty("liberty.test.port");
16        String war = System.getProperty("war.name");
17        URL = "http://localhost:" + port + "/" + war + "/" + "servlet";
18    }
19
20    @Test
21    public void testServlet() throws Exception {
22        HttpClient httpClient = new HttpClient();
23        GetMethod httpGetMethod = new GetMethod(URL);
24        try {
25            int actualStatusCode = httpClient.executeMethod(httpGetMethod);
26            int expectedStatusCode = HttpStatus.SC_OK;
27            assertEquals("HTTP GET failed", expectedStatusCode, actualStatusCode);
28            String response = httpGetMethod.getResponseBodyAsString(1000);
29            assertTrue("Unexpected response body",
30                response.contains("Hello! Is Gradle working for you?"));
31        } finally {
32            httpGetMethod.releaseConnection();
33        }
34    }
35}

The test class name ends in it to indicate that it contains an integration test. The integration tests are put in the it folder by convention.

build.gradle

 1apply plugin: 'war'
 2apply plugin: 'liberty'
 3
 4group = 'io.openliberty.guides'
 5version = '1.0-SNAPSHOT'
 6description = "Gradle guide"
 7
 8sourceCompatibility = 1.8
 9targetCompatibility = 1.8
10tasks.withType(JavaCompile) {
11    options.encoding = 'UTF-8'
12}
13
14buildscript {
15    repositories {
16        mavenCentral()
17    }
18    dependencies {
19        classpath 'net.wasdev.wlp.gradle.plugins:liberty-gradle-plugin:2.6.3'
20    }
21}
22
23repositories {
24    mavenCentral()
25}
26
27dependencies {
28    providedCompile group:'javax.servlet', name:'javax.servlet-api', version:'3.1.0'
29    testCompile group:'commons-httpclient', name:'commons-httpclient', version:'3.1'
30    testCompile group:'junit', name:'junit', version:'4.12'
31    libertyRuntime group:'io.openliberty', name:'openliberty-runtime', version:'[17.0.0.4,)'
32}
33
34ext {
35    appName = project.name
36    testServerHttpPort = 9080
37    testServerHttpsPort = 9443
38    warContext = appName
39}
40
41liberty {
42    server {
43        name = "${appName}Server"
44        configFile = file("src/main/liberty/config/server.xml")
45        bootstrapProperties = ['default.http.port': testServerHttpPort,
46                               'default.https.port': testServerHttpsPort,
47                               'app.context.root': warContext]
48        packageLiberty {
49            archive = "$buildDir/${appName}.zip"
50            include = "usr"
51        }
52    }
53}
54
55war {
56    archiveName = "${baseName}.${extension}"
57}
58
59test {
60    reports.html.destination = file("$buildDir/reports/unit")
61    reports.junitXml.destination = file("$buildDir/test-results/unit")
62    exclude '**/it/**'
63}
64
65task integrationTest(type: Test) {
66    group 'Verification'
67    description 'Runs the integration tests.'
68    reports.html.destination = file("$buildDir/reports/it")
69    reports.junitXml.destination = file("$buildDir/test-results/it")
70    include '**/it/**'
71    exclude '**/unit/**'
72
73    systemProperties = ['liberty.test.port': testServerHttpPort, 'war.name': warContext]
74}
75
76task openBrowser {
77    description = 'Open browser to http://localhost:8080/${warContext}'
78    doLast {
79        java.awt.Desktop.desktop.browse
80            "http://localhost:${testServerHttpPort}/${appName}".toURI()
81    }
82}
83
84task openTestReport {
85    description = 'Open browser to the test report'
86    doLast {
87        java.awt.Desktop.desktop.browse file("$buildDir/reports/it/index.html").toURI()
88    }
89}
90
91clean.dependsOn 'libertyStop'
92check.dependsOn 'integrationTest'
93integrationTest.dependsOn 'libertyStart'
94integrationTest.finalizedBy 'libertyStop'
95integrationTest.finalizedBy 'openTestReport'
96libertyPackage.dependsOn 'libertyStop'

The test section in your build file configures Gradle to run the integration tests in the it folder.

The systemProperties configuration defines some variables needed by the test class. While the port number and context-root information can be hardcoded in the test class, it is better to specify it in a single place like the Gradle build.gradle file, in case they need to change. The systemProperties section passes these details to the Java test program as a series of system properties, resolving the liberty.test.port and war.name variables.

The init() method in the EndpointIT.java test class uses these system variables to build the URL of the application.

In the test class, after defining how to build the application URL, the @Test annotation indicates the start of the test method.

In the try block of the test method, an HTTP GET request to the URL of the application returns a status code. If the response to the request includes the string Hello! Is Gradle working for you?, the test passes. If that string is not in the response, the test fails. The HTTP client then disconnects from the application.

In the import statements of this test class, you’ll notice that the test has some new dependencies. Earlier you added some testCompile dependencies. The Apache commons-httpclient and junit dependencies are needed to compile and run the integration test EndpointIT class.

The scope for each of the dependencies is set to testCompile because the libraries are needed only during the Gradle test phase and do not need to be packaged with the application.

Now, during the build, Gradle installs the application and then runs any integration test classes in the it folder.

The directory structure of the project in the start folder should now look like this example:

└── build.gradle
├── settings.gradle
└── src
    ├── main
    │    ├── java
    │    ├── liberty
    │    │    └── config
    │    └── webapp
    │         └── WEB_INF
    └── test
         └── java

A few more pieces

We show a few more Gradle tricks in this example with the openBrowser and the openTestReport tasks. These tasks display your application and the test report in the default browser.

build.gradle

 1apply plugin: 'war'
 2apply plugin: 'liberty'
 3
 4group = 'io.openliberty.guides'
 5version = '1.0-SNAPSHOT'
 6description = "Gradle guide"
 7
 8sourceCompatibility = 1.8
 9targetCompatibility = 1.8
10tasks.withType(JavaCompile) {
11    options.encoding = 'UTF-8'
12}
13
14buildscript {
15    repositories {
16        mavenCentral()
17    }
18    dependencies {
19        classpath 'net.wasdev.wlp.gradle.plugins:liberty-gradle-plugin:2.6.3'
20    }
21}
22
23repositories {
24    mavenCentral()
25}
26
27dependencies {
28    providedCompile group:'javax.servlet', name:'javax.servlet-api', version:'3.1.0'
29    testCompile group:'commons-httpclient', name:'commons-httpclient', version:'3.1'
30    testCompile group:'junit', name:'junit', version:'4.12'
31    libertyRuntime group:'io.openliberty', name:'openliberty-runtime', version:'[17.0.0.4,)'
32}
33
34ext {
35    appName = project.name
36    testServerHttpPort = 9080
37    testServerHttpsPort = 9443
38    warContext = appName
39}
40
41liberty {
42    server {
43        name = "${appName}Server"
44        configFile = file("src/main/liberty/config/server.xml")
45        bootstrapProperties = ['default.http.port': testServerHttpPort,
46                               'default.https.port': testServerHttpsPort,
47                               'app.context.root': warContext]
48        packageLiberty {
49            archive = "$buildDir/${appName}.zip"
50            include = "usr"
51        }
52    }
53}
54
55war {
56    archiveName = "${baseName}.${extension}"
57}
58
59test {
60    reports.html.destination = file("$buildDir/reports/unit")
61    reports.junitXml.destination = file("$buildDir/test-results/unit")
62    exclude '**/it/**'
63}
64
65task integrationTest(type: Test) {
66    group 'Verification'
67    description 'Runs the integration tests.'
68    reports.html.destination = file("$buildDir/reports/it")
69    reports.junitXml.destination = file("$buildDir/test-results/it")
70    include '**/it/**'
71    exclude '**/unit/**'
72
73    systemProperties = ['liberty.test.port': testServerHttpPort, 'war.name': warContext]
74}
75
76task openBrowser {
77    description = 'Open browser to http://localhost:8080/${warContext}'
78    doLast {
79        java.awt.Desktop.desktop.browse
80            "http://localhost:${testServerHttpPort}/${appName}".toURI()
81    }
82}
83
84task openTestReport {
85    description = 'Open browser to the test report'
86    doLast {
87        java.awt.Desktop.desktop.browse file("$buildDir/reports/it/index.html").toURI()
88    }
89}
90
91clean.dependsOn 'libertyStop'
92check.dependsOn 'integrationTest'
93integrationTest.dependsOn 'libertyStart'
94integrationTest.finalizedBy 'libertyStop'
95integrationTest.finalizedBy 'openTestReport'
96libertyPackage.dependsOn 'libertyStop'

The final Gradle magic to add is the task dependency directives. The dependency directives associate the war and java tasks to the liberty tasks to get proper task ordering.

Ready to run

You can now run the build command to build and test the application.

From the folder containing your build.gradle file, run the command:

build.gradle

 1apply plugin: 'war'
 2apply plugin: 'liberty'
 3
 4group = 'io.openliberty.guides'
 5version = '1.0-SNAPSHOT'
 6description = "Gradle guide"
 7
 8sourceCompatibility = 1.8
 9targetCompatibility = 1.8
10tasks.withType(JavaCompile) {
11    options.encoding = 'UTF-8'
12}
13
14buildscript {
15    repositories {
16        mavenCentral()
17    }
18    dependencies {
19        classpath 'net.wasdev.wlp.gradle.plugins:liberty-gradle-plugin:2.6.3'
20    }
21}
22
23repositories {
24    mavenCentral()
25}
26
27dependencies {
28    providedCompile group:'javax.servlet', name:'javax.servlet-api', version:'3.1.0'
29    testCompile group:'commons-httpclient', name:'commons-httpclient', version:'3.1'
30    testCompile group:'junit', name:'junit', version:'4.12'
31    libertyRuntime group:'io.openliberty', name:'openliberty-runtime', version:'[17.0.0.4,)'
32}
33
34ext {
35    appName = project.name
36    testServerHttpPort = 9080
37    testServerHttpsPort = 9443
38    warContext = appName
39}
40
41liberty {
42    server {
43        name = "${appName}Server"
44        configFile = file("src/main/liberty/config/server.xml")
45        bootstrapProperties = ['default.http.port': testServerHttpPort,
46                               'default.https.port': testServerHttpsPort,
47                               'app.context.root': warContext]
48        packageLiberty {
49            archive = "$buildDir/${appName}.zip"
50            include = "usr"
51        }
52    }
53}
54
55war {
56    archiveName = "${baseName}.${extension}"
57}
58
59test {
60    reports.html.destination = file("$buildDir/reports/unit")
61    reports.junitXml.destination = file("$buildDir/test-results/unit")
62    exclude '**/it/**'
63}
64
65task integrationTest(type: Test) {
66    group 'Verification'
67    description 'Runs the integration tests.'
68    reports.html.destination = file("$buildDir/reports/it")
69    reports.junitXml.destination = file("$buildDir/test-results/it")
70    include '**/it/**'
71    exclude '**/unit/**'
72
73    systemProperties = ['liberty.test.port': testServerHttpPort, 'war.name': warContext]
74}
75
76task openBrowser {
77    description = 'Open browser to http://localhost:8080/${warContext}'
78    doLast {
79        java.awt.Desktop.desktop.browse
80            "http://localhost:${testServerHttpPort}/${appName}".toURI()
81    }
82}
83
84task openTestReport {
85    description = 'Open browser to the test report'
86    doLast {
87        java.awt.Desktop.desktop.browse file("$buildDir/reports/it/index.html").toURI()
88    }
89}
90
91clean.dependsOn 'libertyStop'
92check.dependsOn 'integrationTest'
93integrationTest.dependsOn 'libertyStart'
94integrationTest.finalizedBy 'libertyStop'
95integrationTest.finalizedBy 'openTestReport'
96libertyPackage.dependsOn 'libertyStop'
gradle build -i

The -i option provides additional information on the command line. The build takes a little longer than before the test existed, but expect to see the browser opened with the test summary displayed.

To see whether the test detects a failure, change the response string in the src/main/java/io/openliberty/guides/hello/HelloServlet.java file so that it doesn’t match the string that the test is looking for. Then rerun the Gradle integration tests by running the command:

HelloServlet.java

 1package io.openliberty.guides.hello;
 2
 3import java.io.IOException;
 4
 5import javax.servlet.ServletException;
 6import javax.servlet.annotation.WebServlet;
 7import javax.servlet.http.HttpServlet;
 8import javax.servlet.http.HttpServletRequest;
 9import javax.servlet.http.HttpServletResponse;
10
11@WebServlet(urlPatterns="/servlet")
12public class HelloServlet extends HttpServlet {
13    private static final long serialVersionUID = 1L;
14
15    /**
16     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
17     */
18    protected void doGet(HttpServletRequest request, HttpServletResponse response)
19        throws ServletException, IOException {
20        response.getWriter().append("Hello! Is Gradle working for you?\n");
21    }
22
23    /**
24     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
25     */
26    protected void doPost(HttpServletRequest request, HttpServletResponse response)
27        throws ServletException, IOException {
28        doGet(request, response);
29    }
30}
gradle integrationTest -i

Then, check to see if the test fails.

Great work! You’re done!

You built and tested a web application project with an Open Liberty server using Gradle.

Guide Attribution

Building a web application with Gradle by Open Liberty is licensed under CC BY-ND 4.0

Copied to clipboard
Copy file contents
Git clone this repo to get going right away:
git clone https://github.com/OpenLiberty/guide-gradle-intro.git
Copy github clone command
Copied to clipboard

Nice work! Where to next?

What could make this guide better?

Raise an issue to share feedback

Create a pull request to contribute to this guide

Need help?

Ask a question on Stack Overflow

What did you think of this guide?

Extreme Dislike Dislike Like Extreme Like