Introduction

Gradle includes a war plugin for building Java web applications, and the community supplies an excellent plugin called gretty for testing and deploying web applications on Jetty or Tomcat. This guide demonstrates how to build a simple web app and deploy it on Jetty. You’ll also learn how to write a unit test for a servlet using the Mockito framework and how to write a functional test for the web app using gretty and Selenium.

What you’ll build

You will create a web application in the default Gradle structure, add dependencies for the Servlet API, add the gretty plugin, then build and test the application.

What you’ll need

  • About NN

  • A text editor or IDE

  • A Java distribution, version 7 or higher

  • A Gradle distribution, version 4.0 or better

Create the structure of a web application

Gradle includes a war plugin that is documented in the Web Application Quickstart and the WAR plugin chapter of the user manual. The war plugin extends the Java plugin to add support for web applications. By default, it uses a folder called src/main/webapp for web-related resources.

The "Web Application Quickstart" section in the User Manual still refers to the jetty plugin, which is deprecated in favor of the gretty plugin used here. The parts specific to the war plugin are fine, however, and the section will be updated soon.

Therefore, create the following file structure for a project called webdemo:

Sample project layout
webdemo/
    src/
        main/
            java/
            webapp/
        test
            java/

Any servlets or other Java classes will go in src/main/java, tests will go in src/test/java, and other web artifacts will go in src/main/webapp.

Add a Gradle build file

Add a file called build.gradle to the root of the project, with the following contents:

build.gradle
plugins {
    id 'java'
    id 'war'  (1)
}

repositories {
    jcenter()
}

dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0' (2)
    testCompile 'junit:junit:4.12'
}
1 Using the war plugin
2 Current release version of the servlet API

The war plugin adds the configurations providedCompile and providedRuntime, analogous to compile and runtime in regular Java applications, to represent dependencies that are needed locally but should not be added to the generated webdemo.war file.

The plugins syntax is used to apply the java and war plugins. No version is needed for either, since they are included with the Gradle distribution.

It is a good practice to generate a Gradle wrapper for the project by executing the wrapper task:

$ gradle wrapper --gradle-version=4.0
:wrapper

This will produce gradlew and gradlew.bat scripts and the gradle folder with the wrapper jar inside as described in the wrapper section of the User Manual.

If you are using Gradle 4.0 or later you may see less output from the console that you might see in this guide. In this guide, output is shown using the --console-plain flag on the command-line. This is done to show the tasks that Gradle is executing.

Add a servlet and metadata to the project

There are two options for defining web application metadata. Prior to version 3.0 of the servlet specification, metadata resided in a deployment descriptor called web.xml in the WEB-INF folder of the project. Since 3.0, the metadata can be defined using annotations.

Create a package folder org/gradle/demo below the src/main/java folder. Add a servlet file HelloServlet.java, with the following contents:

src/main/java/com/gradle/demo/HelloServlet.java
package org.gradle.demo;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "HelloServlet", urlPatterns = {"hello"}, loadOnStartup = 1) (1)
public class HelloServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        response.getWriter().print("Hello, World!");  (2)
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        String name = request.getParameter("name");
        if (name == null) name = "World";
        request.setAttribute("user", name);
        request.getRequestDispatcher("response.jsp").forward(request, response); (3)
    }
}
1 Annotation-based servlet
2 GET request returns a simple string
3 POST request forwards to a JSP page

The servlet uses the @WebServlet annotation for configuration. The doGet method responds to HTTP GET requests by writing a "Hello, World!" string to the output writer. It reacts to HTTP POST requests by looking for a request parameter called name and adding it to the request as an attribute called user, then forwarding to a response.jsp page.

The war plugin supports the use of the older web.xml deployment descriptor, which by default should reside in the WEB-INF folder under src/main/webapp. Feel free to use that as an alternative to the annotation-based approach.

You now have a simple servlet that responds to HTTP GET and POST requests.

Add JSP pages to the demo application

Add an index page to the root of the application by creating the file index.html in the src/main/webapp folder, with the following contents:

src/main/webapp/index.html
<html>
<head>
  <title>Web Demo</title>
</head>
<body>
<p>Say <a href="hello">Hello</a></p> (1)

<form method="post" action="hello">  (2)
  <h2>Name:</h2>
  <input type="text" id="say-hello-text-input" name="name" />
  <input type="submit" id="say-hello-button" value="Say Hello" />
</form>
</body>
</html>
1 Link submits GET request
2 Form uses POST request

The index.html page uses a link to submit an HTTP GET request to the servlet, and a form to submit an HTTP POST request. The form contains a text field called name, which is accessed by the servlet in its doPost method.

In its doPost method, the servlet forwards control to another JSP page called response.jsp. Therefore define a file of that name inside src/main/webapp with the following contents:

src/main/webapp/response.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Hello Page</title>
    </head>
    <body>
        <h2>Hello, ${user}!</h2>
    </body>
</html>

The response page accessed the user variable from the request and renders it inside an h2 tag.

Add the gretty plugin and run the app

The gretty plugin is an outstanding community-supported plugin that can be found in the Gradle plugin repository at https://plugins.gradle.org/plugin/org.akhikhl.gretty. The plugin makes it easy to run or test webapps on either Jetty or Tomcat.

Add it to our project by adding the following line to the plugins block inside build.gradle.

Updating build.gradle to add gretty
plugins {
    id 'java'
    id 'war'
    id 'org.akhikhl.gretty' version '1.4.2' (1)
}
1 Adding the gretty plugin

The gretty plugin adds a large number of tasks to the application, useful for running or testing in Jetty or Tomcat environments. Now you can build and deploy the app to the default (Jetty) container by using the appRun task.

Executing the appRun task
$ ./gradlew appRun
:prepareInplaceWebAppFolder
:createInplaceWebAppFolder UP-TO-DATE
:compileJava
:processResources UP-TO-DATE
:classes
:prepareInplaceWebAppClasses
:prepareInplaceWebApp
:appRun
12:25:13 INFO  Jetty 9.2.15.v20160210 started and listening on port 8080
12:25:13 INFO  webdemo runs at:
12:25:13 INFO    http://localhost:8080/webdemo
Press any key to stop the server.
> Building 87% > :appRun

BUILD SUCCESSFUL

You can now access the web app at http://localhost:8080/webdemo and either click on the link to execute a GET request or submit the form to execute a POST request.

Although the outputs says Press any key to stop the server, standard input is intercepted by Gradle. To stop the process press `ctrl-C.

Unit test the servlet using Mockito

The open source Mockito framework makes it easy to unit test Java applications. Add the Mockito dependency to the build.gradle file under the testCompile configuration.

Adding the Mockito library to build.gradle
dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    testCompile 'junit:junit:4.12'
    testCompile 'org.mockito:mockito-core:2.7.19'  (1)
}
1 Adding Mockito

To unit test the servlet, create a package folder org.gradle.demo beneath src/test/java. Add a test class file HelloServletTest.java with the following contents:

src/test/java/org/gradle/demo/HelloServletTest.java
package org.gradle.demo;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;

public class HelloServletTest {
    @Mock private HttpServletRequest request;
    @Mock private HttpServletResponse response;
    @Mock private RequestDispatcher requestDispatcher;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void doGet() throws Exception {
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter);

        when(response.getWriter()).thenReturn(printWriter);

        new HelloServlet().doGet(request, response);

        assertEquals("Hello, World!", stringWriter.toString());
    }

    @Test
    public void doPostWithoutName() throws Exception {
        when(request.getRequestDispatcher("response.jsp"))
            .thenReturn(requestDispatcher);

        new HelloServlet().doPost(request, response);

        verify(request).setAttribute("user", "World");
        verify(requestDispatcher).forward(request,response);
    }

    @Test
    public void doPostWithName() throws Exception {
        when(request.getParameter("name")).thenReturn("Dolly");
        when(request.getRequestDispatcher("response.jsp"))
            .thenReturn(requestDispatcher);

        new HelloServlet().doPost(request, response);

        verify(request).setAttribute("user", "Dolly");
        verify(requestDispatcher).forward(request,response);
    }
}

The test creates mock objects for the HttpServletRequest, HttpServletResponse, and RequestDispatcher classes. For the doGet test, a PrintWriter that uses a StringWriter is created, and the mock request object is configured to return it when the getWriter method is invoked. After calling the doGet method, the test checks that the returned string is correct.

For the post requests, the mock request is configured to return a given name if present or null otherwise, and the getRequestDispatcher method returns the associated mock object. Calling the doPost method executes the request. Mockito then verifies that the setAttribute method was invoked on the mock response with the proper arguments and that the forward method was called on the request dispatcher.

You can now test the servlet using Gradle with the test task (or any task, like build, that depends on it).

$ ./gradlew build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:war
:assemble
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

The test output can be accessed from build/reports/tests/test/index.html in the usual manner. You should get a result similar to:

test results

Add a functional test

The gretty plugin combines with Gradle to make it easy to add functional tests to web applications. To do so, add the following lines to your build.gradle file:

Gretty additions to build.gradle for functional testing
gretty {
    integrationTestTask = 'test'  (1)
}

// ... rest from before ...

dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    testCompile 'junit:junit:4.12'
    testCompile 'org.mockito:mockito-core:2.7.19'
    testCompile 'io.github.bonigarcia:webdrivermanager:1.6.1' (2)
    testCompile 'org.seleniumhq.selenium:selenium-java:3.3.1' (3)
}
1 Tell gretty to start and stop the server on test
2 Automatically installs browser drivers
3 Uses Selenium for functional tests

The gretty plugin needs to know which task requires a start and stop of the server. Frequently that is assigned to your own task, but to keep things simple just use the existing test task.

Selenium is a popular open-source API for writing functional tests. Version 2.0 is based on the WebDriver API. Recent versions require testers to download and install a version of WebDriver for their browser, which can be tedious and hard to automate. The WebDriverManager project makes it easy to let Gradle handle that process for you.

Add the following functional test to your project, in the src/test/java directory:

src/test/java/org/gradle/demo/HelloServletFunctionalTest.java
package org.gradle.demo;

import io.github.bonigarcia.wdm.ChromeDriverManager;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import static org.junit.Assert.assertEquals;

public class HelloServletFunctionalTest {
    private WebDriver driver;

    @BeforeClass
    public static void setupClass() {
        ChromeDriverManager.getInstance().setup(); (1)
    }

    @Before
    public void setUp() {
        driver = new ChromeDriver();               (2)
    }

    @After
    public void tearDown() {
        if (driver != null)
            driver.quit();                         (3)
    }

    @Test
    public void sayHello() throws Exception {      (4)
        driver.get("http://localhost:8080/webdemo");

        driver.findElement(By.id("say-hello-text-input")).sendKeys("Dolly");
        driver.findElement(By.id("say-hello-button")).click();

        assertEquals("Hello Page", driver.getTitle());
        assertEquals("Hello, Dolly!", driver.findElement(By.tagName("h2")).getText());
    }
}
1 Downloads and installs browser driver, if necessary
2 Start the browser automation
3 Shut down the browser when done
4 Run the functional test using the Selenium API

The WebDriverManager portion of this test checks for the latest version of the binary, and downloads and installs it when it is not present. Then the sayHello test method drives a Chrome browser to the root of our application, fills in the input text field, clicks the button, and verifies the title of the destination page and that the h2 tag contains the expected string.

The WebDriverManager system supports Chrome, Opera, Internet Explorer, Microsoft Edge, PhantomJS, and Firefox. Check the project documentation for more details.

Run the functional test

Run the test using the test task:

$ ./gradlew test
:prepareInplaceWebAppFolder UP-TO-DATE
:createInplaceWebAppFolder UP-TO-DATE
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:prepareInplaceWebAppClasses UP-TO-DATE
:prepareInplaceWebApp UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:appBeforeIntegrationTest
12:57:56 INFO  Jetty 9.2.15.v20160210 started and listening on port 8080
12:57:56 INFO  webdemo runs at:
12:57:56 INFO    http://localhost:8080/webdemo
:test
:appAfterIntegrationTest
Server stopped.

BUILD SUCCESSFUL

The gretty plugin starts up an embedded version of Jetty 9 on the default port, executes the tests, and shuts down the server. If you watch, you’ll see the Selenium system open a new browser, access the site, complete the form, click the button, check the new page, and finally shut down the browser.

Integration tests are often handled by creating a separate source set and dedicated tasks, but that is beyond the scope of this guide. See the Gretty documentation for details.

Summary

In this guide, you learned how to:

  • Use the war plugin in Gradle builds to define a web application

  • Add a servlet and JSP pages to a web app

  • Use the gretty plugin to deploy the application

  • Unit test a servlet using the Mockito framework

  • Functionally test the web app using gretty and Selenium

Next steps

Gretty is a very powerful API. See the Gretty documentation for details. Further details about Selenium can be found on the Selenium website, and more about WebDriverManager system is available on the WebdriverDriverManager GitHub repository.

If you are interested in functional testing, check out the open source Geb library, which provides a powerful Groovy DSL for browser automation that rests on top of Selenium and WebDriver.

Help improve this guide

Have feedback or a question? Found a typo? Like all Gradle guides, help is just a GitHub issue away. Please add an issue or pull request to gradle-guides/building-java-web-applications and we’ll get back to you.