In this recipe, we will add third-party dependencies to our pom.xml
files using inheritance. We will load Spring application contexts
and create the first controller of our application. Finally, we will deploy and start the web app in Tomcat.
Now that we have Eclipse ready and Maven configured properly, the fun can begin. We need to specify all the necessary Spring dependencies in our pom.xml
files, and we need to set up Spring so that it loads and retrieves its context for every module.
We also need to organize and optionally expose web resources, such as JSPs, JavaScript files, CSS files, and so on. If you've completed this configuration, we should end up with a static welcome page provided by the Tomcat server, started without exceptions!
Our first set of changes relate to the parent projects:
pom.xml
from the chapter_1
source code directory and select the pom.xml tab (underneath the main window).Copy and paste into the cloudstreetmarket-parent's pom.xml file the <properties>
, <dependencyManagement>
, and <build>
blocks.
Now, repeat the operation for zipcloud-parent.
pom.xml
file from the chapter_1 source code and click on the pom.xml tab.<properties>
and <dependencyManagement>
blocks. You should already have copied over the <build>
section in the third recipe.pom.xml
from the chapter_1 source code and select the pom.xml tab.pom.xml
the <build>
and <dependencies>
blocks.pom.xml
from the chapter_1 source code directory and click on the pom.xml tab.<build>
and <dependencies>
blocks.pom.xml
from the chapter_1 source code and click on the pom.xml tab.<dependencies>
block.If you still have Warnings in the project, your Eclipse Maven configuration may be out of synchronization with the local repository.
In this case, perform the following steps:
wars
and start Tomcat:Add the servers view in Eclipse. To do so, perform the following operations:
INFO: Starting ProtocolHandler ["http-nio-8080"] Oct 20, 2014 11:43:44 AM org.apache.coyote.AbstractProtocol start INFO: Starting ProtocolHandler ["ajp-nio-8009"] Oct 20, 2014 11:43:44 AM org.apache.catalina.startup.Cata.. start INFO: Server startup in 6898 ms
Finally, if you try to reach http://localhost:8080/portal/index.html
with your browser, you should receive the following HTML content:
Through this recipe, we have been moving across web resources and Maven dependencies related to Spring, Spring MVC, and the web environment. Now, we will go through the way that Maven dependency and plugin management are performed. We will then talk about the Spring web application context and finally about the organization and packaging of web resources.
There are two strategies concerning the inheritance of dependencies between parent projects and children modules. They both are implemented from the parent project. On the one hand, we can choose to define these dependencies directly from the <dependencies>
node, shaping a basic inheritance in this way. On the other hand, to set up a managed inheritance, we can define the <dependencies>
node as a child node of <dependencyManagement>
. Let's have a look at the differences between the two.
With a basic inheritance, all the dependencies specified in the parent's pom.xml
file are automatically inherited into the child module with the same attributes (scope, version, packaging type, and so on) unless you override them (redefining these dependencies with the same couple groupId
/artifactId
).
On the one hand, it provides the option of using the versions of the dependencies we want in the modules we want. On the other hand, we can end up with a very complex dependencies schema and huge pom.xml
files in the children modules. Also, managing version conflicts with external transitive dependencies can be a pain.
There are no standards in this inheritance type for external dependencies.
With the < dependencyManagement>
mechanism, dependencies defined in the parent pom.xml
are not automatically inherited in children modules. However, the dependency attributes (scope, version, packaging type, and so on) are pulled from the parent dependency's definition, and therefore, the redefinition of these attributes is made optional.
This process drives us towards a centralized dependency definition where all the children modules use the same versions of dependencies unless a specific dependency requires a custom one.
Among the dependencies copied over, you might have noticed a few Spring modules, some test, web, logging, and utility dependencies.
The idea has been to start with a basic web development tool box, which is enhanced with all the Spring modules. We will visit most of the dependencies actually included when we face a particular situation.
As presented in the following diagram taken from the spring.io website, these days, the Spring Framework is currently made of 20 modules that are grouped in different areas:
These modules have been included in the parent POMs as managed dependencies. This will allow us, later on, to quickly cherry-pick the needed ones, narrowing down a selection for our wars
.
The Spring MVC module is self-contained in the spring-webmvc
jar. Spring MVC in a web application is a fundamental element, as it handles incoming client requests and smoothly monitors the business operations from controllers. It finally offers a number of tools and interfaces capable of preparing responses in the format the clients expect them in.
All this workflow comes along with the spring-webmvc jar output HTML content or web services.
Spring MVC is entirely integrated in the Spring Framework, and all its components are standard with regard to the Spring architecture choices.
In each parent pom.xml
file, we have defined a <properties>
block as part of the <project>
section. These properties are user-defined properties bound to a project, but we can also define such properties within a Maven Profile option. Like variables, properties are referenced in the POMs with their name surrounded by ${…}.
There is a standard on defining property names using periods as word separators. More than a standard, it is a uniform notation to access both user-defined variables and attributes of objects that constitute the Maven model. The Maven model is the public interface of Maven and starts from the project level.
The POM XML Schema Definition (xsd) is generated from this Maven model. It can sound abstract but in the end, the Maven model is only a set of POJOs with getters and setters. Have a look at the JavaDoc of the Maven model from the URL below, to identify concepts, specific to pom.xml files (Build, Dependency, Plugin, and so on.):
http://maven.apache.org/ref/3.0.3/maven-model/apidocs/index.html
To summarize, we can retrieve a node value defined in a POM and navigate the Maven model hierarchy using a period-based expression language that targets the getters.
For example, ${project.name}
references the current project.getName()
, ${project.parent.groupId}
, the current project.getParent().getGroupId()
, and so on.
Defining user properties that match an existing path of the Maven model is a way of overriding its value. That's what we have done for project.build.sourceEncoding
.
Maven also offers the possibility to reach properties defined in the settings.xml
files such as ${settings.localRepository}
; but also environment variables such as ${env.JAVA_HOME}
; and Java System properties such as ${java.class.path}
, ${java.version}
, ${user.home}
, or ${user.name}
.
If you remember, we copied/pasted the entire src/main/webapp
directory from the chapter_1
source code. The webapp
directory name is a Maven standard. The webapp
folder in Eclipse doesn't need to be tagged as a source folder for the build path, as it would create a complex and useless package hierarchy for static files. Preferably, it appears as a plain directory tree.
The webapp
directory must be seen as the document root of the application and positioned at the root level of the WAR. The public static web resources under webapp
, such as HTML files, Javascript, CSS, and image files, can be placed in the subdirectories and structure of our choice. However, as described in the Servlet 3.0 Specification, the WEB-INF
directory is a special directory within the application hierarchy. All its contents can never be reached from outside the application; its content is accessible from the servlet code calling for getResource
or getResourceAsStream
on ServletContext
. The specification also tells us that the content of a WEB-INF
directory is made up of the following:
/WEB-INF/web.xml
deployment descriptor./WEB-INF/classes/
directory for servlet and utility classes. The classes in this directory must be available to the application class loader./WEB-INF/lib/*.jar
area for Java ARchive files. These files contain servlets, beans, static resources, and JSPs packaged in a JAR file and other utility classes useful to the web application. The web application class loader must be able to load classes from any of these archive files.It is good practice to create a jsp
directory inside the WEB-INF
folder so that the jsp
files cannot be directly targeted without passing through an explicitly defined controller.
JSP applications do exist, and by definition, they will not follow this practice. These type of applications may be suited to certain needs, but they also don't specifically promote the use of an MVC pattern nor a great separation of concerns.
To use JSPs in a web application, the feature must be enabled in web.xml
with the definition of a servlet of the org.apache.jasper.servlet.JspServlet
type mapped to the JSP files location.
We have experienced warnings in the index.jsp
files. We have sorted them out by adding a target runtime to our projects. We also saw that Tomcat comes with the Eclipse Compilator for Java as a JAR library. To perform the JSP compilation, the tomcat8lib
directory must include the following JAR libraries: jsp-api
, servlet-api
and el-api
, and so on. Specifying a target runtime for a project in Eclipse emulates and anticipates situation where the application will be run from an external Tomcat container (setup with those libraries). This also explains why the jsp-api
and el-api
dependencies are defined in the parent POMs with a provided scope.
In the web.xml
files, we defined a special type of Servlet, the Spring MVC DispatcherServlet
, and we named it spring
. This servlet covers the widest /*
URL pattern. We will revisit the DispatcherServlet
in the next chapter.
A DispatcherServlet
has its own discovery algorithm that builds up WebApplicationContext
. An optional contextConfigLocation
initialization parameter is provided that points to a dispatcher-context.xml
file. This parameter overrides the default expected filename and path (/WEB-INF/{servletName}-servlet.xml
) for the WebApplicationContext
defined in the DispatcherServlet
discovery logic.
With the load-on-startup
attribute set to 1
, as soon as the servlet container gets ready, a new WebApplicationContext
gets loaded and scoped only for the starting servlet. Now, we don't wait for the first client request to load WebApplicationContext.
A Spring WebApplicationContext
file usually defines or overrides the configuration and beans that Spring MVC offers to the web application.
Still in the web.xml
file, an org.sfw.web.context.ContextLoaderListener
listener is set up. The purpose of this listener is to start and shut down another Spring ApplicationContext
, which will be the root one following the container's life cycle.
To load more than one spring context file easily, the trick here is to use the classpath notation (which is relative) and the star (*
) character in the resource path:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/META-INF/spring/*-config.xml</param-value> </context-param>
Doing so allows us to load all the context files encountered in the classpath that match a standard notation and location. This approach is appreciated for the consistency it imposes but also for the way it targets context files in underlying jars.
The aggregation of all the matching context files creates an ApplicationContext
root with a much broader scope, and the WebApplicationContext
inherits it. The beans we define in the root context become visible to the WebApplicationContext
context. We can override them if needed. However, the DispatcherServlet
context's beans are not visible to the root context.
Maven is, above all, a plugin's execution framework. Every task run by Maven corresponds to a plugin. A plugin has one or more goals that are associated individually to life cycle phases. Like the dependencies, the plugins are also identified by a groupId
, an artifactId
, and a version. When Maven encounters a plugin that is not in the local repository, it downloads it. Also, a specific version of Maven targets, by default, a number of plugins that match the life cycle phases. These plugins are frozen on fixed versions and therefore on a defined behavior—you need to override their definition to get a more recent version or to alter their default behavior.
The maven-compiler-plugin is a Maven core plugin. The core plugins are named as such because their goals are triggered on Maven core phases (clean, compile, test, and so on.). Noncore plugins relate to packaging, reporting, utilities, and so on. It is good practice to redefine the maven-compiler-plugin to control which version of the compiler is to be used or to trigger some external tools' actions (the m2eclipse project management tool, actually).
As its name suggests, the maven compiler plugin compiles the Java sources. For that, it uses the javax.tools.JavaCompiler
class and has two goals: compiler:compile
(triggered as part of the compile phase to compile the java/main
source classes) and compiler:testCompile
(triggered as part of the test-compile phase to compile the java/test
source classes).
The maven-surefire-plugin is also a Maven core plugin that has only one goal: surefire:test
. This is invoked as part of the default life cycle (the test phase) to run unit tests defined in the application. It generates reports (*.txt
or *.xml
), by default, under the ${basedir}/target/surefire-reports
location.
The maven-enforcer-plugin is very useful to define environmental conditions as critical for the project. It has two goals: enforcer:enforce
(bound, by default, to the validate phase, where it executes each defined rule once per module) and enforcer:display-info
(it displays the detected information on execution of the rules).
The most interesting standard rule is probably DependencyConvergence
: it analyzes all the used dependencies (direct and transitive) for us. In case of divergence of a version, it highlights it and stops the build. When we face this kind of conflict, it is amazingly easy to decide between the following:
We also quickly talked about the <pluginManagement>
section, which was associated to the maven-enforcer-plugin. In this case, this is because m2eclipse doesn't support this plugin. Thus, to avoid a warning in Eclipse, it is necessary to add this section so that m2eclipse skips the enforce goal.
Using the maven-war-plugin, we redefined in our web POMs. We have again overridden the default behavior of this plugin that is used to package web modules. This is definitely necessary if you have a non-Maven standard project structure.
We may want to package our web resources in a different way that how it is organized in our IDE. We may need, for some reason, to exclude some resources from the war packaging or we may even want to give a name to the built war so that it can be used by the servlet container that matches a specific context path in the application URLs (/api
, /app
, and so on). Filtering, moving web resources around, and managing the generated war is the purpose of this plugin.
This has been quite a broad overview about concepts that naturally require deeper interest:
http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
http://maven.apache.org/ref/3.0.3/maven-model/apidocs/index.html
web.xml
file definition and about the structure of a WebArchive at: http://download.oracle.com/otn-pub/jcp/servlet-3.0-fr-eval-oth-JSpec/servlet-3_0-final-spec.pdfOne other interesting enough plugin which could also be highlighted here is the maven-checkstyle-plugin. When a team is growing, we sometimes need to guarantee the maintenance of certain development practices or we may need to maintain specific security-related coding practices. Like the maven-enforcer-plugin, the maven-checkstyle-plugin makes our builds assertive against this type of violation.
Find out more about this plugin, again in the Maven documentation, at: http://maven.apache.org/plugins/maven-checkstyle-plugin.