-
Notifications
You must be signed in to change notification settings - Fork 5
Chapter 8 Demo
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.
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.
- 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 themaven-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/pokemonto 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.
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...
- 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 providesrc/main/webapp/WEB-INF/web.xmlto 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
MockitoJUnitRunnerand it should have a setup method which builds an instance ofMockMcvviaMockMvcBuilders.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 likeMockMvcRequestBuilders.get("/some/service")(.get()will invoke an HTTPGETservice,.post()aPOSTservice, and so on). The return value ofthis.mockMvc.perform(...)can then be checked by invoking.andExpect(...)with various values gathered from the static methods onMockMvcResultsMatchers. 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/getand asserts that the status code is 200 and the response body is a string with the appropriate value
- Create a test method which invokes
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.
- 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
@Controlleris 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
DispatcherServletwhich provides all of the hard, long complicated code for implementing theServletinterface. - The new servlet needs to know about our Spring application, so provide it with a
<context-param>whose name iscontextConfigLocationand 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 newPokemonServletto/secure/jas. The/secureis a standard UP phenomenon that informs our Apache server that the application should be secured via SiteMinder. The/jasis 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 theweb.xmlfile. - Before we can deploy our WAR, we'll need to connect
ControllerConfigto the rest of our Spring container. Remember: ourweb.xmlis pointing toMainConfig, butMainConfigis not yet importingControllerConfig. Go ahead and add a reference toControllerConfiginMainConfig's invocation of@Import.