Enterprise Java Development@TOPIC@

Chapter 49. EAR Deployment

49.1. Purpose
49.1.1. Goals
49.1.2. Objectives
49.2. Create EAR Module
49.3. Create RMI Test Module
49.4. Deploy the EAR
49.5. Lookup and Invoke @Remote Interface
49.6. EJB Client
49.7. Summary

EARs are Java archives that are used to house the overall application, with all of its components. The EAR can contain many EJB and WAR components as well as their dependencies (and a little-used Java EE Client type). The EAR is used to deploy the contained modules to the server. It is the original JavaEE deployment type aside from a naked EJB.

Naked EJB deployments are rare because they provide no construct to be deployed with dependencies. Everything required by the EJB must be a part of the EJB. The EAR, on the other hand, can deploy an EJB and any dependencies such that they are loaded by the same class loader. Since Naked EJB deployments are of limited use and present very few additional technical challenges -- we will not be addressing that type of deployment.

A single Maven module can house the development of a single EAR. The bulk of the project is solely within the pom.xml as nearly all of its contents are brought in through dependencies.

  1. Create the sub-project directory for the EAR.

    $ mkdir basicejb-ear
    
  2. Add the initial entries for the EAR pom.xml.

    
    <?xml version="1.0" encoding="UTF-8"?>
    <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">
        <parent>
            <groupId>myorg.basicejb</groupId>
            <artifactId>basicejbEx</artifactId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>

        <artifactId>basicejb-ear</artifactId>
        <packaging>ear</packaging>

        <name>Basic EJB Exercise::EAR</name>
        <description>
            This project provides a sample EAR for the Java EE components
            associated with the overall project.
        </description>

        <dependencies>
        </dependencies>
    </project>
  3. Add the EJB dependency to the EAR. Use exclusions to keep any unwanted 3rd party .jars from being brought along.

    
        <dependencies>
            <dependency>
                <groupId>${project.groupId}</groupId>
                <artifactId>basicejb-ejb</artifactId>
                <version>${project.version}</version>
                <type>ejb</type>
                <exclusions>
                    <!-- server doesn't want to see already provided jars -->
                    <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-api</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
  4. Verify the EAR builds.

    $ mvn clean verify
    ...
    [INFO] Building Basic EJB Exercise::EAR 1.0-SNAPSHOT
    ...
    [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ basicejb-ear ---
    ...
    [INFO] --- maven-ear-plugin:2.8:generate-application-xml (default-generate-application-xml) @ basicejb-ear ---
    [INFO] Generating application.xml
    ...
    [INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ basicejb-ear ---
    ...
    [INFO] --- maven-ear-plugin:2.8:ear (default-ear) @ basicejb-ear ---
    [INFO] Copying artifact [ejb:myorg.basicejb:basicejb-ejb:1.0-SNAPSHOT] to [basicejb-ejb-1.0-SNAPSHOT.jar]
    [INFO] Could not find manifest file: .../basicejbEx/basicejb-ear/target/basicejb-ear-1.0-SNAPSHOT/META-INF/MANIFEST.MF - Generating one
    [INFO] Building jar: .../basicejbEx/basicejb-ear/target/basicejb-ear-1.0-SNAPSHOT.ear
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    
  5. Inspect the generated EAR archive. Notice how the EJB we developed in the previous chapter and included as a dependency here was brought into the archive.

    $ jar tf target/basicejb-ear-1.0-SNAPSHOT.ear
    ...
    basicejb-ejb-1.0-SNAPSHOT.jar
    META-INF/application.xml
    ...
    
  6. Inspect the generated application.xml. There is a copy in target/application.xml without having to unzip the archive. Notice how the archive was registered within this required descriptor as an EJB. Without this designation the EJB will be not be recognized as an EJB module by the container when deployed.

    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE application PUBLIC
        "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN"
        "http://java.sun.com/dtd/application_1_3.dtd">
    <application>
      <display-name>basicejb-ear</display-name>
      <description>This project provides a sample EAR for the Java EE components
            associated with the overall project.</description>
      <module>
        <ejb>basicejb-ejb-1.0-SNAPSHOT.jar</ejb>
      </module>
    </application>
  7. Add the EAR to the *root* level module and verify everything builds from the root.

    
        <modules>
            <module>basicejb-ejb</module>
            <module>basicejb-ear</module>
        </modules>
    
    $ mvn clean install
    ...
    [INFO] ------------------------------------------------------------------------
    [INFO] Reactor Summary:
    [INFO] 
    [INFO] Basic EJB Exercise ................................. SUCCESS [  0.435 s]
    [INFO] Basic EJB Exercise::EJB ............................ SUCCESS [  3.063 s]
    [INFO] Basic EJB Exercise::EAR ............................ SUCCESS [  0.557 s]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
  8. This is what our project looks like so far.

    .
    |-- basicejb-ear
    |   `-- pom.xml
    |-- basicejb-ejb
    |   |-- pom.xml
    |   `-- src
    |       |-- main
    |       |   `-- java
    |       |       `-- org
    |       |           `-- myorg
    |       |               `-- basicejb
    |       |                   `-- ejb
    |       |                       |-- ReservationEJB.java
    |       |                       |-- ReservationLocal.java
    |       |                       `-- ReservationRemote.java
    |       `-- test
    |           |-- java
    |           |   `-- org
    |           |       `-- myorg
    |           |           `-- basicejb
    |           |               `-- ejb
    |           |                   `-- ReservationTest.java
    |           `-- resources
    |               `-- log4j.xml
    `-- pom.xml

Any tests we implement within the EJB module itself would likely be a POJO-level unit test. EJB 3.2 does provide a means to create a lightweight EJB container to be used as a test harness, but does not substitue for honest end-to-end testing using a server deployment of the EJB/EAR and external test clients. We will create an additional module to deploy the EAR, locate the server and EJB remote interface, and test the EJB through that interface. We can reuse tests from lower levels, but that will not be shown as a part of this exercise. This module will have no target artifact (i.e., artifactId.jar) that we care about. One could do some tweeking of the pom.xml to keep that from being generated, but I have found that to only confuse Eclipse so we'll just live with and empty, unused RMI Test.jar.

  1. Create the sub-project directory for the RMI Test.

    $ mkdir basicejb-test
    
  2. Create the pom.xml for the RMI Test module.

    
    <?xml version="1.0" encoding="UTF-8"?>
    <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">
        <parent>
            <artifactId>basicejbEx</artifactId>
            <groupId>myorg.basicejb</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>

        <artifactId>basicejb-test</artifactId>
        <packaging>jar</packaging>

        <name>Basic EJB Exercise::Remote Test</name>
        <description>
            This project provides an example RMI Test project.
        </description>

        <dependencies>

        </dependencies>

        <build>
            <plugins>

            </plugins>
        </build>
    </project>
  3. Add the dependencies to the Test/pom.xml required to use logging and JUnit.

    
            <!-- core dependencies -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <scope>test</scope>
            </dependency>

            <!-- test dependencies -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <scope>test</scope>
            </dependency>
  4. Add the dependencies required to be an RMI client of JBoss/Wildfly. This is not as clean or simple as it sounds. There are many APIs in JavaEE and many layered jars that implement them. If we use something that includes everything -- we get a dependency tree that is very bloated. I have attempted to cut the list down to a set we need for class and have included it in the following project.

    
            <!-- dependencies used for remote interface -->
            <dependency>
                <groupId>info.ejava.examples.common</groupId>
                <artifactId>jboss-rmi-client</artifactId>
                <type>pom</type>
                <scope>test</scope>
            </dependency>    
  5. Add a definition of the above dependency to your parent pom

    
        <properties>
            ...
            <ejava.version>4.0.0-SNAPSHOT</ejava.version>
    ...
            <dependency>
                <groupId>info.ejava.examples.common</groupId>
                <artifactId>jboss-rmi-client</artifactId>
                <version>${ejava.version}</version>
                <type>pom</type>
            </dependency>
  6. Attempt to resolve all dependencies at this point. If you don't yet have a copy of the info.ejava.examples.common#jboss-rmi-client in your repository this will fail. You can use the dependency:go-offline to make sure you have everything your project needs.

    (from basicejb-test directory)
    $ mvn dependency:go-offline
    ...
    [WARNING] The POM for info.ejava.examples.common:jboss-rmi-client:pom:4.0.0-SNAPSHOT is missing, no dependency information available
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD FAILURE
    ...
    [ERROR] Failed to execute goal on project basicejb-test: Could not resolve dependencies for project myorg.basicejb:basicejb-test:jar:1.0-SNAPSHOT: 
    Could not find artifact info.ejava.examples.common:jboss-rmi-client:pom:4.0.0-SNAPSHOT -> [Help 1]
    
  7. Add the repository information required to resolve info.ejava.examples.common#jboss-rmi-client and its dependencies from the Internet. We need one entry for the SNAPSHOT release. Place the following in the root module pom.xml.

    
        <repositories>
            <repository>
                <id>webdev-snapshot</id>
                <name>ejava webdev snapshot repository</name>
                <url>http://webdev.jhuep.com/~jcs/maven2-snapshot</url>
                <releases>
                    <enabled>false</enabled>
                </releases>
                <snapshots>
                    <enabled>true</enabled>
                    <updatePolicy>daily</updatePolicy>
                </snapshots>
            </repository>
        </repositories>
    (from basicejb-test directory)
    $ mvn dependency:go-offline
    
    ...
    [INFO] <<< maven-dependency-plugin:2.8:go-offline (default-cli) < :resolve-plugins @ basicejb-test <<<
    Downloading: http://webdev.jhuep.com/~jcs/maven2-snapshot/info/ejava/examples/common/jboss-rmi-client/4.0.0-SNAPSHOT/maven-metadata.xml
    Downloaded: http://webdev.jhuep.com/~jcs/maven2-snapshot/info/ejava/examples/common/jboss-rmi-client/4.0.0-SNAPSHOT/maven-metadata.xml (621 B at 0.8 KB/sec)
    Downloading: http://webdev.jhuep.com/~jcs/maven2-snapshot/info/ejava/examples/common/jboss-rmi-client/4.0.0-SNAPSHOT/jboss-rmi-client-4.0.0-20141001.053140-16.pom
    Downloaded: http://webdev.jhuep.com/~jcs/maven2-snapshot/info/ejava/examples/common/jboss-rmi-client/4.0.0-SNAPSHOT/jboss-rmi-client-4.0.0-20141001.053140-16.pom (5 KB at 77.3 KB/sec)
    ...
    [INFO] BUILD SUCCESS
    
  8. Create a JNDI configuration for JBoss Remoting. Use variable references to the server to better support different configurations. Place this file in src/test/resources. We will describe the contents of the file later once we get tangible values inserted.

    $ mkdir -p basicejb-test/src/test/resources
    $ vi basicejb-test/src/test/resources/jndi.properties
    
    $ cat basicejb-test/src/test/resources/jndi.properties
    
    #jndi.properties for JBoss Remoting
    java.naming.factory.initial=${jboss.remoting.java.naming.factory.initial}
    java.naming.factory.url.pkgs=${jboss.remoting.java.naming.factory.url.pkgs}
    java.naming.provider.url=${jboss.remoting.java.naming.provider.url}
    #java.naming.security.principal=${jndi.user}
    #java.naming.security.credentials=${jndi.password}
    jboss.naming.client.ejb.context=true
    
  9. Build the RMI Test project after the jndi.properties file is in place.

    $ mvn clean process-test-resources
    ...
    [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ basicejb-test ---
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] Copying 1 resource
    ...
    [INFO] BUILD SUCCESS
    

    Notice the properties file was copied from the src/test tree to target/test-classes without modification. We need to make more changes so the properties within the file get assigned to actual values from out environment.

    $ cat basicejb-test/target/test-classes/jndi.properties
    
    #jndi.properties for JBoss Remoting
    java.naming.factory.initial=${jboss.remoting.java.naming.factory.initial}
    java.naming.factory.url.pkgs=${jboss.remoting.java.naming.factory.url.pkgs}
    java.naming.provider.url=${jboss.remoting.java.naming.provider.url}
    #java.naming.security.principal=${jndi.user}
    #java.naming.security.credentials=${jndi.password}
    jboss.naming.client.ejb.context=true
    
  10. Add resource filtering to test resources in the pom.xml. This will cause the jndi.properties file to have variables replaces with physical values when copied to the target tree.

    
            <build>
            <!-- filter test/resource files for profile-specific valies -->
            <testResources>
                <testResource>
                    <directory>src/test/resources</directory>
                    <filtering>true</filtering>
                    <includes>
                        <include>**/*.properties</include>
                    </includes>
                </testResource>
                <testResource>
                    <directory>src/test/resources</directory>
                    <filtering>false</filtering>
                    <excludes>
                        <exclude>**/*.properties</exclude>
                    </excludes>
                </testResource>
            </testResources>
  11. Rebuild the RMI Test module and notice two copies take place; one for the filtered set and a second for the unfiltered set.

    $ mvn clean process-test-resources
    ...
    [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ basicejb-test ---
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] Copying 1 resource
    [INFO] Copying 0 resource
    ...
    [INFO] BUILD SUCCESS
    

    However, our jndi.properties file in the target tree still looks the same. That is because we have not defined the referenced variables in the environment.

    $ cat basicejb-test/target/test-classes/jndi.properties
    
    #jndi.properties for JBoss Remoting
    java.naming.factory.initial=${jboss.remoting.java.naming.factory.initial}
    java.naming.factory.url.pkgs=${jboss.remoting.java.naming.factory.url.pkgs}
    java.naming.provider.url=${jboss.remoting.java.naming.provider.url}
    #java.naming.security.principal=${jndi.user}
    #java.naming.security.credentials=${jndi.password}
    jboss.naming.client.ejb.context=true
    
  12. Add the properties referenced in jndi.properties to the *root* pom.xml.

    
    $ cat pom.xml
        <properties>
    ...
            <jboss.host>localhost</jboss.host>
            <jboss.http.port>8080</jboss.http.port>
            <jndi.user>known</jndi.user>
            <jndi.password>password1!</jndi.password>

            <jboss.remoting.java.naming.factory.initial>org.jboss.naming.remote.client.InitialContextFactory</jboss.remoting.java.naming.factory.initial>
            <jboss.remoting.java.naming.provider.url>http-remoting://${jboss.host}:${jboss.http.port}</jboss.remoting.java.naming.provider.url>
            <jboss.remoting.java.naming.factory.url.pkgs/>
        </properties>
  13. Rebuild the RMI Test module and note the contents of the jndi.properties file in the target/test-classes tree should be expanded with the properties defined in the root pom.xml.

    $ mvn clean process-test-resources
    [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ basicejb-test ---
    ...
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] Copying 1 resource
    [INFO] Copying 0 resource
    ...
    [INFO] BUILD SUCCESS
    
    $ cat target/test-classes/jndi.properties
    
    #jndi.properties for JBoss Remoting
    java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
    java.naming.factory.url.pkgs=
    java.naming.provider.url=http-remoting://localhost:8080
    #java.naming.security.principal=known
    #java.naming.security.credentials=password1!
    jboss.naming.client.ejb.context=true
    
  14. Create a JUnit IT test that will lookup the EJB and invoke the ping method.

    $ mkdir -p basicejb-test/src/test/java/org/myorg/basicejb/earejb
    
    $ cat basicejb-test/src/test/java/org/myorg/basicejb/earejb/ReservationIT.java
    
    package org.myorg.basicejb.earejb;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import org.junit.Before;
    import org.junit.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    public class ReservationIT {
        private static final Logger logger = LoggerFactory.getLogger(ReservationIT.class);
        private InitialContext jndi;
        
        
        @Before
        public void setUp() throws NamingException {
            logger.debug("getting jndi initial context");
            jndi=new InitialContext();
            logger.debug("jndi={}", jndi.getEnvironment());
        }
        @Test
        public void testPing() {
            logger.info("*** testPing ***");
        }
    }
  15. Add a log4j.xml file to configure Log4j loggers. You may use the same file you put into your EJB.

    $ cp basicejb-ejb/src/test/resources/log4j.xml basicejb-test/src/test/resources/
    
    
    $ cat basicejb-test/src/test/resources/log4j.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE log4j:configuration PUBLIC
      "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
    <log4j:configuration 
        xmlns:log4j="http://jakarta.apache.org/log4j/" 
        debug="false">
       
        <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
            <param name="Target" value="System.out"/>
            <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%d{HH:mm:ss,SSS} %-5p (%F:%L) -%m%n"/>
            </layout>
        </appender>

        <appender name="logfile" class="org.apache.log4j.RollingFileAppender">
            <param name="File" value="target/log4j-out.txt"/>
            <param name="Append" value="false"/>
            <param name="MaxFileSize" value="100KB"/>
            <param name="MaxBackupIndex" value="1"/>
            <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" 
                       value="%-5p %d{dd-MM HH:mm:ss,SSS} [%c] (%F:%M:%L)  -%m%n"/>
            </layout>
       </appender>

       <logger name="org.myorg">
          <level value="debug"/>
          <appender-ref ref="logfile"/>  
       </logger>
       <root>
          <priority value="info"/>    
          <appender-ref ref="CONSOLE"/>  
       </root>   
    </log4j:configuration>
  16. Try building the Test module at this point. Notice how no tests attempted to run. That is because the Tests run reported are surefire unit tests and we have no unit tests in this module. All our tests (1) are integration tests. Okay...why didn't our integration test run? The failsafe plugin, unlike the surefire plugin does not run automatically. We must wire it into the build.

    You will also notice the extra resources copy for the log4j.xml we put into src/test/resources. It was not filtered because it did not match the include pattern.

    $cd basicejb-test; mvn clean install
    ...
    [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ basicejb-test ---
    [INFO] Deleting /home/jcstaff/proj/basicejbEx/basicejb-test/target
    ...
    [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ basicejb-test ---
    ...
    [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ basicejb-test ---
    ...
    [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ basicejb-test ---
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] Copying 1 resource
    [INFO] Copying 1 resource
    ...
    [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ basicejb-test ---
    [INFO] Compiling 1 source file to /home/jcstaff/proj/basicejbEx/basicejb-test/target/test-classes
    ...
    [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ basicejb-test ---
    ...
    [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ basicejb-test ---
    ...
    [INFO] --- maven-install-plugin:2.4:install (default-install) @ basicejb-test ---
    ...
    [INFO] BUILD SUCCESS
    
  17. Declare the failsafe plugin to your RMI Test/pom.xml to cause our JUnit IT test to be attempted.

    
            <plugins>
                <!-- adds IT integration tests to the build -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-failsafe-plugin</artifactId>
                </plugin>
  18. Define the failsafe plugin in the parent module. This definition will have...

    
        <properties>
        ...
            <maven-failsafe-plugin.version>2.17</maven-failsafe-plugin.version>
        ...
        <build>
            <pluginManagement>
                <plugins>
                    ...
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-failsafe-plugin</artifactId>
                        <version>${maven-failsafe-plugin.version}</version>
                        <configuration>
                            <argLine>${surefire.argLine}</argLine>
                        </configuration>
                        <executions>
                            <execution> 
                               <goals>
                                   <goal>integration-test</goal>
                                   <goal>verify</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
        ...
        <profiles>
            <profile> <!-- tells surefire/failsafe to run JUnit tests with remote debug -->
                <id>debugger</id>
                <activation>
                    <property>
                        <name>debugger</name>
                    </property>
                </activation>
                <properties>
                    <surefire.argLine>-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -Xnoagent -Djava.compiler=NONE</surefire.argLine>
                </properties>                                  
            </profile>        
  19. Try building the Test module now that the failsafe plugin has been correctly declared. Notice how our IT test runs within the integration-test phase. It is not doing much yet but we are purposely taking baby steps to explain every corner of the multi-module build.

    $cd basicejb-test; mvn clean install
    ...
    [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ basicejb-test ---
    ...
    [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ basicejb-test ---
    ...
    [INFO] --- maven-failsafe-plugin:2.17:integration-test (default) @ basicejb-test ---
    ...
    -------------------------------------------------------
     T E S T S
    -------------------------------------------------------
    Running org.myorg.basicejb.earejb.ReservationIT
    20:51:22,679 DEBUG (ReservationIT.java:18) -getting jndi initial context
    20:51:22,815 INFO  (Xnio.java:92) -XNIO version 3.2.2.Final
    20:51:22,925 INFO  (NioXnio.java:56) -XNIO NIO Implementation Version 3.2.2.Final
    20:51:23,028 INFO  (EndpointImpl.java:69) -JBoss Remoting version 4.0.3.Final
    20:51:23,180 DEBUG (ReservationIT.java:20) -jndi={java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory, java.naming.provider.url=http-remoting://localhost:8080, java.naming.factory.url.pkgs=, jboss.naming.client.ejb.context=true}
    20:51:23,181 INFO  (ReservationIT.java:26) -*** testPing ***
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.767 sec - in org.myorg.basicejb.earejb.ReservationIT
    
    Results :
    
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
    
    [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
    [INFO] 
    [INFO] --- maven-failsafe-plugin:2.17:verify (default) @ basicejb-test ---
    [INFO] Failsafe report directory: /home/jcstaff/proj/basicejbEx/basicejb-test/target/failsafe-reports
    [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
    [INFO] 
    [INFO] --- maven-install-plugin:2.4:install (default-install) @ basicejb-test ---
    ...
    [INFO] BUILD SUCCESS
    
  20. Add an additional line to the @Before method. This will perform a remote lookup of the "jms" JNDI context. If the server is up and knows about this context, we will continue to be successful. However, if the server is down or does not know about the context -- it will fail.

        @Before
    
        public void setUp() throws NamingException {
            logger.debug("getting jndi initial context");
            jndi=new InitialContext();
            logger.debug("jndi={}", jndi.getEnvironment());
            jndi.lookup("jms");
        }
  21. Re-run the IT test with the server stopped. Note the failure in the build does not come until after the verify phase.

    $ cd basicejb-test; mvn clean install
     T E S T S
    -------------------------------------------------------
    Running org.myorg.basicejb.earejb.ReservationIT
    ...
    Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 5.668 sec <<< FAILURE! - in org.myorg.basicejb.earejb.ReservationIT
    testPing(org.myorg.basicejb.earejb.ReservationIT)  Time elapsed: 5.471 sec  <<< ERROR!
    javax.naming.CommunicationException: Failed to connect to any server. Servers tried: [http-remoting://localhost:8080 (Operation failed with status WAITING after 5000 MILLISECONDS)]
    	at org.jboss.naming.remote.protocol.IoFutureHelper.get(IoFutureHelper.java:97)
    	at org.jboss.naming.remote.client.HaRemoteNamingStore.failOverSequence(HaRemoteNamingStore.java:198)
    	at org.jboss.naming.remote.client.HaRemoteNamingStore.namingStore(HaRemoteNamingStore.java:149)
    	at org.jboss.naming.remote.client.HaRemoteNamingStore.namingOperation(HaRemoteNamingStore.java:130)
    	at org.jboss.naming.remote.client.HaRemoteNamingStore.lookup(HaRemoteNamingStore.java:272)
    	at org.jboss.naming.remote.client.RemoteContext.lookup(RemoteContext.java:87)
    	at org.jboss.naming.remote.client.RemoteContext.lookup(RemoteContext.java:129)
    	at javax.naming.InitialContext.lookup(InitialContext.java:411)
    	at org.myorg.basicejb.earejb.ReservationIT.setUp(ReservationIT.java:22)
    
    Results :
    
    Tests in error: 
      ReservationIT.setUp:22 » Communication Failed to connect to any server. Server...
    
    Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
    ...
    [INFO] --- maven-failsafe-plugin:2.17:verify (default) @ basicejb-test ---
    ...
    [INFO] BUILD FAILURE
    
  22. Re-run the IT test with the server running.

    $ cd basicejb-test; mvn clean install
    ...
    [INFO] BUILD SUCCESS
    
  23. Register the RMI Test module with the root pom and perform a root-level build.

    
        <modules>
            <module>basicejb-ejb</module>
            <module>basicejb-ear</module>
            <module>basicejb-test</module>
        </modules>
    $ mvn clean install
    ...
    [INFO] Reactor Summary:
    [INFO] 
    [INFO] Basic EJB Exercise ................................. SUCCESS [  0.564 s]
    [INFO] Basic EJB Exercise::EJB ............................ SUCCESS [  5.183 s]
    [INFO] Basic EJB Exercise::EAR ............................ SUCCESS [  0.951 s]
    [INFO] Basic EJB Exercise::Remote Test .................... SUCCESS [  0.731 s]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    

In the previous section we ended with the IT test communicating with the server's JNDI tree. In this section we will *finally* deploy the EAR, lookup the @Remote interface of our EJB, and invoke our first method.

We want tests to run as automated as possible. This allows us to simplify testing as well as leverage continous integration techniques (e.g., CruiseControl, Hudson, Jenkins; i.e., nightly builds/tests). To help automate this we are going to leverage the Maven cargo plugin. Cargo, itself, is a Java library that is used to manage Java EE containers. The maven cargo plugin just makes it callable from within Maven. We will add the cargo plugin to the RMI Test project (to deploy the application) since the application isn't ready to be deployed until after the EAR is built.

  1. Declare the cargo plugin in the RMI Test pom.xml to deploy the EAR to JBoss. Like always, We will only put what is specific to this module in the module's pom.xml.

    
    $ cat basicejb-test/pom.xml
    ...
        <build>
            <plugins>
                ...
                <!-- artifacts to deploy to server -->
                <plugin>
                    <groupId>org.codehaus.cargo</groupId>
                    <artifactId>cargo-maven2-plugin</artifactId>
                    <configuration>
                        <deployables>
                            <deployable>
                                <groupId>${project.groupId}</groupId>
                                <artifactId>basicejb-ear</artifactId>
                                <type>ear</type>
                            </deployable>
                        </deployables>
                    </configuration>
                </plugin>
  2. Cargo requires the module to be deployed to also be a scope=compile dependency of the local module. Since this is a Test module with no dependents -- we can add that without concern.

    
    <!-- cargo requires scope=compile dependencies on deployables -->
            <dependency>
                <groupId>${project.groupId}</groupId>
                <artifactId>basicejb-ear</artifactId>
                <type>ear</type>
                <version>${project.version}</version>
            </dependency>
  3. Define the details of the cargo plugin in the parent pom. Since cargo is not specific to any one container -- there is a good bit that requires configuring. The details are a mouthful but, in short, this tells cargo to deploy our artifacts to a running JBoss server (of a specific version), listening on a specific admin address:port, and where to place the runtime logs from this activity.

    
        <properties>
            ...
            <wildfly.version>8.1.0.Final</wildfly.version>
            <cargo-maven2-plugin.version>1.4.3</cargo-maven2-plugin.version>
            <cargo.containerId>wildfly8x</cargo.containerId>
            <jboss.mgmt.host>${jboss.host}</jboss.mgmt.host>
            <jboss.mgmt.port>9990</jboss.mgmt.port>
        ...
        <build>
            <pluginManagement>
                <plugins>
                    <plugin>
                    ...
                        <groupId>org.codehaus.cargo</groupId>
                        <artifactId>cargo-maven2-plugin</artifactId>
                        <version>${cargo-maven2-plugin.version}</version>
                        <configuration>
                            <container>
                                <containerId>${cargo.containerId}</containerId>
                                <type>remote</type>
                                <log>target/server.log</log>
                                <output>target/output.log</output>
                            </container>
                            <configuration>
                                <type>runtime</type>
                                <properties>
                                    <cargo.hostname>${jboss.mgmt.host}</cargo.hostname>
                                    <cargo.jboss.management.port>${jboss.mgmt.port}</cargo.jboss.management.port>
                                </properties>
                            </configuration>
                        </configuration>
                        <dependencies>
                            <dependency>
                                <groupId>org.wildfly</groupId>
                                <artifactId>wildfly-controller-client</artifactId>
                                <version>${wildfly.version}</version>
                            </dependency>
                        </dependencies>
                        <executions>
                           <execution>
                               <id>cargo-prep</id> 
                                   <phase>pre-integration-test</phase>
                               <goals>
                                    <goal>redeploy</goal>
                               </goals>
                           </execution>
                            <execution>
                                <id>cargo-post</id>
                                <phase>post-integration-test</phase>
                                <goals>
                                    <goal>undeploy</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
  4. Rebuild the RMI Test and note the deployment of the EAR to the JBoss server prior to running the integration tests with failsafe and undeployed after finishing.

    ...
    [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ basicejb-test ---
    ...
    [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ basicejb-test ---
    ...
    [INFO] --- cargo-maven2-plugin:1.4.3:redeploy (cargo-prep) @ basicejb-test ---
    Oct 05, 2014 11:17:36 PM org.xnio.Xnio <clinit>
    INFO: XNIO version 3.2.2.Final
    Oct 05, 2014 11:17:36 PM org.xnio.nio.NioXnio <clinit>
    INFO: XNIO NIO Implementation Version 3.2.2.Final
    Oct 05, 2014 11:17:36 PM org.jboss.remoting3.EndpointImpl <clinit>
    INFO: JBoss Remoting version 4.0.3.Final
    ...
    [INFO] --- maven-failsafe-plugin:2.17:integration-test (default) @ basicejb-test ---
    ...
    Running org.myorg.basicejb.earejb.ReservationIT
    ...
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
    ...
    [INFO] --- cargo-maven2-plugin:1.4.3:undeploy (cargo-post) @ basicejb-test ---
    ...
    [INFO] --- maven-failsafe-plugin:2.17:verify (default) @ basicejb-test ---
    ...
    [INFO] BUILD SUCCESS
    

    The following should have been output at the JBoss console and server.log. The "java:" names are JNDI names that can be used to locate the local and remote interfaces of our ReservationEJB.

    23:17:37,356 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) 
    JNDI bindings for session bean named ReservationEJB in deployment unit subdeployment "basicejb-ejb-1.0-SNAPSHOT.jar" of 
    deployment "basicejb-ear-1.0-SNAPSHOT.ear" are as follows:
    
            java:global/basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationLocal
            java:app/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationLocal
            java:module/ReservationEJB!org.myorg.basicejb.ejb.ReservationLocal
            java:global/basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
            java:app/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
            java:module/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
            java:jboss/exported/basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
    
    23:17:37,368 INFO  [org.jboss.weld.deployer] (MSC service thread 1-2) JBAS016005: Starting Services for CDI deployment: basicejb-ear-1.0-SNAPSHOT.ear
    23:17:37,376 INFO  [org.jboss.weld.deployer] (MSC service thread 1-3) JBAS016008: Starting weld service for deployment basicejb-ear-1.0-SNAPSHOT.ear
    23:17:38,335 INFO  [org.jboss.as.server] (management-handler-thread - 1) JBAS018559: Deployed "basicejb-ear-1.0-SNAPSHOT.ear" (runtime-name : "basicejb-ear-1.0-SNAPSHOT.ear")
    

    The only one that is available to our external RMI client starts with "java:jboss/exported/". That JNDI is available to external clients -- like our IT test.

    java:jboss/exported/basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
    

    The following will be the base JNDI name of the EJB deployed by the EAR.

    basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
    

Each EJB interface will have an entry in the JNDI tree. Clients will use the JNDI tree to locate the interface object they need based on a hierarchical name. The names available locally within the server were standardized in JavaEE 6. However that specification did not cover external references -- so we have to peek at what JBoss is telling for the exported name.

java:jboss/exported/basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
  1. Add the above JNDI name for the @Remote interface in the RMI Test failsafe configuration so that our IT test does not have to know about version numbers. The following is equivalent to passing -Djndi.name.reservation to the JVM.

    
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <configuration>
                <systemPropertyVariables>
                    <jndi.name.reservation>basicejb-ear-${project.version}/basicejb-ejb-${project.version}/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</jndi.name.reservation>
                </systemPropertyVariables>
            </configuration>
        </plugin>
  2. Add the dependency on the ejb-client.jar to the RMI Test. This will go in the root dependency area. This was built by the maven-ejb-plugin when we built the EJB module.

    
            <!-- brings in the EJB-client jar file w/o the EJB -->
            <dependency>
                <groupId>${project.groupId}</groupId>
                <artifactId>basicejb-ejb</artifactId>
                <version>${project.version}</version>
                <type>ejb-client</type>
                <scope>test</scope>
            </dependency>
  3. Add the handling of the provided JNDI name to the IT class by adding the following snippets of code. Note the JNDI name passed as a system property by failsafe.

    import static org.junit.Assert.*;
    
    ...
    public class ReservationIT {
        ...
        private static final String reservationJNDI = System.getProperty("jndi.name.reservation"); 
        
        @Before
        public void setUp() throws NamingException {
            assertNotNull("jndi.name.reservation not supplied", reservationJNDI);
            ...
            logger.debug("jndi name:{}", reservationJNDI);
        }
  4. Add a lookup of the JNDI name and some debug of the remote interface that came back. We should now have something we can communcate with.

    import org.myorg.basicejb.ejb.ReservationRemote;
    
    ...
    public class ReservationIT {
        ...
        private ReservationRemote reservationist; 
        
        @Before
        public void setUp() throws NamingException {
            ...
            reservationist = (ReservationRemote) jndi.lookup(reservationJNDI);
            logger.debug("reservationist={}", reservationist);
        }
  5. Add a call to the ReservationRemote.ping() method in the testPing() @Test method. This should complete our initial end-to-end IT test.

    public class ReservationIT {
    
        ...
        @Test
        public void testPing() throws NamingException {
            ...
            reservationist.ping();
        }
    }
  6. Build the application from the root. Note the JNDI lookup of the @Remote interface and call to ping() that took place.

    ...
    [INFO] --- cargo-maven2-plugin:1.4.3:redeploy (cargo-prep) @ basicejb-test ---
    Oct 06, 2014 12:16:23 AM org.xnio.Xnio <clinit>
    INFO: XNIO version 3.2.2.Final
    Oct 06, 2014 12:16:23 AM org.xnio.nio.NioXnio <clinit>
    INFO: XNIO NIO Implementation Version 3.2.2.Final
    Oct 06, 2014 12:16:23 AM org.jboss.remoting3.EndpointImpl <clinit>
    INFO: JBoss Remoting version 4.0.3.Final
    ...
    [INFO] --- maven-failsafe-plugin:2.17:integration-test (default) @ basicejb-test ---
    ...
    Running org.myorg.basicejb.earejb.ReservationIT
    00:16:25,660 DEBUG (ReservationIT.java:23) -getting jndi initial context
    ...
    00:16:26,033 DEBUG (ReservationIT.java:25) -jndi={java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory, java.naming.provider.url=http-remoting://localhost:8080, java.naming.factory.url.pkgs=, jboss.naming.client.ejb.context=true}
    ...
    00:16:26,545 DEBUG (ReservationIT.java:28) -jndi name:basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
    00:16:26,745 DEBUG (ReservationIT.java:30) -reservationist=Proxy for remote EJB StatelessEJBLocator{appName='basicejb-ear-1.0-SNAPSHOT', moduleName='basicejb-ejb-1.0-SNAPSHOT', distinctName='', beanName='ReservationEJB', view='interface org.myorg.basicejb.ejb.ReservationRemote'}
    00:16:26,746 INFO  (ReservationIT.java:35) -*** testPing ***
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.46 sec - in org.myorg.basicejb.earejb.ReservationIT
    ...
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
    ...
    [INFO] --- cargo-maven2-plugin:1.4.3:undeploy (cargo-post) @ basicejb-test ---
    ...
    [INFO] --- maven-failsafe-plugin:2.17:verify (default) @ basicejb-test ---
    ...
    [INFO] BUILD SUCCESS
    
  7. Look in the server.log for the following output showing the server-side EJB logging INFO and DEBUG messages.

    2014-10-06 00:16:26,566 INFO  [org.jboss.ejb.client] (pool-1-thread-4) JBoss EJB Client version 2.0.1.Final
    2014-10-06 00:16:26,894 DEBUG [org.myorg.basicejb.ejb.ReservationEJB] (EJB default - 1) *** ReservationEJB.init() ***
    2014-10-06 00:16:26,898 DEBUG [org.myorg.basicejb.ejb.ReservationEJB] (EJB default - 1) ping called
    2014-10-06 00:16:26,899 DEBUG [org.myorg.basicejb.ejb.ReservationEJB] (EJB default - 1) *** ReservationEJB.destroy() ***
    
  8. Now that you have everything working lets insert a common mistake made when forming a IT test. Rename your ReservationIT test to TestReservationIT or ReservationTest). Be sure to rename both the Java class and the file.

    $ mv basicejb-test/src/test/java/org/myorg/basicejb/earejb/ReservationIT.java basicejb-test/src/test/java/org/myorg/basicejb/earejb/TestReservationIT.java
    ...
    public class TestReservationIT {
        private static final Logger logger = LoggerFactory.getLogger(TestReservationIT.class);
    
  9. Attempt to build your RMI Test module. The problem here is that your IT test (which requires a JNDI name property passed to it) is being run during the test phase and the failsafe configuration we put in place runs in the integration-test-phase. It is running in the earlier test phase because the name of the class now matches the surefire pattern.

    $ mvn clean verify
    ...
    [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ basicejb-test ---
    ...
    [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ basicejb-test ---
    [INFO] Surefire report directory: /home/jcstaff/proj/basicejbEx/basicejb-test/target/surefire-reports
    
    -------------------------------------------------------
     T E S T S
    -------------------------------------------------------
    Running org.myorg.basicejb.earejb.TestReservationIT
    Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.209 sec <<< FAILURE!
    testPing(org.myorg.basicejb.earejb.TestReservationIT)  Time elapsed: 0.013 sec  <<< FAILURE!
    java.lang.AssertionError: jndi.name.reservation not supplied
    	at org.junit.Assert.fail(Assert.java:88)
    	at org.junit.Assert.assertTrue(Assert.java:41)
    	at org.junit.Assert.assertNotNull(Assert.java:621)
    	at org.myorg.basicejb.earejb.TestReservationIT.setUp(TestReservationIT.java:22)
    
    Results :
    
    Failed tests:   testPing(org.myorg.basicejb.earejb.TestReservationIT): jndi.name.reservation not supplied
    
    Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
    
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD FAILURE
    
  10. Lets go one more (mistaken) step and (mistakenly) assume all we have to do is copy our failsafe configuration to the surefire plugin. That should make the assert happy.

    
    
            <plugins>
                <!-- a mistaken step to attempt to correct an IT test setup problem -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <configuration>
                        <systemPropertyVariables>
                            <jndi.name.reservation>basicejb-ear-${project.version}/basicejb-ejb-${project.version}/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</jndi.name.reservation>
                        </systemPropertyVariables>
                    </configuration>
                </plugin>
                <!-- adds IT integration tests to the build -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-failsafe-plugin</artifactId>
                    <configuration>
                        <systemPropertyVariables>
                            <jndi.name.reservation>basicejb-ear-${project.version}/basicejb-ejb-${project.version}/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</jndi.name.reservation>
                        </systemPropertyVariables>
                    </configuration>
                </plugin>
  11. Attempt to build the RMI Test module with the assert for the JNDI name resolved and notice the error we get. The IT test is configured correctly. It is doing all the right things. The problem is that with its current name, it matches the surefire criteria and is being run prior to the application being deployed to the server.

    $ mvn clean verify
    ...
    [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ basicejb-test ---
    ...
    [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ basicejb-test ---
    
    -------------------------------------------------------
     T E S T S
    -------------------------------------------------------
    Running org.myorg.basicejb.earejb.TestReservationIT
    12:59:48,726 DEBUG (TestReservationIT.java:24) -getting jndi initial context
    ...
    12:59:49,546 INFO  (TestReservationIT.java:36) -*** testPing ***
    12:59:49,595 INFO  (VersionReceiver.java:103) -EJBCLIENT000017: Received server version 2 and marshalling strategies [river]
    12:59:49,596 INFO  (RemotingConnectionEJBReceiver.java:215) -EJBCLIENT000013: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext@3f7b86b6, receiver=Remoting connection EJB receiver [connection=org.jboss.ejb.client.remoting.ConnectionPool$PooledConnection@46c93749,channel=jboss.ejb,nodename=fedora17x64-kde]} on channel Channel ID d8293998 (outbound) of Remoting connection 6762a5f3 to localhost/127.0.0.1:8080
    Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 1.091 sec <<< FAILURE!
    testPing(org.myorg.basicejb.earejb.TestReservationIT)  Time elapsed: 0.891 sec  <<< ERROR!
    java.lang.IllegalStateException: EJBCLIENT000025: No EJB receiver available for handling [appName:basicejb-ear-1.0-SNAPSHOT, moduleName:basicejb-ejb-1.0-SNAPSHOT, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@6e9af2a2
    	at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:749)
    
    ...
    Tests in error: 
      testPing(org.myorg.basicejb.earejb.TestReservationIT): EJBCLIENT000025: No EJB receiver available for handling [appName:basicejb-ear-1.0-SNAPSHOT, moduleName:basicejb-ejb-1.0-SNAPSHOT, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@6e9af2a2
    
    Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
    
    ...
    [INFO] BUILD FAILURE
    
  12. Restore your IT test back to its original state so that it does not get executed during the test phase. Also remote the surefire configuration since the JNDI name is never needed during a unit test and our RMI Test module does not run any unit tests.

    $ mv basicejb-test/src/test/java/org/myorg/basicejb/earejb/TestReservationIT.java basicejb-test/src/test/java/org/myorg/basicejb/earejb/ReservationIT.java
    ...
    public class ReservationIT {
        private static final Logger logger = LoggerFactory.getLogger(ReservationIT.class);
    
  13. Your build should now be working again.

    $ mvn clean install
    

In this section we will make a few slight changes to upgrade the remote communications to EJB Client. JBoss recommends against the general purpose JBoss Remoting for RMI access to EJBs in favor of the EJB-specific EJB Client. Since EJB Client knows it is talking to a JBoss Server with an EJB at the remote end -- portions of the interactions can be optimized. JBoss also uses EJB Client exclusively within the server.

We will have to make a few changes to our client in order to support EJB Client.

  1. Re-run your build and note the output during the IT test. This will be slightly different after the next few steps.

    $ mvn clean install
    ...
    Running org.myorg.basicejb.earejb.ReservationIT
    ...
    01:33:43,814 DEBUG (ReservationIT.java:31) -jndi name:basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
    ...
    01:33:43,922 INFO  (ReservationIT.java:38) -*** testPing ***
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.123 sec - in org.myorg.basicejb.earejb.ReservationIT
    01:33:43,984 INFO  (ChannelAssociation.java:458) -EJBCLIENT000016: Channel Channel ID b2993636 (outbound) of Remoting connection 4ceee548 to localhost/127.0.0.1:8080 can no
    longer process messages
    ...
    
  2. Update the java.naming.factory.url.pkgs specification in the jndi.properties.

    $ cat src/test/resources/jndi.properties
    ...
    java.naming.factory.url.pkgs=${jboss.ejbclient.java.naming.factory.url.pkgs}
    ...
    
  3. Create a second new file in src/test/resources to house EJB Client custom properties.

    $ cat src/test/resources/jboss-ejb-client.properties
    
    #top level property listing the names of the connections. There will be a set 
    #of properties for each name listed here
    remote.connections=default
    
    #here we define the properties for the server we have called "default"
    remote.connection.default.host=${jboss.host}
    remote.connection.default.port=${jboss.naming.port}
    remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
    #remote.connection.default.username=${jndi.user}
    #remote.connection.default.password=${jndi.password}
    
  4. Add the following new properties to the root pom. This will define the extra properties used in the files in src/test/resources.

    
            <jboss.remoting.port>${jboss.http.port}</jboss.remoting.port>
            <jboss.naming.port>${jboss.remoting.port}</jboss.naming.port>
            ...
            <jboss.ejbclient.java.naming.factory.url.pkgs>org.jboss.ejb.client.naming</jboss.ejbclient.java.naming.factory.url.pkgs>
  5. Build the test resources to see the new property files fully instantiated. Notice the jndi.properties now has a new java.naming.factory.url.pkgs property. The package specification will be used to search for JNDI prefix handlers not handled by the InitialContextFactory.

    
     
    $ mvn clean process-test-resources
    $ cat target/test-classes/jndi.properties

    #jndi.properties
    java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
    java.naming.factory.url.pkgs=org.jboss.ejb.client.naming
    java.naming.provider.url=http-remoting://localhost:8080
    #java.naming.security.principal=known
    #java.naming.security.credentials=password1!
    jboss.naming.client.ejb.context=true
  6. Add the JNDI prefix "ejb:" to the beginning of the JNDI name in the failsafe configuration.

    
    <jndi.name.reservation>ejb:basicejb-ear-${project.version}/basicejb-ejb-${project.version}/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</jndi.name.reservation>

    Notice the jboss-ejb-client.jar is in the classpath for the IT Test. This was brought in by the direct dependency on info.ejava.examples.common:jboss-rmi-client.

    $ mvn dependency:tree
    
    [INFO] +- info.ejava.examples.common:jboss-rmi-client:pom:4.0.0-SNAPSHOT:test
    [INFO] |  +- org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:jar:1.0.0.Final:test
    [INFO] |  +- org.jboss.spec.javax.ejb:jboss-ejb-api_3.2_spec:jar:1.0.0.Final:test
    [INFO] |  +- org.jboss:jboss-ejb-client:jar:2.0.1.Final:test
    

    Notice that jboss-ejb-client.jar provides a handler for the "ejb:" prefix below the "org.jboss.ejb.client.naming" package as specified in the java.naming.factory.url.pkgs property. Prior to adding the extra package specification, the InitialContextFactory would not have been able to resolve a handler for the "ejb:" prefix.

    $ jar tf ~/.m2/repository/org/jboss/jboss-ejb-client/2.0.1.Final/jboss-ejb-client-2.0.1.Final.jar | grep org.jboss.ejb.client.naming
    org/jboss/ejb/client/naming/
    org/jboss/ejb/client/naming/ejb/
    org/jboss/ejb/client/naming/ejb/SecurityActions$1.class
    org/jboss/ejb/client/naming/ejb/EjbNamingContext$2.class
    org/jboss/ejb/client/naming/ejb/EjbNamingContext.class
    org/jboss/ejb/client/naming/ejb/EjbNamingContextSetup.class
    org/jboss/ejb/client/naming/ejb/ejbURLContextFactory.class
    org/jboss/ejb/client/naming/ejb/SecurityActions.class
    org/jboss/ejb/client/naming/ejb/EjbJndiIdentifier.class
    org/jboss/ejb/client/naming/ejb/EjbJndiNameParser.class
    org/jboss/ejb/client/naming/ejb/SecurityActions$2.class
    org/jboss/ejb/client/naming/ejb/SecurityActions$3.class
    org/jboss/ejb/client/naming/ejb/EjbNamingContext$1.class
    
  7. Re-run your IT test

    $ mvn clean install
    ...
    Running org.myorg.basicejb.earejb.ReservationIT
    ...
    01:39:27,809 DEBUG (ReservationIT.java:31) -jndi name:ejb:basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
    ...
    01:39:27,831 INFO  (ReservationIT.java:38) -*** testPing ***
    01:39:27,906 INFO  (VersionReceiver.java:103) -EJBCLIENT000017: Received server version 2 and marshalling strategies [river]
    01:39:27,913 INFO  (RemotingConnectionEJBReceiver.java:215) -EJBCLIENT000013: Successful version handshake completed for receiver context
    EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext@301487f9, receiver=Remoting connection EJB receiver
    [connection=org.jboss.ejb.client.remoting.ConnectionPool$PooledConnection@13eb0518,channel=jboss.ejb,nodename=fedora17x64-kde]} on channel Channel ID ccb6efe6 (outbound) of
    Remoting connection 0231556b to localhost/127.0.0.1:8080
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.215 sec - in org.myorg.basicejb.earejb.ReservationIT
    01:39:28,012 INFO  (ChannelAssociation.java:458) -EJBCLIENT000016: Channel Channel ID ccb6efe6 (outbound) of Remoting connection 0231556b to localhost/127.0.0.1:8080 can no
    longer process messages
    01:39:28,015 INFO  (ChannelAssociation.java:458) -EJBCLIENT000016: Channel Channel ID f4db6e8d (outbound) of Remoting connection 68eefca4 to localhost/127.0.0.1:8080 can no
    longer process messages
    
    Results :
    
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
    ...
    [INFO] BUILD SUCCESS
    
  8. Temporarily remove the java.naming.factory.url.pkgs value while keeping the "ejb:" JNDI prefix.

    #jndi.properties
    java.naming.factory.url.pkgs=
    
  9. Re-run the IT test and notice the JNDI failure when the package specification is not present.

    javax.naming.NameNotFoundException: ejb:basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote -- service jboss.naming.context.java.jboss.exported."ejb:basicejb-ear-1.0-SNAPSHOT"."basicejb-ejb-1.0-SNAPSHOT"."ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote"
    	at org.jboss.as.naming.ServiceBasedNamingStore.lookup(ServiceBasedNamingStore.java:104)
    ...
    [INFO] BUILD FAILURE
    
  10. Restore the jndi.properties package specification.

    java.naming.factory.url.pkgs=${jboss.ejbclient.java.naming.factory.url.pkgs}
    
  11. As a final sanity check, this is what your multi-module application should look like at this time.

    .
    |-- basicejb-ear
    |   `-- pom.xml
    |-- basicejb-ejb
    |   |-- pom.xml
    |   `-- src
    |       |-- main
    |       |   `-- java
    |       |       `-- org
    |       |           `-- myorg
    |       |               `-- basicejb
    |       |                   `-- ejb
    |       |                       |-- ReservationEJB.java
    |       |                       |-- ReservationLocal.java
    |       |                       `-- ReservationRemote.java
    |       `-- test
    |           |-- java
    |           |   `-- org
    |           |       `-- myorg
    |           |           `-- basicejb
    |           |               `-- ejb
    |           |                   `-- ReservationTest.java
    |           `-- resources
    |               `-- log4j.xml
    |-- basicejb-test
    |   |-- pom.xml
    |   `-- src
    |       `-- test
    |           |-- java
    |           |   `-- org
    |           |       `-- myorg
    |           |           `-- basicejb
    |           |               `-- earejb
    |           |                   `-- ReservationIT.java
    |           `-- resources
    |               |-- jboss-ejb-client.properties
    |               |-- jndi.properties
    |               `-- log4j.xml
    `-- pom.xml