Skip to content

Chapter 8 Demo

David Zemon edited this page Feb 9, 2017 · 3 revisions

Web Module

We need a better title for this chapter... hmmm...

In this chapter, we're going to show the basics of putting a Java application on the web. Most of this is, in fact, not related to Spring in any way. This chapter largely covers what is known as Java Enterprise Edition, JaveEE, J2EE, or just JEE. We will, however, show how to tie your Spring container into this new web application.

Demo 1

Like every new programming assignment, we'll start with "Hello, World!" This demo will make the fewest possible changes such that we'll be able to see our application from a web browser. Note that these changes have nothing to do with Spring.

  1. Java web applications are bundled with a different extension: "war" instead of "jar." Tell Maven that this is a web application by adding a new XML tag: <packaging>war</packaging>
  • Delete the maven-jar-plugin (because we're not building a jar) and the maven-shade-plugin (because we're not building any jar, let alone a shaded jar).

  • "WAR" files are typically deployed to Java web servers that are started up by some magical demons that Union Pacific calls either DEA or OSG. In order for us to test our application locally without bothering those demons, we'll add a new Maven plugin called org.apache.tomcat.maven:tomcat7-maven-plugin. Note that this plugin IS NOT NECESSARY to build our application; it is simply a useful tool for running our application locally without needing a separate installation of Tomcat or some other Java web server.

  • To most closely mimic the configuration that will be used in dev, test, and production environments, we should configure the Tomcat plugin to deploy our application to a specific context path. Set the context path by providing /tng/pokemon to the <path> tag.

  • The web application deployment descriptor is also a required file for deploying a Java web application. The bare minimum for this file is pretty bare:

    <?xml version="1.0" encoding="UTF-8" ?>
    <web-app version="2.5"
             xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                                http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
        <display-name>tng-pokemon</display-name>
    </web-app>

    Place this file into src/main/webapp/WEB-INF/web.xml. Note that the value for the <display-name> can be whatever floats your boat.

  • The final step isn't entirely necessary, but it wouldn't be a very exciting demo without it. We'll go ahead and create an HTML file that will be accessible from our web application as confirmation that the application is A) deployed and B) running. Drop the following contents into src/main/webapp/index.html:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Pokemon</title>
      </head>
      <body>
        <h1>Pokemon Guessing Game</h1>
        <p>Silly programmer. This page is useless.</p>
      </body>
    </html>
  • The application can be built and started with the following Maven command: mvn clean tomcat7:run. The Tomcat plugin will default to executing on port 8080, so point your web browser to http://localhost:8080/tng/pokemon and bask in the beauty that is your HTML content being served through Java.

Demo 2 - Part 1 - Say "Hello"

Now it's time to write some real code and make our Spring application respond to the web. No more static HTML files (feel free to delete index.html if you choose). Lots of changes in this demo, so get ready...

  1. Providing special packaging instructions for our WAR wasn't required by a basic HTML application, but anything more complex requires configuration via Maven's org.apache.maven.plugins:maven-war-plugin. Add an invocation of that plugin and provide src/main/webapp/WEB-INF/web.xml to the <webXml> tag in the configuration.
  • Two new dependencies are needed for even the bare bones basics of SpringMVC to function. Add the following to your pom.xml:
    • org.springframework:spring-webmvc (compile)
    • javax.servlet:javax.servlet-api (provided) (this dependency is easy to forget, but weird things happen when you don't add it)
  • Create your first ever "controller": com.uprr.app.tng.spring.controller.HeartbeatController. Having a "HeartbeatController" is a common pattern that can be useful for quickly determining whether or not your Java server is "up." We'll use this class for demonstrating various functionality provided by the SpringMVC module.
  • In proper TDD fashion, the first thing we should do after creating the controller is to create a unit test for it. Once created, we'll set up the boiler plate code that every controller's test class needs. It should be run with Mockito's MockitoJUnitRunner and it should have a setup method which builds an instance of MockMcv via MockMvcBuilders.standaloneSetup(...). Note that this is a very new concept for UP, and there's a reasonable chance that you will be the first person to show your team this new way to test controllers.
  • SpringMVC endpoints (services) can be invoked in our test by method by invoking this.mockMvc.perform(...). The input to .perform(...) is typically something like MockMvcRequestBuilders.get("/some/service") (.get() will invoke an HTTP GET service, .post() a POST service, and so on). The return value of this.mockMvc.perform(...) can then be checked by invoking .andExpect(...) with various values gathered from the static methods on MockMvcResultsMatchers. This entire test method can often be written a single, fluid method call (but be aware, that can make debugging via breakpoints difficult, so temporary local variables may come in handy).
    • Create a test method which invokes /heartbeat/get and asserts that the status code is 200 and the response body is a string with the appropriate value

Demo 2 - Part 2 - Add the Controller to the Spring Container

Having a unit test is good, but we still can't deploy this application to a server. Spring doesn't know that our controller even exists.

  1. Add the controller as a bean. "Controller" is a new pattern in the code (like DAO), so it belongs in its own configuration class. Nothing fancy is needed here - just a simple bean with no dependencies. Simply having a bean in the Spring container whose class is annotated with @Controller is all that is necessary to wire in new controllers to an existing Spring MVC project.
  • Unfortunately, this isn't an existing Spring MVC project, it's a brand new one. Our web application deployment descriptor will inform the Java server where our applications entry point is. We'll do that by listing out all of our applications servlets and what parameters should be provided to those servlets. Because we're smart and we're using Spring to wire our project, we can take advantage of Spring's DispatcherServlet which provides all of the hard, long complicated code for implementing the Servlet interface.
  • The new servlet needs to know about our Spring application, so provide it with a <context-param> whose name is contextConfigLocation and value is the fully-qualified name to our main configuration class: com.uprr.app.tng.spring.config.MainConfig. The other context parameter in the demo is boiler plate and we don't really care about it... just add it.
  • With the servlet loaded by the server, we need to inform the server which requests should be routed to our new servlet. This is because a single deployable unit (the WAR file) can have multiple servlets (not that this happens at UP very often, but it's possible so we must code for it). In other words, we have to "map" our servlet to a URL. Add the <servlet-mapping> tag and map the new PokemonServlet to /secure/jas. The /secure is a standard UP phenomenon that informs our Apache server that the application should be secured via SiteMinder. The /jas is a convention that allows us to disambiguate Java service calls from static content such as HTML files.
  • Finally, add some boiler plate code for the <context-listener> that is black magic and doesn't matter. This completes the web.xml file.
  • Before we can deploy our WAR, we'll need to connect ControllerConfig to the rest of our Spring container. Remember: our web.xml is pointing to MainConfig, but MainConfig is not yet importing ControllerConfig. Go ahead and add a reference to ControllerConfig in MainConfig's invocation of @Import.

Clone this wiki locally