Gradle Tutorial : Part 4 : Java Web Applications

Welcome to Part 4 of the Gradle Tutorial. This part takes off from Part 3 , where we covered building multiple interdependent Java projects.

LAST UPDATE : January 22, 2015 :
i) Fixed minor Text
ii) Updated code in Github
iii) Updated web page screenshot
iv) Fixed web/build.gradle : Added stopKey and stopPort to allow “gradle jettyStop” task to function correctly.

In this part of the tutorial, we shall look at building Java Web Applications via Gradle. As is the pattern, we shall have a multi-project scenario where we will have one Java Project that has some utility class and which is built separately. And then we have a Java Web Project that is dependent on this project and has JSP/Servlets and so on.

In the process, we shall look at 2 additional plugins that Gradle provides, which will make our task easier. The plugins are:

  1. War Plugin : This plugin allows us to compile and assemble a WAR (Web Application Archive) file from our Java Web Application.
  2. Jetty Plugin : This plugin allows us to run our web application inside of a Jetty container. Very useful to test out the project quickly.

This part assumes that you have a Gradle installation on your machine and the basic environment is setup. For more information on that, refer to Part 1. Additionally, you know the basics of using the Java plugin in Gradle, which we covered in Part 2 and building multiple and interdependent Java projects that we covered in Part 3.

Our Project Scenario

For this episode, we shall look at 2 projects that are arranged under a common directory. Our root directory is going to be called mywebapp and inside of that we shall have 2 folders that will contain individual projects as shown below:

mywebapp
|- utils
|- web

All the project source code , including the build files is available on Github. Please download it from here and keep it available on your machine, so that you can follow the tutorial and run it along as we go through this episode.

Now, let us talk about the dependencies. These dependencies are something like this:

  1. utils: This project contains some utility code and hence it will not depend on any of the other projects. The Java code in this project depends on an external Java Date Time Library : Joda Time. So we will need to have that dependency defined for this project.
  2. web: This is a Java Web Application project, that just has a simple Servlet and JSP file, along with the web.xml file. This project depends on the utils project, since it uses the utility Java class from there. Along with that dependency, it also has dependency on the Servlet Jar file that is needed to compile Java HTTP Servlet code.

Multiple Gradle files

As we had seen in the last episode, it is better to create multiple build.gradle files, so that we can customize and maintain the build specific requirements for each project in a much more maintainable fashion.

In essence, what we are ending up with is a structure that looks like the following:

/mywebapp
|- /utils
     |- build.gradle
     |- (Java Sources and files)
|- /web
     |- build.gradle
     |- (Java Sources , JSPs and files)
|- settings.gradle
|- build.gradle

settings.gradle

Since, we have two projects (utils and web), our settings.gradle will reference both the project as shown below:

include ":utils",":web"

build.gradle

The build.gradle file that is found at the root folder i.e. \mywebapp is shown below. You will notice that since both of these are Java projects, we are applying the Java plugin and also specifying that Maven Central be used for downloading any dependent libraries:

subprojects {
  
  apply plugin : "java"
 
  repositories {
     mavenCentral()
  }
}

Now that we have got the common configuration out of the way, let us look at the individual build.gradle files that will be present in each of the projects i.e. web and utils.

utils project – build.gradle

The build.gradle file for the utils project is shown below:

apply plugin: "java"
dependencies {
 compile "joda-time:joda-time:2.4"
}

The above is a straight forward build.gradle file. It is applying the Java plugin. You need not mention it since it is specified for all subprojects in the common build.gradle at the root folder, but I am just mentioning it here, so that you have a complete build.gradle file.

The next line defines the dependency on the joda-time library. We have got the group id, artifact id and the version from the Maven Central library over here.

Please note that if you download the source code from Github, the utils project only contains a single Java utility class (DateUtils.java) under the conventional directory structure as shown below:

gradle-ep4-1

Note the conventional structure i.e. inside of the utils project. I have the src/main/java folder and inside of that are my package structure and Java class names. So for example, in the above case, the Java class is DateUtils and its package is com.mindstorm.apputils.

web project – build.gradle

Now, let us look at the build.gradle file for the web project. We will introduce the Jetty plugin a bit later.

The initial version of the build.gradle file is shown below:

apply plugin: "war"

dependencies {
     compile project(":utils")
     compile "javax.servlet:servlet-api:2.5"
}

Here we are applying the War plugin. Additionally, we are making this project dependent on the utils project and on the Servlet API 2.5 version, since we have some Servlets in this project.

As per the documentation “The War plugin extends the Java plugin to add support for assembling web application WAR files. It disables the default JAR archive generation of the Java plugin and adds a default WAR archive task.”

The plugin also adds the war and assemble tasks to the tasks available for the project.

You can look at the additional tasks that have been added by opening up a command window/terminal and navigating to the web folder.

Simple fire the following:

gradle tasks

and you should see additional tasks available via the war plugin that you have applied:

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend
on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles classes 'main'.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles classes 'test'.
war - Generates a war archive with all the compiled classes, the web-app content
 and the libraries.

Building the WAR file

Since we have made the web project depend on the utils project, we can build the entire Web Application, by going to the web folder in the terminal and firing the following command:

gradle assemble

You should see the following output in the console:

E:\mywebapp\web>gradle assemble
:utils:compileJava
:utils:processResources UP-TO-DATE
:utils:classes
:utils:jar
:web:compileJava
:web:processResources UP-TO-DATE
:web:classes
:web:war
:web:assemble

BUILD SUCCESSFUL

Total time: 5.804 secs

Notice, how Gradle takes care of the dependencies by compiling the utils project first and then building out the web project.

At this point in time, you should have a build folder inside your web folder. Go to the libs folder inside the build folder and you will find that the .war file has been generated for you.

The Jetty Plugin

It would be cool if we could not just build out the WAR file but also deploy it inside of the Jetty container and run the application for us to test out. That is exactly what the Jetty plugin can do for you.

To do this, we will modify our build.gradle file for the web folder as shown below: (Note : If you have downloaded the file from Github, then these changes are already present in the web/build.gradle file)

apply plugin: "war"
apply plugin: "jetty"

dependencies {
 compile project(":utils")
 compile "javax.servlet:servlet-api:2.5"
}

httpPort = 8080
stopPort = 9080
stopKey = "stopKey"

Notice the additional entries that we have added in bold. We have simply applied the Jetty plugin and also provided one of the standard properties (httpPort) available to configure the Jetty Web Server when it is launched i.e. the HTTP Port. We have specified 8080 as the value. Additionally, we have provided the stopPort and stopKey properties so that we can stop the Jetty server from the command line/terminal. Note that the httpPort and stopPort are different.

Save the above in the build.gradle file. Go to the web folder again and fire the following command again:

gradle tasks

You will notice that the additional tasks around Jetty have also got added now (only the additional Jetty tasks are shown in the output below):

Web application tasks
---------------------
jettyRun - Uses your files as and where they are and deploys them to Jetty.
jettyRunWar - Assembles the webapp into a war and deploys it to Jetty.
jettyStop - Stops Jetty.

The tasks look straightforward. Our goal should be now to assemble the WAR, deploy the same into Jetty and run the server too.

To do that, simply give the following command in the web project.

gradle jettyRunWar

This will build things, if they are updated and you should see output similar to the following:

E:\mywebapp\web>gradle jettyRunWar
:utils:compileJava UP-TO-DATE
:utils:processResources UP-TO-DATE
:utils:classes UP-TO-DATE
:utils:jar UP-TO-DATE
:web:compileJava UP-TO-DATE
:web:processResources UP-TO-DATE
:web:classes UP-TO-DATE
:web:war UP-TO-DATE
> Building 88% > :web:jettyRunWar > Running at http://localhost:8080/web

This means that the server has got started with our web application and is available at http://localhost:8080/web.

If we navigate to the URL in a browser, we see the web application come up nicely as shown below:

webapp-screenshot

You can even visit the servlet configured if you want i.e. http://localhost:8080/web/daystogo.

To stop the Jetty Server, you can open up command console / terminal, navigate to the web folder and then run the following command:

gradle jettyStop

Moving forward

This tutorial helped you understand how to build your Java Web Applications using Gradle. Specifically, we looked at how the WAR and Jetty Plugin makes this possible.

I recommend that you also look at a couple of examples that ship with the standard Gradle build. These examples are present in samples/webApplication folder of your Gradle installation directory. The folder has two examples : quickstart, which is sort of what we have seen here. The other example customized is interesting and shows how you can move away from convention and specify exactly what files and from where it should take to build the Web application.

In the next episode, we shall look at the Google App Engine plugin in Gradle, and how it can help build/run/deploy our Java Web applications written for the App Engine platform.

Till then, Happy Gradling!

References

Advertisements

14 thoughts on “Gradle Tutorial : Part 4 : Java Web Applications

  1. again, excellent!

    just a comment, in my case I have to add two lines to one of the build.gradle files (otherwise, I was unable to run jettyStop)

    apply plugin: “war”
    apply plugin: “jetty”

    dependencies {
    compile project(“:utils”)
    compile “javax.servlet:servlet-api:2.5”
    }

    httpPort = 8080
    stopPort = 8080
    stopKey = “stopKey”

    thanks again!

    1. Thanks for leaving this comment. I had the same problem with no idea how to solve it, it was driving me nuts.

      Also copying and pasting your code didn’t work. I had to type it out manually.
      I got an error “Invalid variable name. Must start with a letter but was:”
      This was fixed by just typing it.

      Thanks for these great tutorials!

  2. Hi,

    You may want to change this line:

    compile “javax.servlet:servlet-api:2.5”
    to this:
    providedCompile “javax.servlet:servlet-api:2.5”

    As the first one will embed the jar file into your WEB-INF lib directory, and you don’t want that as it risks clashing with the one provided by your application server.

    Regs,

    Florian.

    1. Agreed…the tutorial should be corrected.. It is very important to use the providedCompile configuration for container-supplied dependencies. The way the tutorial shows it is very dangerous because it may work in your test environment but then suddenly fail at runtime when deployed to a different Servlet container. I have seen this happen. The common symptom is a NoSuchMethodError thrown when the container tries to invoke a method that was introduced in a later version of Servlet API such as ServletRequest.isAsyncStarted(), while the version of the class that was incorrectly bundled in the WAR has taken precedence.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s