8 Building a Real Application with Maven

Many real world applications include modules that are targeted to be deployed on different runtime environments. For example, you may have a web application that uses data stored in a Coherence cache.

This chapter describes how to build such a web application.

Introducing the Maven Example Application

The example application that you build in this chapter displays a list of people, with their names and age, on a web page. It also allows you to add a new person. The details of the people are stored in a Coherence cache.

This application contains the following parts:

  • A Coherence GAR project, which contains a Person POJO (Plain Old Java Object), which you need to build into a Portable Object, a utility class to access the cache, and Coherence cache definitions

  • A Jakarta EE web application, which you need to build into a WAR, which contains a servlet and a deployment descriptor

  • A project to assemble the GAR and WAR into an EAR and deploy that EAR to WebLogic Server

In this example, you can see how to build a multi-module Maven project, with dependencies between modules, and how to assemble the application components into a deployable EAR file that contains the whole application.

The aim of this chapter is to show how to use Maven to build whole applications, not to demonstrate how to write web or Coherence applications, so the content of the example itself, in terms of the servlet and the Coherence code, is quite basic. For specific steps,, refer to Building Jakarta EE Projects for WebLogic Server with Maven and Building Oracle Coherence Projects with Maven.

About Multi-Module Maven Projects

Maven lets you create projects with multiple modules. Each module is in effect another Maven project. At the highest level, you have a POM file that tells Maven about the modules and lets you build the whole application with one Maven command.

Each of the modules are placed in a subdirectory of the root of the top-level project. In the example, the top-level project is called my-real-app and the three modules are my-real-app-gar, my-real-app-war, and my-real-app-ear. The Maven coordinates of the projects are shown in Table 8-1.

Table 8-1 Maven Coordinates and Packaging Types for Example Application

GroupId ArtifactId Version Packaging

org.mycompany

my-real-app

1.0-SNAPSHOT

pom

org.mycompany

my-real-app-gar

1.0-SNAPSHOT

gar

org.mycompany

my-real-app-war

1.0-SNAPSHOT

war

org.mycompany

my-real-app-ear

1.0-SNAPSHOT

ear

The following are the files that make up the application:



At the highest level, the top-level POM file points to the three modules:

  • The my-real-app-gar directory contains the Coherence GAR project. It contains its own POM, the Coherence configuration files, a POJO/POF class definition (Person.java) and a utility class that is needed to access the cache (CacheWrapper.java).

  • The my-real-app-war directory contains the web application. It contains its own POM, a servlet, and a deployment descriptor. This project depends on the my-real-app-gar project.

  • The my-real-app-ear directory contains the deployment descriptor for the EAR file and a POM file to build and deploy the EAR.

Building a Maven Project

To build the example project, you create the directory, and then create the GAR, WAR, and EAR projects.

While it is more natural to start with the top-level POM file, doing this will cause issues if you choose to use the archetypes to create the projects. As such, the top-level POM is created at the end, even though the individual project POM files depend on it.

This section includes the following topics:

Creating a Directory for the Projects

Create a directory to hold the projects, using the following command:

mkdir my-real-app
cd my-real-app

Note:

Throughout the rest of this chapter, paths shown are relative to this directory.

Creating the GAR Project

This section includes the following topics:

Creating the Initial GAR Project

You can create the GAR project either using an archetype, as described in Creating a Coherence Project from a Maven Archetype, or you can create the directories and files manually:

  • To use the archetype, run the following command:

    mvn archetype:generate
        -DarchetypeGroupId=com.oracle.coherence.archetype
        -DarchetypeArtifactId=gar-maven-archetype
        -DarchetypeVersion=14.1.2-0-0
        -DgroupId=org.mycompany
        -DartifactId=my-real-app-gar
        -Dversion=1.0-SNAPSHOT
    
  • To create the project manually, use the following commands to create the necessary directories:

    mkdir -p my-real-app-gar/src/main/resources/META-INF
    mkdir -p my-real-app-gar/src/main/java/org/mycompany
Creating or Modifying the POM File

If you use the archetype, you already have a POM file. Modify that file to match the following example. If you create the project manually, create the POM file (my-real-app-gar/pom.xml) with the following contents:

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <artifactId>my-real-app-gar</artifactId>
  <packaging>gar</packaging>

  <parent>
    <groupId>org.mycompany</groupId>
    <artifactId>my-real-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath>
  </parent>

  <dependencies>
    <dependency>
      <groupId>com.oracle.coherence</groupId>
      <artifactId>coherence</artifactId>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>com.oracle.coherence</groupId>
        <artifactId>gar-maven-plugin</artifactId>
        <extensions>true</extensions>
        <configuration>
          <generatePof>true</generatePof>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <forceCreation>true</forceCreation>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Examine the POM file to understand each part of the file:

  • The Maven coordinates groupId and version are inherited from the top-level POM and the POM only needs to specify the artifactId and packaging type. Notice that the packaging is gar because you use the Coherence GAR Maven plug-in to build this project into a Coherence GAR file.

    <artifactId>my-real-app-gar</artifactId>
    <packaging>gar</packaging>
  • The coordinates of the parent project are set. These coordinates point back to the top-level POM to allow for inheritance of configuration.

    <parent>
      <groupId>org.mycompany</groupId>
      <artifactId>my-real-app</artifactId>
      <version>1.0-SNAPSHOT</version>
      <relativePath>../pom.xml</relativePath>
    </parent>
  • The dependencies section identifies any dependencies that this project has. In this case, you depend only on the Coherence library, that is, com.oracle.coherence:coherence:14.1.2-0-0. Notice that the dependency declaration does not need to include the version because that was inherited from the top-level POM file's dependencyManagement section. The scope provided means that this library is just for compilation and does not need to be packaged in the artifact that you build (the GAR file) as it is already provided in the runtime environment.

      <dependencies>
        <dependency>
          <groupId>com.oracle.coherence</groupId>
          <artifactId>coherence</artifactId>
          <scope>provided</scope>
        </dependency>
      </dependencies>
    
  • The plugins section includes any plug-ins that you want to run during the build that are not automatically included or included plug-ins for which you need to specify additional configuration, like the maven-jar-plugin. In this case, you must set generatePof to true so that the plug-in looks for POJOs with POF annotations and generates the necessary artifacts. You also must tell the maven-jar-plugin to always regenerate the GAR file. Notice that the versions of the plug-ins are not provided because those are inherited from the top-level POM file's pluginManagement section.

    <plugins>
      <plugin>
        <groupId>com.oracle.coherence</groupId>
        <artifactId>gar-maven-plugin</artifactId>
        <extensions>true</extensions>
        <configuration>
          <generatePof>true</generatePof>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <forceCreation>true</forceCreation>
        </configuration>
      </plugin>
    </plugins>
Creating or Modifying the Coherence Configuration Files

There are three Coherence configuration files that you need in your GAR project. If you use the archetype, the files already exist, but you need to modify them to match the following examples. If you create the project manually, create these files in the following locations:

my-real-app-gar/src/main/resources/META-INF/pof-config.xml
my-real-app-gar/src/main/resources/META-INF/coherence-application.xml
my-real-app-gar/src/main/resources/META-INF/cache-config.xml
  • The following example shows the contents for the pof-config.xml file:

    <?xml version="1.0"?>
    <pof-config
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config"
            xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config coherence-pof-config.xsd">
      <user-type-list>
        <include>coherence-pof-config.xml</include>
        <user-type>
          <type-id>1001</type-id>
          <class-name>org.mycompany.Person</class-name>
        </user-type>
      </user-type-list>
    </pof-config>

    This file requires adding the Person type if you created it with the archetype.

  • The following example shows the contents for the coherence-application.xml file:

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <coherence-application xmlns="http://xmlns.oracle.com/weblogic/coherence-application">
      <cache-configuration-ref>META-INF/cache-config.xml</cache-configuration-ref>
      <pof-configuration-ref>META-INF/pof-config.xml</pof-configuration-ref>
    </coherence-application>
    

    This file requires little or no modification if you created it with the archetype.

  • The cache-config.xml file must be updated if you have used the archetype.

    In this file, create a cache named People, with a caching scheme named real-distributed-gar and a service name of RealDistributedCache, which uses the local backing scheme and is automatically started. If you are not familiar with these terms, see Building Oracle Coherence Projects with Maven. The following shows an example of the file:

    <?xml version="1.0"?>
    <cache-config
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
        xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd">
      <caching-scheme-mapping>
        <cache-mapping>
          <cache-name>People</cache-name>
          <scheme-name>real-distributed-gar</scheme-name>
        </cache-mapping>
      </caching-scheme-mapping>
    
      <caching-schemes>
        <distributed-scheme>
          <scheme-name>real-distributed-gar</scheme-name>
          <service-name>RealDistributedCache</service-name>
          <backing-map-scheme>
            <local-scheme/>
          </backing-map-scheme>
          <autostart>true</autostart>
        </distributed-scheme>
      </caching-schemes>
    </cache-config>
Creating the Portable Objects

Create the Person object, which will store information in the cache. Create a new Java class in the following location:

my-real-app-gar/src/main/java/org/mycompany/Person.java
The following is the content for this class:
package org.mycompany;

import com.tangosol.io.pof.annotation.Portable;
import com.tangosol.io.pof.annotation.PortableProperty;

@Portable
public class Person {
  @PortableProperty(0)
  public String name;

  @PortableProperty(1)
  public int age;

  public Person() {}

  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String getName() { return this.name; }
  public int getAge() { return this.age; }
}

This POJO tells Coherence what to do with the class. Because the focus of this chapter is on building applications with Maven, it does not go into the details of writing Coherence applications. For more information on Coherence, refer to Building Oracle Coherence Projects with Maven.

Creating a Wrapper Class to Access the Cache

Create a small wrapper class that you can use to access the cache. Create another Java class in this location:

my-real-app-gar/src/main/java/org/mycompany/CacheWrapper.java
The following is the content for this class:
package org.mycompany;

import java.util.Map;
import java.util.Set;

import org.mycompany.Person;
import com.tangosol.net.CacheFactory;

public class CacheWrapper {
  private static CacheWrapper INSTANCE = new CacheWrapper();
  public static CacheWrapper getInstance() {
    return INSTANCE;
  }

  public Set<Map.Entry<Object, Object>> getPeople() {
    return CacheFactory.getCache("People").entrySet();
  }

  public void addPerson(int personId, String name, int age) {
    CacheFactory.getCache("People").put(personId, new Person(name, age));
  }
}

Later, you can use this class in a servlet to get data from the cache and to add new data to the cache.

Creating the WAR Project

This section includes the following topics:

Creating the Initial WAR Project

You can create the WAR project either using an archetype as described in Building Jakarta EE Projects for WebLogic Server with Maven, or you can create the directories and files manually.

  • To use the archetype, run the following command:

    mvn archetype:generate
      -DarchetypeGroupId=com.oracle.weblogic.archetype
      -DarchetypeArtifactId=basic-webapp
      -DarchetypeVersion=14.1.2-0-0
      -DgroupId=org.mycompany
      -DartifactId=my-real-app-war
      -Dversion=1.0-SNAPSHOT

    If you use the archetype, then you must remove any unnecessary files included in the project. For example, the generated AccountBean.java file needs to be removed because the updated POM file will not include all of the dependencies needed to compile it.

  • To create the project manually, use the following commands to create the necessary directories:

    mkdir -p my-real-app-war/src/main/webapp/WEB-INF
    mkdir -p my-real-app-war/src/main/java/org/mycompany/servlets
Creating or Modifying the POM File

If you use the archetype, the POM file already exists. Modify that file to match the following example. If you created the project manually, then create the POM file (my-real-app-war/pom.xml) with the following contents:

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <artifactId>my-real-app-war</artifactId>
  <packaging>war</packaging>

  <parent>
    <groupId>org.mycompany</groupId>
    <artifactId>my-real-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath>
  </parent>

  <dependencies>
    <dependency>
      <groupId>${project.groupId}</groupId>
      <artifactId>my-real-app-gar</artifactId>
      <version>${project.version}</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>jakarta.servlet</groupId>
      <artifactId>jakarta.servlet-api</artifactId>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

Examine the POM file to understand each part of the file:

  • As you saw in the GAR project, you only need to set the artifactId and packaging type coordinates for this project because the groupId and version are inherited from the top-level POM file. Notice that the packaging for this project is war.

    <artifactId>my-real-app-war</artifactId>
    <packaging>war</packaging>
  • Define the parent, as you did in the GAR project:

    <parent>
      <groupId>org.mycompany</groupId>
      <artifactId>my-real-app</artifactId>
      <version>1.0-SNAPSHOT</version>
      <relativePath>../pom.xml</relativePath>
    </parent>
  • List the dependencies for this project. In this case, there are two dependencies: the GAR project to access the POJO and utility classes you defined there, and the Servlet API. Because the GAR project is built as part of this example application, you use the ${project.groupId} and ${project.version} built-in property references to specify the groupId and version of this dependency.

    <dependencies>
      <dependency>
        <groupId>${project.groupId}</groupId>
        <artifactId>my-real-app-gar</artifactId>
        <version>${project.version}</version>
        <scope>provided</scope>
      </dependency>
      <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <scope>provided</scope>
      </dependency>
    </dependencies>
Creating the Deployment Descriptor

The web application has a simple Jakarta EE deployment descriptor that sets the display-name for the web application. It resides at the following location:

my-real-app-war/src/main/webapp/WEB-INF/web.xml

The following are the contents of this file:

<web-app version="3.1"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
  <display-name>my-real-app-war</display-name>
</web-app>
Creating the Servlet

To create the servlet, locate the MyServlet.java file:

my-real-app-war/src/main/java/org/mycompany/servlets/MyServlet.java

The servlet displays a list of people that are currently in the cache and allows you to add a new person to the cache. The aim of the section is to learn how to build these types of applications with Maven, not to learn how to write Jakarta EE web applications, hence the use of a simplistic servlet.

The following is the content for the servlet class:
package org.mycompany.servlets;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;

import org.mycompany.Person;
import org.mycompany.CacheWrapper;

@WebServlet(name = "MyServlet", urlPatterns = "MyServlet")
public class MyServlet extends HttpServlet {
  protected void doPost(HttpServletRequest request,
                        HttpServletResponse response)
    throws ServletException, IOException {
    String id = request.getParameter("id");
    String name = request.getParameter("name");
    String age = request.getParameter("age");
    if (name == null || name.isEmpty() ||
        age == null || age.isEmpty() ||
        id == null || id.isEmpty()) {
      // no need to add a new entry
    } else {
      // we have a new entry - so add it
      CacheWrapper.getInstance().addPerson(Integer.parseInt(id),
        name, Integer.parseInt(age));
    }
    renderPage(request, response);
  }

  protected void doGet(HttpServletRequest request,
                       HttpServletResponse response)
    throws ServletException, IOException {
    renderPage(request, response);
  }

  private void renderPage(HttpServletRequest request,
                          HttpServletResponse response)
    throws ServletException, IOException {
    // get the data
    Set<Map.Entry<Object, Object>> people =
      CacheWrapper.getInstance().getPeople();

    PrintWriter out = response.getWriter();
    out.write("<html><head><title>MyServlet" +
              "</title></head><body>");
    out.write("<h2>Add a new person</h2>");
    out.write("<form name=\"myform\" method=\"POST\">");
    out.write("ID:<input type=\"text\" name=\"id\"/><br/>");
    out.write("Name:<input type=\"text\" name=\"name\"/><br/>");
    out.write("Age:<input type=\"text\" name=\"age\"/><br/>");
    out.write("<input type=\"submit\" name=\"submit\" " +
              "value=\"add\"/>");
    out.write("</form>");
    out.write("<h2>People in the cache now</h2>");
    out.write("<table><tr><th>ID</th><th>Name" +
              "</th><th>Age</th></tr>");
    // for each person in data
    if (people != null) {
      for (Map.Entry<Object, Object> entry : people) {
        out.write("<tr><td>" +
          entry.getKey() +
          "</td><td>" +
          ((Person) entry.getValue()).getName() +
          "</td><td>" +
          ((Person) entry.getValue()).getAge() +
          "</td></tr>");
      }
    }
    out.write("</table></body></html>");
  }
}

Check if the user has entered any data in the form. If so, then add a new person to the cache using that data. Note that this application has fairly minimal error handling. To add the new person to the cache, use the addPerson() method in the CacheWrapper class that you created in your GAR project.

Print out the contents of the cache in a table. In this example, assume that the cache has a reasonably small number of entries, and read them all using the getPeople() method in the CacheWrapper class.

Creating the EAR Project

The EAR project manages assembling the WAR and the GAR into an EAR.

This section includes the following topics:

Creating the Initial EAR Project

Create the EAR project manually using the following command:

mkdir -p my-real-app-ear/src/main/application/META-INF

There are two files in this project: a POM file and a deployment descriptor:

my-real-app-ear/pom.xml
my-real-app-ear/src/main/application/META-INF/weblogic-application.xml
About the POM File for the Example Application
The following are the contents of the POM file:
<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <artifactId>my-real-app-ear</artifactId>
  <packaging>ear</packaging>

  <parent>
    <groupId>org.mycompany</groupId>
    <artifactId>my-real-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath>
  </parent>

  <dependencies>
    <dependency>
      <groupId>${project.groupId}</groupId>
      <artifactId>my-real-app-gar</artifactId>
      <version>${project.version}</version>
      <type>gar</type>
    </dependency>
    <dependency>
      <groupId>${project.groupId}</groupId>
      <artifactId>my-real-app-war</artifactId>
      <version>${project.version}</version>
      <type>war</type>
    </dependency>
  </dependencies>

  <build>
    <finalName>my-real-app-ear</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <execution>
            <id>copy-gar-locally</id>
            <phase>prepare-package</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <artifactItems>
                <artifactItem>
                  <groupId>${project.groupId}</groupId>
                  <artifactId>my-real-app-gar</artifactId>
                  <version>${project.version}</version>
                  <type>gar</type>
                </artifactItem>
              </artifactItems>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-ear-plugin</artifactId>
        <configuration>
          <outputFileNameMapping>@{artifactId}@-@{version}@.@{extension}@</outputFileNameMapping>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
            </manifest>
          </archive>
          <artifactTypeMappings>
            <artifactTypeMapping type="gar" mapping="jar"/>
          </artifactTypeMappings>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <profiles>
    <profile>
      <id>integration-test</id>
      <activation>
        <property>
          <name>skipITs</name>
          <value>false</value>
        </property>
      </activation>
      <build>
        <plugins>
          <plugin>
            <groupId>com.oracle.weblogic</groupId>
            <artifactId>weblogic-maven-plugin</artifactId>
            <executions>
              <!--Deploy the application to the server-->
              <execution>
                <id>deploy for integration testing</id>
                <phase>pre-integration-test</phase>
                <goals>
                  <goal>deploy</goal>
                </goals>
                <configuration>
                  <adminurl>${wls.admin.url}</adminurl>
                  <user>${wls.admin.user}</user>
                  <password>${wls.admin.pass}</password>
                  <!--The location of the file or directory to be deployed-->
                  <source>${project.build.directory}/${project.build.finalName}.${project.packaging}</source>
                  <!--The target servers where the application is deployed-->
                  <targets>${wls.ear.targets}</targets>
                  <verbose>true</verbose>
                  <name>${project.build.finalName}</name>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
</project>

Examine the POM file to understand each part of the file:

  • As in the previous projects, you only need to specify the artifactId, packaging type, and the parent:

    <artifactId>my-real-app-ear</artifactId>
    <packaging>ear</packaging>
    
    <parent>
      <groupId>org.mycompany</groupId>
      <artifactId>my-real-app</artifactId>
      <version>1.0-SNAPSHOT</version>
      <relativePath>../pom.xml</relativePath>
    </parent>
  • The dependencies on the WAR and GAR projects are listed:

    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>my-real-app-gar</artifactId>
            <version>${project.version}</version>
            <type>gar</type>
        </dependency>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>my-real-app-war</artifactId>
            <version>${project.version}</version>
            <type>war</type>
        </dependency>
    </dependencies>
  • The first plug-in configuration is for the maven-dependency-plugin. Configure it to copy the GAR file from the my-real-app-gar project's output (target) directory into the EAR project:

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <executions>
        <execution>
          <id>copy-gar-locally</id>
          <phase>prepare-package</phase>
          <goals>
            <goal>copy</goal>
          </goals>
          <configuration>
            <artifactItems>
              <artifactItem>
                <groupId>${project.groupId}</groupId>
                <artifactId>my-real-app-gar</artifactId>
                <version>${project.version}</version>
                <type>gar</type>
              </artifactItem>
            </artifactItems>
          </configuration>
        </execution>
      </executions>
    </plugin>
  • The second plug-in configuration is for the maven-ear-plugin. You need to tell it to treat a GAR file like a JAR file by adding an artifactTypeMapping, as in the following example:

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-ear-plugin</artifactId>
      <configuration>
        <archive>
          <manifest>
            <addClasspath>true</addClasspath>
          </manifest>
        </archive>
        <artifactTypeMappings>
          <artifactTypeMapping type="gar" mapping="jar"/>
        </artifactTypeMappings>
      </configuration>
    </plugin>
  • The profile section adds a third plug-in configuration for the weblogic-maven-plugin that tells it how to deploy the resulting EAR file. Notice that the adminurl, user, password, and targets parameters' values are set to properties that will be defined in the top-level POM file.

    The profile is activated by adding -DskipITs=false to the Maven command line. This makes it easier to build the EAR file without needing to deploy it on every build.

    <profile>
      <id>integration-test</id>
      <activation>
        <property>
          <name>skipITs</name>
          <value>false</value>
        </property>
      </activation>
      <build>
        <plugins>
          <plugin>
            <groupId>com.oracle.weblogic</groupId>
            <artifactId>weblogic-maven-plugin</artifactId>
            <executions>
              <!--Deploy the application to the server-->
              <execution>
                <id>deploy for integration testing</id>
                <phase>pre-integration-test</phase>
                <goals>
                  <goal>deploy</goal>
                </goals>
                <configuration>
                  <adminurl>${wls.admin.url}</adminurl>
                  <user>${wls.admin.user}</user>
                  <password>${wls.admin.pass}</password>
                  <!--The location of the file or directory to be deployed-->
                  <source>${project.build.directory}/${project.build.finalName}.${project.packaging}</source>
                  <!--The target servers where the application is deployed-->
                  <targets>${wls.ear.targets}</targets>
                  <verbose>true</verbose>
                  <name>${project.build.finalName}</name>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>

After you have completed the POM project, add a deployment descriptor.

About the Deployment Descriptor for the Example Application

The WebLogic deployment descriptor for the EAR file is located in this file:

my-real-app-ear/src/main/application/META-INF/weblogic-application.xml

The following are the contents:

<weblogic-application>
       <module>
             <name>GAR</name>
             <type>GAR</type>
             <path>my-real-app-gar-1.0-SNAPSHOT.gar</path>
       </module>
</weblogic-application>

This deployment descriptor provides the details for where in the EAR file, the GAR file should be placed, and what it should be called.

Creating the Top-Level POM

Create the top-level POM. This is located in the pom.xml file in the root directory of your application and contains the following:

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.mycompany</groupId>
  <artifactId>my-real-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>

    <fmw.version>14.1.2-0-0</fmw.version>
    <coherence.version>${fmw.version}</coherence.version>
    <weblogic.version>${fmw.version}</weblogic.version>

    <wls.admin.url>t3://localhost:7001</wls.admin.url>
    <wls.admin.user>weblogic</wls.admin.user>
    <wls.admin.pass>password</wls.admin.pass>
    <wls.ear.targets>AdminServer</wls.ear.targets>
  </properties>

  <modules>
    <module>my-real-app-gar</module>
    <module>my-real-app-war</module>
    <module>my-real-app-ear</module>
  </modules>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.oracle.coherence</groupId>
        <artifactId>coherence</artifactId>
        <version>${coherence.version}</version>
      </dependency>
      <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <version>4.0.4</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>com.oracle.coherence</groupId>
          <artifactId>gar-maven-plugin</artifactId>
          <version>${coherence.version}</version>
        </plugin>
        <plugin>
          <groupId>com.oracle.weblogic</groupId>
          <artifactId>weblogic-maven-plugin</artifactId>
          <version>${weblogic.version}</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-dependency-plugin</artifactId>
          <version>3.8.1</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-ear-plugin</artifactId>
          <version>3.3.0</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-enforcer-plugin</artifactId>
          <version>3.5.0</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.4.2</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.4.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
  
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <executions>
          <execution>
            <id>enforce-java-version</id>
            <goals>
              <goal>enforce</goal>
            </goals>
            <configuration>
              <rules>
                <requireJavaVersion>
                  <version>[17,18),[21,22)</version>
                  <message>You must use JDK 17 or JDK 21 to build the project</message>
                </requireJavaVersion>
              </rules>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Set the coordinates for the project. These match the parent coordinates that you specified in each of the three projects. Note that the packaging is pom. This tells Maven that this project itself does not create an artifact. In this example, it simply organizes the build for a set of modules, as named in the modules section. There is one module entry for each of the three subprojects.

Because this POM is the parent of the other three, and because POM's inherit from their parents, you can add any common properties to this POM and they will be available in all the three subprojects. In this case, you are adding three sections:
  • properties section: This allows you to define variables that can be used in most locations in the current POM file and any module POM files. The first three variables are ones used to configure the maven-compiler-plugin. You could have declared the maven-compiler-plugin as a normal plug-in and added configuration to accomplish the same thing, but using these properties makes for less configuration—a Maven best practice.

    Next, you have defined the fmw.version property to hold the version of Oracle Fusion Middleware that you will use. For convenience, you also define the coherence.version and weblogic.version properties and set their values to the value of the fmw.version property.

    Finally, you have defined properties that specify the WebLogic Server URL, credentials, and location to which to target the application for testing.

  • dependencyManagement section: This section allows you to centrally manage the versions of dependencies used by the project and all of its modules. In this case, the project only has two external dependencies that are needed for compilation but already provided at runtime: the core Coherence dependency and the Jakarta Servlet API.
  • pluginManagement section: This section allows you to centrally manage the versions of the plug-ins used by the project and all of its modules. In this example, these include the following plug-ins:
    • gar-maven-plugin: Assembles the Coherence GAR file
    • weblogic-maven-plugin: Deploys the EAR file to WebLogic Server for integration testing
    • maven-dependency-plugin: Copies the Coherence GAR file so that it can be included in the EAR file
    • maven-enforcer-plugin: Ensures that the build uses a compatible Java version
    • maven-ear-plugin: Assembles the Jakarta EE EAR file
    • maven-jar-plugin: Participates in assembling the Coherence GAR file
    • maven-war-plugin: Assembles the Jakarta EE WAR file
  • plugin section: This section configures the Maven Enforcer Plugin to make sure that the build is run with either JDK 17 or JDK 21.

Building the Application Using Maven

You are now ready to build and deploy the application. Before running Maven to build and deploy the application, there are a few things you may need to know. The following sections will cover these topics:

What You May Need to Know About Coherence Networking
By default, Coherence tries to select the appropriate network interface to use and tries to form its cluster using IP multicast. On some machines, Coherence may select the wrong interface or Coherence may decide that the selected interface does not support IP multicast. This can cause the WebLogic Maven Plugin deploy command to hang until it times out. Once deployed, the server may also print messages like the following:
2024-11-06 18:48:27.677/52.215 Oracle Coherence GE 14.1.2.0.0 <Warning>
      (thread=Cluster, member=n/a): Delaying formation of a new cluster; multicast networking
      appears to be inoperable on interface 192.168.1.179 as this process isn't receiving even its
      own transmissions; consider forcing IPv4 via -Djava.net.preferIPv4Stack=true
Should you run into such issues, the easiest solution for this simple example it to set Java System Property coherence.wka to 127.0.0.1 when starting WebLogic Server. This will cause Coherence to use unicast instead of multicast and use the network interface associated with 127.0.0.1. To pass this system property to WebLogic Server, simply set the JAVA_OPTIONS environment variable prior to running the startWebLogic script. On macOS or Linux, do the following:
export JAVA_OPTIONS="-Dcoherence.wka=127.0.0.1"
On Windows, do the following:
set "JAVA_OPTIONS=-Dcoherence.wka=127.0.0.1"
What You May Need to Know About Maven Dependency Resolution
When building Maven projects, there are two ways that Maven uses to resolve dependencies:
  • Fetching dependencies from a Maven repository
  • Passing dependencies between modules in a multi-module build

To fetch a dependency from a Maven repository, the dependency must exist in either a remote repository (for example, Maven Central) or in the local Maven repository. Any dependency that isn't available in the local Maven repository is fetched from the remote repository and added to the local Maven repository. To put build artifacts in the local Maven repository, you typically run mvn install. All artifacts in the local repository can be fetched by Maven builds without any restrictions.

In this example, we have three modules—some of which have dependencies on other modules in the build. The my-real-app-war module depends on the my-real-app-gar module and the my-real-app-ear module depends on both the my-real-app-gar and my-real-app-war. If you try to build the my-real-app-war module by itself before building and installing the my-real-app-gar module artifacts in the local Maven repository, you will get an error like this:
[ERROR] Failed to execute goal on project my-real-app-war: Could not resolve
      dependencies for project org.mycompany:my-real-app-war:war:1.0-SNAPSHOT: The following
      artifacts could not be resolved: org.mycompany:my-real-app-gar:jar:1.0-SNAPSHOT (absent):
      Could not find artifact org.mycompany:my-real-app-gar:jar:1.0-SNAPSHOT -> [Help
      1]
There are two ways to resolve this issue:
  • Run mvn install in the my-real-app-gar module directory
  • Run the build with any target, such as mvn package, from the top-level my-real-app directory

Maven understands multi-module builds and creates an in-memory structure called the Maven Reactor that it uses to pass around information about the build to the various modules. Part of this information is about how to resolve cross-module dependencies that may not be present in the local Maven Repository. This allows Maven to build the my-real-app-war module using the reactor information about the my-real-app-gar module that is not in the local Maven repository.

Running Maven to Build and Deploy the Application
You can now build the application using Maven by using one or more of the following commands (in the top-level directory my-real-app):
mvn compile
mvn package
mvn verify
mvn install
mvn install -DskipITs=false

Maven runs all of the phases up to and including the one named. Table 8-2 shows the effects of each command.

Table 8-2 Effects of Maven Commands

Command Details

mvn compile

Compiles the Java source into target class files.

mvn package

  1. Compiles the Java source into target class files.

  2. Runs any unit tests you may have written.

  3. Creates the archive (WAR, GAR, and so on) containing compiled files and resources (deployment descriptors, configuration files, and so on).

mvn verify

  1. Compiles the Java source into target class files.

  2. Runs any unit tests you may have written.

  3. Creates the archive (WAR, GAR, and so on) containing compiled files and resources (deployment descriptors, configuration files, and so on).

  4. Deploys the EAR file to the WebLogic Server environment.Foot 1

  5. Runs any integration tests you may have written.

  6. Verifies the integration test results, if any.

mvn install

  1. Compiles the Java source into target class files.

  2. Runs any unit tests you may have written.

  3. Creates the archive (WAR, GAR, and so on) containing compiled files and resources (deployment descriptors, configuration files, and so on).

  4. Deploys the EAR file to the WebLogic Server environment.Foot 2

  5. Runs any integration tests you may have written.

  6. Verifies the integration test results, if any.

  7. Copies the artifact, if any, and the POM file to the local Maven repository.

Footnote 1 Deployment only happens if the -DskipITs=false argument is passed to Maven.

Footnote 2 Deployment only happens if the -DskipITs=false argument is passed to Maven.

Remember, the typical Maven build will skip deploying the application unless you add -DskipITs=false. This makes it easier to build the application without having to deploy each time. After the application is built and successfully deployed, it is time to test the application to ensure that it is working properly.

Accessing the Application

After the application is successfully deployed, simply use the /my-real-app-war/MyServlet URL context path to see the application; for example, http://127.0.0.1:7001/my-real-app-war/MyServlet.