Inheritance of Maven dependencies
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.
Tip
A transitive dependency is a required dependency with the needed dependency. Transitive dependencies have been automatically imported since Maven 2.0.
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.
Including third-party dependencies
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.
The Spring Framework dependency model
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 dependency
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:
- The
/WEB-INF/web.xml deployment descriptor. - The
/WEB-INF/classes/ directory for servlet and utility classes. The classes in this directory must be available to the application class loader. - The
/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.
The target runtime environment
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 tomcat8\lib 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.
The Spring web application context
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:
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
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
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
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:
- Excluding the lowest version from the classpath
- Not upgrading the dependency
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.
Tip
By default, the web resources are copied to the WAR root. To override the default destination directory, specify the target path *.