Archived content

Archived date: 2019-05-22

This content is no longer being updated or maintained. The content is provided “as is.” Given the rapid evolution of technology, some content, steps, or illustrations may have changed.

Introduction

In Part 1 of this series, you migrated the Daytrader3 application from IBM® WebSphere® Application Server Liberty 8.5.5.0 to the latest version of Liberty server. In Part 2, you migrated the Daytrader3 application that is running in an on-premises Liberty 17.0.0.2 server to the cloud platforms of IBM Cloud Private (with Docker and Kubernetes) and IBM Cloud Public (with Cloud Foundry). Now, in Part 3, you add build automation to the monolith by using the Maven industry standard build lifecycle. You also augment the lifecycle with plug-in bindings to support continuous integration of the monolith by converting the Web Tools Platform project from Part 2 into the Maven projects.

Part 3 starts with the Eclipse workspace, referred to as the Web Tools Platform workspace, from Part 2. With this workspace, you can build the monolith and run manual integration tests against a stand-alone Liberty server. However, you cannot build the monolith, nor run continuous integration from the command line. This workspace does not provide a good foundation for DevOps.

To continue with Part 3, you must have the Web Tools Platform workspace that you created in Part 2. If you did not complete these steps, you can download the final copy of the workspace from Part 2.

Prerequisites

If you are using older versions of macOS or Microsoft Windows, you might need to use the Docker Toolbox and create your own Docker Machine. To test the installation, on the console, enter docker run hello-world.

Converting Eclipse projects to Maven projects

You have two options for converting Eclipse Web Tools Platform projects into Maven projects:

  • Option 1: Enable the M2Eclipse Plug-in on the existing projects. You right-click the Eclipse project and select Configure -> Convert to Maven Project. This method avoids manual copying. However, it keeps the existing folder structure, which does not follow the Maven standard layout, making it more difficult to customize the Maven lifecycle with the required plug-ins for automation and continuous integration. All plug-in documentation, such as the Maven Docker Plug-in, the Liberty Maven Plug-in, and the Maven Failsafe Plug-in, reflects the Maven standard layout.
  • Option 2: Use the Maven Archetype Plug-in to create Maven projects, and manually copy the sources and libraries, or create a script for doing so. This method results in a project structure that follows the Maven standards (see Introduction to the standard directory layout) and makes it easier to automate the monolith.

    Standard directory layout for Maven

In our experience, working within the Maven standard directory layout is the better option, especially for monolithic applications, such as the Daytrader3 application. Enabling the M2Eclipse Plug-in did not work for our application. Converting the Web Tools Platform projects to Maven projects for the application requires a lot of manual intervention. Therefore, we chose to use the Maven Archetype Plug-in option for adding build automation and continuous integration of the monolith.

The remainder of this tutorial shows you how to add build automation and continuous integration to the monolith. If you follow the steps in this tutorial, you see the resulting Maven project structure (see the following figure).

The Maven project structure

Build automation for the monolithic application

To begin, you introduce build automation to the DayTrader monolithic application. You create, configure, and populate the Maven projects for the archives that make up the monolith. You can create these projects by using multi-module projects or individual projects:

  • Multi-module projects: This approach introduces a top-level aggregator as a Maven project and then creates Maven modules for the archives of the monolith. You can do the build automation by building the top-level aggregator, which in turn builds the dependent modules in the correct order. The downside of this approach is that it introduces a bidirectional dependency between the modules of the monolith and the top-level aggregator. Using multi-module projects reinforces the high degree of coupling in the monolith.
  • Individual project: This approach creates a Maven project per Java Enterprise Edition (Java EE) module that makes up the monolith. It does not introduce a top-level aggregator or extra coupling. But, the automated build requires building the individual projects in the correct order (for example: the Enterprise JavaBeans (EJB) project, the web projects, and then the enterprise archive (EAR) project). This tutorial uses this approach instead of introducing extra coupling by using a top-level aggregator.

To convert the Web Tools Platform projects to Maven, we looked at using Eclipse (by selecting Configure -> Convert to Maven). This wizard works for simple web apps, but it did not work for the DayTrader enterprise application. For example, the build of the web project failed to compile the source code, and the build of the daytrader-ee6 project failed to create the EAR file. Even if it did work, it creates a non-standard layout instead of standard Maven directory structure, which would make it difficult to add plug-ins. Plug-ins are based on standard layouts, including their documentation and defaults.

The remainder of this section shows how to:

  1. Create the Maven projects.
  2. Update the Maven Project Object Models (POMs).
  3. Initialize the Maven projects.
  4. Run the build lifecycle.

Create the Maven projects

To create the Maven projects, you must establish the naming conventions for the projects, specify the archetypes for creating the projects, and generate the projects by running the archteype:generate command.

Naming conventions. To create the Maven projects for the monolith, a naming convention is required, and it must follow the Maven conventions. This naming convention must also follow the package naming and module naming conventions of the existing monolith. In this tutorial, we use the following naming convention:

  • For GroupId, use com.ibm.websphere.samples.daytrader for all projects. The GroupId is the base package name that the existing code of the monolith uses.
  • For ArtifactId, use the Java EE module name (daytrader-ee6, dt-ejb, web, or Rest). Because Maven uses ArtifactId for the project, the Maven projects are daytrader-ee6, dt-ejb, Rest, and web.
  • For Version and Package, use the default values.

Now that you know the naming conventions for each project and the archetypes (see the side bar) to use, you are ready to generate the Maven projects for the monolith. For each project, you run the archetype:generate commands in noninteractive mode. You can run these commands from any folder because the command creates the project in that folder. You must remember the location of those projects because you will need it later when you import the Maven projects into an Eclipse workspace.

  1. Create the daytrader-ee6 project:

    $ mvn archetype:generate -DgroupId=com.ibm.websphere.samples.daytrader -DartifactId=daytrader-ee6 -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=ear-javaee6 -DinteractiveMode=false
    
  2. Create the dt-ejb project:

    $ mvn archetype:generate -DgroupId=com.ibm.websphere.samples.daytrader -DartifactId=dt-ejb -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=ejb-javaee6 -DinteractiveMode=false
    
  3. Create the Rest project:

    $ mvn archetype:generate -DgroupId=com.ibm.websphere.samples.daytrader -DartifactId=Rest -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=webapp-javaee6 -DinteractiveMode=false
    
  4. Create the web project:

    $ mvn archetype:generate -DgroupId=com.ibm.websphere.samples.daytrader -DartifactId=web -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=webapp-javaee6 -DinteractiveMode=false
    

Update the Maven POMs

In Maven, the project settings are configured by using a single pom.xml file. Build automation requires two kinds of updates to the POM—adding dependencies and configuring plug-ins—both of which are addressed in this section.

  1. Update the dt-ejb POM:

    a. Add the commons-logging dependency:

         <dependency>
         <groupId>commons-logging</groupId>
         <artifactId>commons-logging</artifactId>
         <version>1.0.3</version>
         </dependency>
    

    b. Configure the Maven Compiler Plug-in:

       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>2.3.2</version>
         <configuration>
           <source>1.8</source>
           <target>1.8</target>
         </configuration>
     </plugin>
    
  2. Update the web POM:

    a. Change the javaee-web-api to the javaee-api. This change is required because the web application is calling the Java Message Service (JMS) APIs, which are not provided by the javaee-web-api project.

     <dependency>
       <groupId>javax</groupId>
       <artifactId>javaee-api</artifactId>
       <version>6.0</version>
       <scope>provided</scope>
     </dependency>
    

    b. Add the dt-ejb dependency. This dependency is required because the web application is calling the business methods and passing in parameters from the domain model in the dt-ejb project.

       <dependency>
             <groupId>com.ibm.websphere.samples.daytrader</groupId>
             <artifactId>dt-ejb</artifactId>
             <version>1.0-SNAPSHOT</version>
             <type>ejb</type>
       </dependency>
    

    c. Configure the Maven Compiler Plug-in:

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

    d. Configure the Maven WAR Plug-in:

        <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-war-plugin</artifactId>
             <version>2.1.1</version>
             <configuration>
                <webXml>src/main/webapp/WEB-INF/web.xml</webXml>
             </configuration>
        </plugin>
    
  3. Update the Rest POM:

    a. Configure the Maven Compiler Plug-in:

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

    b. Configure the Maven WAR Plug-in:

     <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-war-plugin</artifactId>
         <version>2.1.1</version>
         <configuration>
             <webXml>src/main/webapp/WEB-INF/web.xml</webXml>
         </configuration>
       </plugin>
    
  4. Update the daytrader-ee6 POM:

    a. Add dependencies to the daytrader-ee6 modules:

     <dependencies>
         <dependency>
             <groupId>com.ibm.websphere.samples.daytrader</groupId>
             <artifactId>Rest</artifactId>
             <version>0.0.1-SNAPSHOT</version>
             <type>war</type>
         </dependency>
         <dependency>
             <groupId>com.ibm.websphere.samples.daytrader</groupId>
             <artifactId>dt-ejb</artifactId>
             <version>1.0-SNAPSHOT</version>
             <type>ejb</type>
         </dependency>
         <dependency>
             <groupId>com.ibm.websphere.samples.daytrader</groupId>
             <artifactId>web</artifactId>
             <version>1.0-SNAPSHOT</version>
             <type>war</type>
         </dependency>
       </dependencies>
    

    b. Configure the Maven EAR Plug-in so that Maven includes the correct build artifacts to include as modules in the EAR file and the name to use for those modules. This example uses the name bundleFileName. You must use the same module names as the monolith to avoid runtime errors. By default, the Maven EAR Plug-in copies the application.xml file and the libs folder from the src\main\application folder. The next step shows how to initialize these projects with resources from the monolith.

     <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-ear-plugin</artifactId>
       <version>2.6</version>
       <configuration>
           <version>6</version>
               <defaultLibBundleDir>lib</defaultLibBundleDir>
               <modules>
           <ejbModule>
               <groupId>com.ibm.websphere.samples.daytrader</groupId>
               <artifactId>dt-ejb</artifactId>
               <bundleFileName>dt-ejb.jar</bundleFileName>
           </ejbModule>
           <webModule>
               <groupId>com.ibm.websphere.samples.daytrader</groupId>
               <artifactId>web</artifactId>
               <bundleFileName>web.war</bundleFileName>
               </webModule>
            <webModule>
               <groupId>com.ibm.websphere.samples.daytrader</groupId>
               <artifactId>Rest</artifactId>
               <bundleFileName>Rest.war</bundleFileName>
             </webModule>
           </modules>
         </configuration>
       </plugin>
    

Copy the sources into the respective directories

Now, you copy sources from the existing Web Tools Platform projects into their respective Maven directories. If you have not already done so, download the Web Tools Platform workspace, and extract it to a local folder. You should see the following folders.

Web Tools Platform workspace

To copy the sources into their respective directories:

  1. Copy the following Web Tools Platform sources into the daytrader-ee6 Maven output directory. The first two rows in the table are required for build automation of the monolith. The last four rows are required for continuous integration of the monolith against a stand-alone Liberty server and against Liberty in a Docker container.

    Table 1. Web Tools Platform sources for the daytrader-ee6 directory

    | Web Tools Platform || Maven | |-|-|-| | Source directory | Filter | Output directory | | daytrader3-ee6 | lib | daytrader3-ee6\src\main\application | | daytrader3-ee6 | META-INF | daytrader3-ee6\src\main\application | | daytrader3-ee6 | config | daytrader3-ee6\src\main\resources | | wlp-17.0.0.2\servers\Daytrader3Sample | lafiles | daytrader3-ee6\src\main\liberty\config | | wlp-17.0.0.2\servers\Daytrader3Sample | resources | daytrader3-ee6\src\main\liberty\config | | wlp-17.0.0.2\servers\Daytrader3Sample | server.xml | daytrader3-ee6\src\main\liberty\config |

    After you copy the sources, you see the following daytrader-ee6 project directory structure.

    Maven project structure for the daytrader-ee6 directory

  2. Copy the following Web Tools Platform sources into the dt-ejb Maven output directory.

    Table 2. Web Tools Platform sources for the dt-ejb directory

    | Web Tools Platform || Maven | |-|-|-| | Source directory | Filter | Output directory | | dt-ejb\ejbModule | com | dt-ejb\src\main\java | | dt-ejb\ejbModule | META-INF | dt-ejb\src\main\resources |

    After you copy the sources, you see the following dt-ejb project directory structure.

    Maven project structure for the dt-ejb directory

  3. Copy the following Web Tools Platform sources into the Rest Maven output directory. In the Filter column, */ means that you must copy the entire contents of the Web Tools Platform source directory to the Maven output directory.

    Table 3. Web Tools Platform sources for the Rest directory

    | Web Tools Platform || Maven | |-|-|-| | Source directory | Filter | Output directory | | Rest\src | **\* | Rest\src\main\java| | Rest\WebContent | **\* | Rest\src\main\webapp |

    After you copy the sources, you see the following Rest project directory structure.

    Maven project structure for the Rest directory

  4. Copy the following Web Tools Platform sources into the web Maven output directory. Again, in the Filter column, */ means that you must copy the entire contents of the Web Tools Platform source directory to the Maven output directory.

    Table 4. Web Tools Platform sources for the web directory

    | Web Tools Platform || Maven | |-|-|-| | Source directory | Filter | Output directory | | Rest\src | **\* | Rest\src\main\java| | Rest\WebContent | **\* | Rest\src\main\webapp |

    After you copy the sources, the web project directory structure looks like the following example. Because the figure is truncated, you should also see the welcomeImg.jsp and pom.xml files.

    Maven project structure for the web directory

Run the build automation

Now, build the projects in the following order:

  1. dt-ejb
  2. Rest
  3. web
  4. daytrader-ee6

To build the projects:

  1. Open a command prompt.
  2. Change to the project’s root folder.
  3. Enter the mvn clean install command.

As you run the build, you see messages in the console. To confirm that a build was successful, you see the BUILD SUCCESS message in the console after you run the build automation on a project.

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Open the EAR file in the daytrader-ee6\target build directory. It has the following structure.

Enterprise archive structure for the daytrader-ee6 directory

To build the projects in Eclipse:

  1. Open Eclipse, and create a new workspace named daytrader-mvn.
  2. Import the Maven projects into Eclipse:

    a. Select File -> Maven -> Existing Maven Projects. b. Browse to the folder that contains the Maven projects. c. Select the projects to import. d. Click Finish.

  3. Build the projects in the following order:

    a. dt-ejb b. Rest c. web d. daytrader-ee6

    To build the projects:

    a. Right-click the project. b. Select Run as -> Maven Build. c. In the Goals box, enter clean install, and click Run.

Continuous integration

Now that you have the build automation in place, you must address continuous integration of the monolith. The approach for adding continuous integration is three-fold:

  1. Add profiles.
  2. Add integration tests.
  3. Run integration tests.

To begin, you need to understand the approach.

Default lifecycle phases

Maven defines three lifecycles: clean, default, and site. These lifecycles define their own phases. (For a list of the lifecycles and their phases, see Lifecycles reference.) The functions of a phase depend on its plug-in bindings. Phases that do not have plug-in bindings are skipped. Maven defines plug-in bindings for the clean and site lifecycle, but it does not define the plug-in bindings for the default lifecycle. Instead, they are defined for every package, that is EAR, WAR, and EJB.

Default lifecycle phases

In this tutorial, we use the default lifecycle for build automation in the previous section. The packaging type is specified for every POM. We did not have to add any plug-ins. The plug-ins were added by Maven based on the packaging type. We just configured them, which was sufficient for build automation of the monolith.

For continuous integration, you must add plug-ins and associate the plug-in goals with the appropriate set of Maven default lifecycle phases to accommodate continuous integration.

Plug-in bindings

This section begins with the bindings for EAR packaging that were used in build automation. You then add bindings for continuous integration against a stand-alone Liberty server and for continuous integration against Liberty in a docker container.

Build automation of the enterprise archive

The following table lists the plug-in bindings and the order in which they are run when you enter the mvn package command in the daytrader-ee6 folder. These bindings were defined by Maven and added to the POM when you created the Maven project for the DayTrader application in the previous section. Appropriate bindings were also defined and included in the POM when you created the EJB and Web projects. These plug-in bindings are used for build automation of the monolith. For more information, see Plug-in bindings for default lifecycle reference.

Table 5. Plug-in bindings for build automation of daytrader-ee6
Plug-in Plugin goal Maven phase Binding Id Description
Maven EAR Plug-in generate-application-xml generate-resources default-generate-application-xml You overwrite it with the monolith application.xml file in the default-install binding.
Maven Resources Plug-in resources process-resources default-resources It copies the generated deployment descriptor to the project build directory.
Maven EAR Plug-in ear package default-ear It copies the monolith application.xml file, the lib folder, and Java EE modules to the project build folder. It also builds the EAR file.
Maven Install Plug-in install install default-install It installs the EAR file in the local repository.
Maven Deploy Plug-in deploy deploy default-deploy It deploys the EAR file to the remote repository.

Continuous integration against stand-alone Liberty

The following table lists the plug-in bindings and the order in which they are run when you type the mvn deploy -Pci-liberty command in the daytrader-ee6 folder. These plug-in bindings add continuous integration against a stand-alone Liberty server.

Table 6. Plug-in bindings for continuous integration against stand-alone Liberty
Plug-in Plugin goal Maven phase Binding Id Description
Maven EAR Plug-in generate-application-xml generate-resources default-generate-application-xml It generates the application.xml file.
Maven Resources Plug-in resources process-resources default-resources It copies the generated XML deployment descriptor to the project build directory. You will overwrite this plug-in with the monolith application.xml file in the default-install binding.
Maven Compiler Plug-in testCompile test-compile test-compile It compiles the integration tests.
Liberty Maven Plug-in install-server prepare-package install-liberty It downloads the Liberty kernel from Maven central, extracts, and installs it.
Maven EAR Plug-in ear package default-ear It copies the monolith application.xml file, the lib folder, and the Java EE modules to the project build folder. It then builds the EAR file.
Maven Resources Plug-in copy-resources package copy-server-config It copies resources (the server.xml and Derby libraries) to the Liberty configuration.
Maven Resources Plug-in copy-resources package copy-app It copies the built EAR file to the Liberty drop-ins folder.
Liberty Maven Plug-in install-feature package liberty-install-feature It runs the Liberty installUtility command to add features to the kernel that this application requires.
Liberty Maven Plug-in start-server pre-integration-test liberty-start-server It starts the stand-alone Liberty server before it runs the integration tests.
Maven Failsafe Plug-in integration-test integration-test integration-test It runs the integration tests.
Liberty Maven Plug-in stop-server post-integration-test liberty-stop-server It stops the stand-alone Liberty server after it runs the tests and before it verifies the results. This plug-in ensures that the Liberty server is always stopped, even if the integration tests fail.
Maven Failsafe Plug-in verify verify verify-results It verifies that the integration tests passed. It stops the build if any of the integration tests fail.
Maven Install Plug-in install install default-install It installs the EAR file in the local repository.
Maven Deploy Plug-in deploy deploy default-deploy It deploys the EAR file to the remote repository.

Continuous integration against Liberty in Docker

The following table shows the plug-in goals and the order in which they are run for continuous integration against Liberty in a Docker container. They are the phases and associated plug-in bindings that are run when you enter the mvn package -Pci-docker command in the daytrader-ee6 folder.

Table 7. Plug-in bindings for continuous integration against Liberty in Docker
Plug-in Plug-in goal Maven phase Binding Id Description
Maven EAR Plug-in generate-application-xml generate-resources default-generate-application-xml It generates the application.xml file.
Maven Resources Plug-in resources process-resources default-resources It copies the generated XML deployment descriptor to the project build directory. You overwrite this plug-in with the monolith application.xml file in the default-install binding.
Maven Compiler Plug-in testCompile test-compile test-compile It compiles the integration tests.
Liberty Maven Plug-in install-server prepare-package install-liberty It downloads the Liberty kernel from Maven central, extracts it, and then installs it.
Maven EAR Plug-in ear package default-ear It copies the monolith application.xml file, the lib folder, and the Java EE modules to the project build folder. It also builds the EAR file.
Maven Resources Plug-in copy-resources package copy-server-config It copies the resources (the server.xml file and the Derby libraries) to the Liberty configuration.
Maven Resources Plug-in copy-resources package copy-app It copies the built EAR file to the Liberty dropins folder.
Maven Resources Plug-in copy-resources package copy-dockerfile It copies the dockerfile to the build directory of the project.
Docker Maven Plug-in build package docker-build It builds the Docker image. It looks for a dockerfile in the build directory of the project (target) and names the image by its artifactId and version (prefixed with dhvines/). It does not push the image to a Docker registry, although you can add that function.
Docker Maven Plug-in start pre-integration-test docker-start It runs the Liberty image (that we built) in a Docker container and opens ports 9080 and 9443.
Maven Failsafe Plug-in integration-test integration-test integration-test It runs the integration tests.
Docker Maven Plug-in stop post-integration-test docker-stop It stops the Docker container after it runs tests and before it verifies the results. This plug-in ensures that the container is always stopped, even if the integration tests fail.
Docker Maven Plug-in remove post-integration-test docker-remove It removes the stopped Docker container from the Docker machine.
Maven Failsafe Plug-in verify verify verify-results It verifies that the integration tests passed and stops the build if any of them failed.
Maven Install Plug-in install install default-install It installs the EAR file in the local repository.
Maven Deploy Plug-in deploy deploy default-package It deploys the EAR file to the remote repository.

Add profiles

To add support for continuous integration, add the profiles under the element. The following fragment shows the overall organization of the profiles element that you must add. You can see two profiles: ci-liberty and ci-docker. Inside each profile, you add the properties, dependencies, and plug-ins as detailed in the following sections.

  1. In Eclipse, open the pom.xml file of the daytrader-ee6 project, and switch to the source view.

  2. Enter the following text directly into the POM file immediately after the <name>daytrader-ee6</name> line:

    <profiles>
     <profile>
       <id>ci-liberty</id>       
       <!—
    See Add Properties in step 3
       ->
    <dependencies>
       <!—
    See Add Dependencies in step 4
       ->
     </dependencies>
       <build>
        <plugins>
           <!—
    See Add the Maven Compiler Plugin in step 5
    See Add the Maven Resources Plugins in step 6
    See Add the Maven Failsafe Plugin in step 7
    See Add the Liberty Maven Plugin in step 8
           ->
         </plugins>
       </build>
     </profile>
    <profile>
       <id>ci-docker</id>        
       <!—
    See Add Properties in step 3
        ->
     <dependencies>
       <!—
    See Add Dependencies in step 4
       ->
     </dependencies>
       <build>
         <plugins>
             <!—
    See Add the Maven Compiler Plugin in step 5
    See Add the Maven Resources Plugins in step 6
    See Add the Maven Failsafe Plugin in step 7
    See Add the Liberty Maven Plugin in step 8
    See Add the Docker Maven Plugin (Spotify) in step 9
    See Add the Docker Maven Plugin (Fabric8) in step 10
             ->
         </plugins>
       </build>
     </profile>
    </profiles>
    
  3. Add the properties:

    a. Add the properties to ci-liberty:

     <properties>
         <wlpServerName>Daytrader3Sample</wlpServerName>
         <daytraderAppRoute>http://localhost:9080</daytraderAppRoute>
     </properties>
    

    b. Add the properties to ci-docker:

     <properties>
         <wlpServerName>Daytrader3Sample</wlpServerName>
         <daytraderAppRoute>http://192.168.254.9:9080</daytraderAppRoute>
     </properties>
    

    Host change: You might need to change the host on the daytraderAppRoute for your Docker environment.

  4. Add dependencies to ci-liberty and ci-docker: Dependency scopes: Throughout this article series, we add dependencies to replicate the class loading in the monolith. Therefore, a brief description of the scopes is warranted. Some of Maven’s more common scopes are:

    • Compile: Available in all class paths and packaged with the application
    • Provided: Similar to compile, but not packaged with the application
    • Test: Available for test compilation and test execution only
    • Runtime: Available for execution only, but not compilation
       <!--  test dependencies -->
    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
       <scope>test</scope>
    </dependency>
    <dependency>
       <groupId>org.glassfish.jersey.core</groupId>
       <artifactId>jersey-client</artifactId>
       <version>2.22.2</version>
       <scope>test</scope>
    </dependency>
    
  5. Add the Maven Compiler Plugin to ci-liberty and ci-docker:

    <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <version>2.3.2</version>
       <configuration>
             <source>1.8</source>
             <target>1.8</target>
       </configuration>
      <executions>
        <execution>
          <phase>test-compile</phase>
                <goals> <goal>testCompile</goal> </goals>
             </execution>
      </executions>
    </plugin>
    
  1. Add the Maven Resources Plugin:

    a. Add the Maven Resources Plugin to ci-liberty:

    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-resources-plugin</artifactId>
     <version>2.7</version>
     <executions>
       <execution>
         <id>copy-server-config</id>
         <phase>package</phase>
         <goals> <goal>copy-resources</goal> </goals>
         <configuration>
           <outputDirectory>
             ${project.build.directory}/wlp/usr/servers/${wlpServerName}
           </outputDirectory>
           <resources>
             <resource>
             <directory>${project.basedir}/src/main/liberty/config</directory>
             <includes> <include>**/*</include> </includes>
             </resource>
           </resources>
         </configuration>
     </execution>
     <execution>
       <id>copy-app</id>
       <phase>package</phase>
       <goals> <goal>copy-resources</goal> </goals>
       <configuration>
         <outputDirectory>
           ${project.build.directory}/wlp/usr/servers/${wlpServerName}/dropins
         </outputDirectory>
         <resources>
           <resource>
             <directory>${project.build.directory}</directory>
             <includes>
               <include>${project.artifactId}-${project.version}.ear</include>
             </includes>
           </resource>
         </resources>
       </configuration>
     </execution>
    </executions>
    </plugin>
    

    b. Add the Maven Resources Plugin to ci-docker:

    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-resources-plugin</artifactId>
     <version>2.7</version>
     <executions>
       <execution>
         <id>copy-server-config</id>
         <phase>package</phase>
         <goals> <goal>copy-resources</goal> </goals>
         <configuration>
           <outputDirectory>
             ${project.build.directory}/wlp/usr/servers/${wlpServerName}
           </outputDirectory>
           <resources>
             <resource>
               <directory>${project.basedir}/src/main/liberty/config</directory>
               <includes> <include>**/*</include> </includes>
               </resource>
             </resources>
           </configuration>
         </execution>
         <execution>
         <id>copy-app</id>
         <phase>package</phase>
         <goals> <goal>copy-resources</goal> </goals>
         <configuration>
           <outputDirectory>
             ${project.build.directory}/wlp/usr/servers/${wlpServerName}/dropins
           </outputDirectory>
           <resources>
             <resource>
               <directory>${project.build.directory}</directory>
               <includes>
                 <include>${project.artifactId}-${project.version}.ear</include>
               </includes>
            </resource>
           </resources>
        </configuration>
       </execution>
       <execution>
         <id>copy-dockerfile</id>
         <phase>package</phase>
         <goals> <goal>copy-resources</goal> </goals>
         <configuration>
           <outputDirectory>${project.build.directory}</outputDirectory>
           <resources>
             <resource>
               <directory>${project.basedir}/src/main/resources/config</directory>
               <includes> <include>Dockerfile</include> </includes>
             </resource>
           </resources>
         </configuration>
       </execution>
     </executions>
    </plugin>
    
    1. Add the Maven Failsafe Plugin to ci-liberty and ci-docker:
    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-failsafe-plugin</artifactId>
     <version>2.18.1</version>
     <executions>
       <execution>
         <phase>integration-test</phase>
         <goals> <goal>integration-test</goal> </goals>
         <configuration>
           <systemPropertyVariables>
             <daytrader.app.route>${daytraderAppRoute}</daytrader.app.route>
           </systemPropertyVariables>
           <testSourceDirectory>src/test/java</testSourceDirectory>
         </configuration>
       </execution>
       <execution>
         <id>verify-results</id>
         <goals> <goal>verify</goal> </goals>
       </execution>
     </executions>
    </plugin>
    
  2. Add the Liberty Maven Plugin to ci-liberty:

    <plugin>
     <groupId>net.wasdev.wlp.maven.plugins</groupId>
     <artifactId>liberty-maven-plugin</artifactId>
     <version>2.0</version>
     <extensions>true</extensions>
     <configuration>
       <serverName>${wlpServerName}</serverName>
       <assemblyArtifact>
         <groupId>com.ibm.websphere.appserver.runtime</groupId>
         <artifactId>wlp-kernel</artifactId>
         <version>17.0.0.2</version>
         <type>zip</type>
       </assemblyArtifact>
       <assemblyInstallDirectory>
         ${project.build.directory}
       </assemblyInstallDirectory>
     </configuration>
     <executions>
       <execution>
         <id>install-liberty</id>
         <phase>prepare-package</phase>
         <goals> <goal>install-server</goal> </goals>
       </execution>
       <execution>
         <id>package-app</id>
         <phase>package</phase>
         <goals> <goal>package-server</goal> </goals>
       </execution>
       <execution>
         <id>install-feature</id>
         <phase>package</phase>
         <goals> <goal>install-feature</goal> </goals>
         <configuration>
           <features>
             <acceptLicense>true</acceptLicense>
             <feature>jsf-2.0</feature>
             <feature>localconnector-1.0</feature>
             <feature>wasjmsserver-1.0</feature>
             <feature>jaxrs-1.1</feature>
             <feature>ejblite-3.1</feature>
             <feature>jpa-2.0</feature>
             <feature>jmsmdb-3.1</feature>
             <feature>wasjmsclient-1.1</feature>
           </features>
         </configuration>
       </execution>
       <execution>
         <id>start-server</id>
         <phase>pre-integration-test</phase>
         <goals> <goal>start-server</goal> </goals>
       </execution>
       <execution>
         <id>stop-server</id>
         <phase>post-integration-test</phase>
         <goals> <goal>stop-server</goal> </goals>
       </execution>
         </executions>
    </plugin>
    
    1. Add the Docker Maven Plugin (Spotify) to ci-docker:
    <plugin>
     <groupId>com.spotify</groupId>
     <artifactId>docker-maven-plugin</artifactId>
     <version>0.2.3</version>
     <executions>
       <execution>
         <phase>package</phase>
         <goals> <goal>build</goal> </goals>
       </execution>
     </executions>
     <configuration>
       <dockerDirectory>${project.build.directory}</dockerDirectory>
       <imageName>dhvines/${project.artifactId}:${project.version}</imageName>
     </configuration>
    </plugin>
    

    You can use other plug-ins to build the image, but this one reads the dockerfile from the file system instead of you configuring the plug-in to create the dockerfile. This approach made the plug-in easier to use and configure.

  3. Add the Docker Maven Plugin (Fabric8) to ci-docker:

    <plugin>
    <groupId>io.fabric8</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <version>0.20.1</version>
    <executions>
      <execution>
        <id>start</id>
        <phase>pre-integration-test</phase>
        <goals> <goal>start</goal> </goals>
        <configuration>
          <images>
            <image>
              <name>dhvines/${project.artifactId}:${project.version}</name>
              <alias>${project.artifactId}</alias>
              <run>
                <ports> <port>9080:9080</port><port>9443:9443</port> </ports>
                <wait>
                  <log>
                    (?s)The server defaultServer is ready to run a smarter planet.
                  </log>
                  <time>60000</time>
                </wait>
              </run>
            </image>
          </images>
        </configuration>
      </execution>
      <execution>
        <id>stop</id>
        <phase>post-integration-test</phase>
        <goals> <goal>stop</goal> <goal>remove</goal> </goals>
      </execution>
    </executions>
    </plugin>
    

Create the integration tests

You will now create some simple integration tests. These examples issue an HTTP request, receive the response, and compare the actual response with the expected response. If they are equal, the test passes. Otherwise, it fails.

In the Eclipse workspace, create the following folders in the daytrader-ee6/sec/test/java/com/ibm/websphere/samples path:

  • /daytrader
  • /daytrader/web
  • /daytrader/rest

Create integration tests for the daytrader folder

In the daytrader-ee6/sec/test/java/com/ibm/websphere/samples/daytrader/web folder, create a new class named DaytraderIT.java. Copy the following code, and save the file. It contains a single integration test against the DayTrader web application.

package com.ibm.websphere.samples.daytrader.web;

import org.junit.Test;
import static org.junit.Assert.assertTrue;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;

public class DaytraderIT {

    @Test
    public void testDeployment() {
        testEndpoint("/daytrader/index.jsp", "<h1>Hello World!</h1>");
    }

    private void testEndpoint(String endpoint, String expectedOutput) {
        String route = System.getProperty("daytrader.app.route");

        Response response = sendRequest(route + endpoint, "GET");
        int responseCode = response.getStatus();
        assertTrue("Incorrect response code: " + responseCode,responseCode == 200);

        String responseString = response.readEntity(String.class);
        response.close();
        assertTrue("Incorrect response, response is " + responseString,
                    responseString.contains(expectedOutput));
    }

    private Response sendRequest(String url, String requestType) {
        Client client = ClientBuilder.newClient();
        WebTarget target = client.target(url);
        Invocation.Builder invoBuild = target.request();
        Response response = invoBuild.build(requestType).invoke();
        return response;
    }

Create integration tests for the rest folder

In the daytrader-ee6/sec/test/java/com/ibm/websphere/samples/daytrader/rest folder, create a new class named RestIT.java. Copy the following code, and save the file. It contains a single integration test against the rest web application.

package com.ibm.websphere.samples.daytrader.rest;

import org.junit.Test;
import static org.junit.Assert.assertTrue;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;

public class RestIT {

    @Test
    public void testDeployment() {
        testEndpoint("/rest/index.jsp", "<h1>Hello World!</h1>");
    }

    private void testEndpoint(String endpoint, String expectedOutput) {
        String route = System.getProperty("daytrader.app.route");

        Response response = sendRequest(route + endpoint, "GET");
        int responseCode = response.getStatus();
        assertTrue("Incorrect response code: " + responseCode,responseCode == 200);

        String responseString = response.readEntity(String.class);
        response.close();
        assertTrue("Incorrect response, response is " + responseString,
                    responseString.contains(expectedOutput));
    }

    private Response sendRequest(String url, String requestType) {
        Client client = ClientBuilder.newClient();
        WebTarget target = client.target(url);
        Invocation.Builder invoBuild = target.request();
        Response response = invoBuild.build(requestType).invoke();
        return response;
    }

Run the integration tests

The Maven Failsafe Plug-in runs the integration tests. It looks for classes in the src/test/java folder that follow the naming convention **IT.class. It starts all methods (with the @Test annotation) in those classes during the Maven verify phase.

First, build all projects, and then verify that the build was successful. Build the modules that make up the monolith in this order:

  1. dt-ejb
  2. Rest
  3. web
  4. daytrader-ee6

You can build the modules from the command line or from Eclipse:

  • To build them from the command line:

    1. Open a command prompt.
    2. Change to the root folder of the project.
    3. Enter the mvn clean install command.
  • To build them from Eclipse:

    1. Right-click the project.
    2. Select Run as -> Maven Build.
    3. In the Goals box, enter clean install, and click Run.

To verify that the build was a success, look for the BUILD SUCCESS message in the console for every build.

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

To run the integration tests, you have the following options:

  • Run the integration tests against the stand-alone Liberty server. In this case, go to the daytrader-ee6 folder (or right-click that project in Eclipse), and run the mvn verify -Pci-liberty command.
  • Run the integration tests against Liberty in a Docker container. In this case, go to the daytrader-ee6 folder (or right-click that project in Eclipse), and run the mvn verify -Pci-docker command.

To confirm that the integration tests were a success:

  1. In Navigator view, expand daytrader-ee6 -> target -> failsafe-reports.

    Integration test reports

  2. Double-click the indicated reports. You see test results as shown in the following example.

    Integration test results

  3. If any of the tests failed, go back and resolve the integration test issues.

You can find the completed workspace for Part 3 on GitHub.

Conclusion

In this tutorial, you learned how to add build automation and continuous integration to the monolith. You used the default Maven lifecycle for build automation and defined a couple of Maven profiles for continuous integration. One of the profiles illustrated continuous integration against a stand-alone Liberty server, and the other profile illustrated continuous integration against Liberty in a Docker container. The result is a platform from which you can add DevOps to the monolith.