6. A Multi-Module Project

6.1. Introduction

In this chapter, we create a multi-module project that combines the examples from the two previous chapters. The simple-weather code developed in Chapter 4, Customizing a Maven Project will be combined with the simple-webapp project defined in Chapter 5, A Simple Web Application to create a web application that retrieves and displays weather forecast information on a web page. At the end of this chapter, you will be able to use Maven to develop complex, multi-module projects.

6.1.1. Downloading this Chapter’s Example

The multi-module project developed in this example consists of modified versions of the projects developed in Chapter 4, Customizing a Maven Project and Chapter 5, A Simple Web Application, and we are not using the Maven Archetype plugin to generate this multi-module project. We strongly recommend downloading a copy of the example code to use as a supplemental reference while reading the content in this chapter. This chapter’s example project may be downloaded with the book’s example code at:

http://books.sonatype.com/mvnex-book/mvnex-examples.zip

Unzip this archive in any directory, and then go to the ch-multi/ directory. There you will see a directory named simple-parent, which contains the multi-module Maven project developed in this chapter. In this directory, you will see a pom.xml and the two submodule directories, simple-weather and simple-webapp.

6.2. The Simple Parent Project

A multi-module project is defined by a parent POM referencing one or more submodules. In the simple-parent/ directory, you will find the parent POM (also called the top-level POM) in simple-parent/pom.xml. See simple-parent Project POM.

simple-parent Project POM.

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.sonatype.mavenbook.multi</groupId>
    <artifactId>simple-parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>
    <name>Multi Chapter Simple Parent Project</name>

    <modules>
        <module>simple-weather</module>
        <module>simple-webapp</module>
    </modules>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Notice that the parent defines a set of Maven coordinates: the groupId is org.sonatype.mavenbook.multi, the artifactId is simple-parent, and the version is 1.0. The parent project doesn’t create a JAR or a WAR like our previous projects; instead, it is simply a POM that refers to other Maven projects. The appropriate packaging for a project like simple-parent that simply provides a Project Object Model is pom. The next section in the pom.xml lists the project’s submodules. These modules are defined in the modules element, and each module element corresponds to a subdirectory of the simple-parent directory. Maven knows to look in these directories for pom.xml files, and it will add submodules to the list of Maven projects included in a build.

Lastly, we define some settings which will be inherited by all submodules. The simple-parent build configuration configures the target for all Java compilation to be the Java 8 JVM. Since the compiler plugin is bound to the lifecycle by default, we can use the pluginManagement section do to this. We will discuss pluginManagement in more detail in later chapters, but the separation between providing configuration to default plugins and actually binding plugins is much easier to see when they are separated this way. The dependencies element adds JUnit 3.8.1 as a global dependency. Both the build configuration and the dependencies are inherited by all submodules. Using POM inheritance allows you to add common dependencies for universal dependencies like JUnit or Log4J.

6.3. The Simple Weather Module

The first submodule we’re going to look at is the simple-weather submodule. This submodule contains all the code from the previous section Chapter 4, Customizing a Maven Project.

simple-weather Module POM.

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.sonatype.mavenbook.multi</groupId>
        <artifactId>simple-parent</artifactId>
        <version>1.0</version>
    </parent>
    <artifactId>simple-weather</artifactId>
    <packaging>jar</packaging>

    <name>Multi Chapter Simple Weather API</name>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <configuration>
                        <testFailureIgnore>true</testFailureIgnore>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

    <dependencies>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.14</version>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

In simple-weather's pom.xml file, we see this module referencing a parent POM using a set of Maven coordinates. The parent POM for simple-weather is identified by a groupId of org.sonatype.mavenbook.multi, an artifactId of simple-parent, and a version of 1.0.

The WeatherService class shown in The WeatherService Class is defined in src/main/java/org/sonatype/mavenbook/weather, and it simply calls out to the three objects defined in Chapter 4, Customizing a Maven Project. In this chapter’s example, we’re creating a separate project that contains service objects that are referenced in the web application project. This is a common model in enterprise Java development; often a complex application consists of more than just a single, simple web application. You might have an enterprise application that consists of multiple web applications and some command-line applications. Often, you’ll want to refactor common logic to a service class that can be reused across a number of projects. This is the justification for creating a WeatherService class; by doing so, you can see how the simple-webapp project references a service object defined in simple-weather.

The WeatherService Class.

package org.sonatype.mavenbook.weather;

import java.io.InputStream;

public class WeatherService {

    public WeatherService() {}

    public String retrieveForecast( String zip ) throws Exception {
        // Retrieve Data
        InputStream dataIn = new YahooRetriever().retrieve( zip );

        // Parse Data
        Weather weather = new YahooParser().parse( dataIn );

        // Format (Print) Data
        return new WeatherFormatter().format( weather );
    }
}

The retrieveForecast() method takes a String containing a zip code. This zip code parameter is then passed to the YahooRetriever's retrieve() method, which gets the XML from Yahoo Weather. The XML returned from YahooRetriever is then passed to the parse() method on YahooParser which returns a Weather object. This Weather object is then formatted into a presentable String by the WeatherFormatter.

6.4. The Simple Web Application Module

The simple-webapp module is the second submodule referenced in the simple-parent project. This web application project depends upon the simple-weather module, and it contains some simple servlets that present the results of the Yahoo weather service query.

simple-webapp Module POM.

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.sonatype.mavenbook.multi</groupId>
        <artifactId>simple-parent</artifactId>
        <version>1.0</version>
    </parent>

    <artifactId>simple-webapp</artifactId>
    <packaging>war</packaging>
    <name>simple-webapp Maven Webapp</name>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.sonatype.mavenbook.multi</groupId>
            <artifactId>simple-weather</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>simple-webapp</finalName>
        <plugins>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>maven-jetty-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

This simple-webapp module defines a very simple servlet that reads a zip code from an HTTP request, calls the WeatherService shown in The WeatherService Class, and prints the results to the response’s Writer.

simple-webapp WeatherServlet.

package org.sonatype.mavenbook.web;

import org.sonatype.mavenbook.weather.WeatherService;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class WeatherServlet extends HttpServlet {
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws ServletException, IOException {
        String zip = request.getParameter("zip" );
        WeatherService weatherService = new WeatherService();
        PrintWriter out = response.getWriter();
        try {
            out.println( weatherService.retrieveForecast( zip ) );
        } catch( Exception e ) {
            out.println( "Error Retrieving Forecast: " + e.getMessage() );
        }
        out.flush();
        out.close();
    }
}

In WeatherServlet, we instantiate an instance of the WeatherService class defined in simple-weather. The zip code supplied in the request parameter is passed to the retrieveForecast() method and the resulting test is printed to the response’s Writer.

Finally, to tie all of this together is the web.xml for simple-webapp in src/main/webapp/WEB-INF. The servlet and servlet-mapping elements in the web.xml shown in simple-webapp web.xml map the request path /weather to the WeatherServlet.

simple-webapp web.xml.

<!DOCTYPE web-app PUBLIC
          "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
          "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>simple</servlet-name>
    <servlet-class>
      org.sonatype.mavenbook.web.SimpleServlet
    </servlet-class>
  </servlet>
  <servlet>
    <servlet-name>weather</servlet-name>
    <servlet-class>
      org.sonatype.mavenbook.web.WeatherServlet
    </servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>simple</servlet-name>
    <url-pattern>/simple</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>weather</servlet-name>
    <url-pattern>/weather</url-pattern>
  </servlet-mapping>
</web-app>

6.5. Building the Multimodule Project

With the simple-weather project containing all WAR file. To do this, you will want to compile and install both projects in the appropriate order; since simple-webapp depends on simple-weather, the simple-weather JAR needs to be created before the simple-webapp project can compile. To do this, you will run mvn clean install command from the simple-parent project:

[INFO] Scanning for projects...
[WARNING] 
[WARNING] Some problems were encountered while building the effective model for org.sonatype.mavenbook.multi:simple-webapp:war:1.0
[WARNING] 'build.plugins.plugin.version' for org.mortbay.jetty:maven-jetty-plugin is missing. @ line 31, column 21
[WARNING] 
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING] 
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING] 
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] Multi Chapter Simple Parent Project                                [pom]
[INFO] Multi Chapter Simple Weather API                                   [jar]
[INFO] simple-webapp Maven Webapp                                         [war]
[INFO] 
[INFO] -------------< org.sonatype.mavenbook.multi:simple-parent >-------------
[INFO] Building Multi Chapter Simple Parent Project 1.0                   [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ simple-parent ---
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ simple-parent ---
[INFO] Installing D:\maven\simple-parent\pom.xml to d:\maven\repo\org\sonatype\mavenbook\multi\simple-parent\1.0\simple-parent-1.0.pom
[INFO] 
[INFO] ------------< org.sonatype.mavenbook.multi:simple-weather >-------------
[INFO] Building Multi Chapter Simple Weather API 1.0                      [2/3]
[INFO] --------------------------------[ jar ]---------------------------------
[WARNING] The artifact org.apache.commons:commons-io:jar:1.3.2 has been relocated to commons-io:commons-io:jar:1.3.2
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ simple-weather ---
[INFO] Deleting D:\maven\simple-parent\simple-weather\target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ simple-weather ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 3 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ simple-weather ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 6 source files to D:\maven\simple-parent\simple-weather\target\classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ simple-weather ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ simple-weather ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 2 source files to D:\maven\simple-parent\simple-weather\target\test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ simple-weather ---
[INFO] Surefire report directory: D:\maven\simple-parent\simple-weather\target\surefire-reports
[INFO] Downloading from : http://maven.aliyun.com/nexus/content/groups/public/org/apache/maven/surefire/surefire-junit3/2.12.4/surefire-junit3-2.12.4.pom
[INFO] Downloading from : http://maven.aliyun.com/nexus/content/groups/public/org/apache/maven/surefire/surefire-providers/2.12.4/surefire-providers-2.12.4.pom
[INFO] Downloading from : http://maven.aliyun.com/nexus/content/groups/public/org/apache/maven/surefire/surefire-junit3/2.12.4/surefire-junit3-2.12.4.jar

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.sonatype.mavenbook.weather.yahoo.WeatherFormatterTest
0    INFO  YahooParser  - Creating XML Reader
245  INFO  YahooParser  - Parsing XML Response
320  INFO  WeatherFormatter  - Formatting Weather Data
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.549 sec
Running org.sonatype.mavenbook.weather.yahoo.YahooParserTest
466  INFO  YahooParser  - Creating XML Reader
470  INFO  YahooParser  - Parsing XML Response
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ simple-weather ---
[INFO] Building jar: D:\maven\simple-parent\simple-weather\target\simple-weather-1.0.jar
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ simple-weather ---
[INFO] Installing D:\maven\simple-parent\simple-weather\target\simple-weather-1.0.jar to d:\maven\repo\org\sonatype\mavenbook\multi\simple-weather\1.0\simple-weather-1.0.jar
[INFO] Installing D:\maven\simple-parent\simple-weather\pom.xml to d:\maven\repo\org\sonatype\mavenbook\multi\simple-weather\1.0\simple-weather-1.0.pom
[INFO] 
[INFO] -------------< org.sonatype.mavenbook.multi:simple-webapp >-------------
[INFO] Building simple-webapp Maven Webapp 1.0                            [3/3]
[INFO] --------------------------------[ war ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ simple-webapp ---
[INFO] Deleting D:\maven\simple-parent\simple-webapp\target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ simple-webapp ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ simple-webapp ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 2 source files to D:\maven\simple-parent\simple-webapp\target\classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ simple-webapp ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\maven\simple-parent\simple-webapp\src\test\resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ simple-webapp ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ simple-webapp ---
[INFO] No tests to run.
[INFO] 
[INFO] --- maven-war-plugin:2.2:war (default-war) @ simple-webapp ---
[INFO] Packaging webapp
[INFO] Assembling webapp [simple-webapp] in [D:\maven\simple-parent\simple-webapp\target\simple-webapp]
[INFO] Processing war project
[INFO] Copying webapp resources [D:\maven\simple-parent\simple-webapp\src\main\webapp]
[INFO] Webapp assembled in [444 msecs]
[INFO] Building war: D:\maven\simple-parent\simple-webapp\target\simple-webapp.war
[INFO] WEB-INF\web.xml already added, skipping
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ simple-webapp ---
[INFO] Installing D:\maven\simple-parent\simple-webapp\target\simple-webapp.war to d:\maven\repo\org\sonatype\mavenbook\multi\simple-webapp\1.0\simple-webapp-1.0.war
[INFO] Installing D:\maven\simple-parent\simple-webapp\pom.xml to d:\maven\repo\org\sonatype\mavenbook\multi\simple-webapp\1.0\simple-webapp-1.0.pom
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for Multi Chapter Simple Parent Project 1.0:
[INFO] 
[INFO] Multi Chapter Simple Parent Project ................ SUCCESS [  0.731 s]
[INFO] Multi Chapter Simple Weather API ................... SUCCESS [  5.100 s]
[INFO] simple-webapp Maven Webapp ......................... SUCCESS [  1.651 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  7.686 s
[INFO] Finished at: 2020-03-07T18:59:47+08:00
[INFO] ------------------------------------------------------------------------

When Maven is executed against a project with submodules, Maven first loads the parent POM and locates all of the submodule POMs. Maven then puts all of these project POMs into something called the Maven Reactor which analyzes the dependencies between modules. The Reactor takes care of ordering components to ensure that interdependent modules are compiled and installed in the proper order.

Note

The Reactor preserves the order of modules as defined in the POM unless changes need to be made. A helpful mental model for this is to picture that modules with dependencies on sibling projects are “pushed down” the list until the dependency ordering is satisfied. On rare occasions, it may be handy to rearrange the module order of your build — for example if you want a frequently unstable module towards the beginning of the build.

除非需要进行更改,否则Reactor会保留POM中定义的模块顺序。 一个有用的思维模型是描绘在兄弟项目上具有依赖项的模块在列表中被“下拉”,直到满足依赖项排序为止。 在极少数情况下,重新安排构建的模块顺序可能会很方便-例如,如果您希望在构建开始时经常使用不稳定的模块。

Once the Reactor figures out the order in which projects must be built, Maven then executes the specified goals for every module in the multi-module build. In this example, you can see that Maven builds simple-weather before simple-webapp, effectively executing mvn clean install for each submodule.

Note

When you run Maven from the command line you’ll frequently want to specify the clean lifecycle phase before any other lifecycle stages. When you specify clean, you make sure that Maven is going to remove old output before it compiles and packages an application. Running clean isn’t necessary, but it is a useful precaution to make sure that you are performing a “clean build”.

6.6. Running the Web Application

Once the multi-module project has been installed with mvn install, you can run the web application with mvn jetty:run.

[INFO] Configuring Jetty for project: simple-webapp Maven Webapp
[INFO] Webapp source directory = D:\maven\simple-parent\simple-webapp\src\main\webapp
[INFO] Reload Mechanic: automatic
[INFO] Classes = D:\maven\simple-parent\simple-webapp\target\classes
[INFO] Logging to org.slf4j.impl.SimpleLogger(org.mortbay.log) via org.mortbay.log.Slf4jLog
[INFO] Context path = /simple-webapp
[INFO] Tmp directory =  determined at runtime
[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] web.xml file = D:\maven\simple-parent\simple-webapp\src\main\webapp\WEB-INF\web.xml
[INFO] Webapp directory = D:\maven\simple-parent\simple-webapp\src\main\webapp
[INFO] Starting jetty 6.1.26 ...
[INFO] jetty-6.1.26
[INFO] No Transaction manager found - if your webapp requires one, please configure one.
[INFO] Started [email protected]:8080
[INFO] Started Jetty Server

Once Jetty has started, load http://localhost:8080/simple-webapp/weather?zip=01201 in a browser and you should see the formatted weather output.

*********************************
Current Weather Conditions for:
New York, NY, US

Temperature: 39
Condition: Fair
Humidity: 67
Wind Chill: 39
*********************************
发布了34 篇原创文章 · 获赞 1 · 访问量 1545

猜你喜欢

转载自blog.csdn.net/m0_37607945/article/details/104720039
今日推荐