Enterprise Java Development@TOPIC@
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.
Create remaining Maven modules to support EAR deployment
Deploy an EJB
Create an IT test to communicate with and test the EAR-based EJB
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.
Create the sub-project directory for the EAR.
$ mkdir basicejb-ear
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>
It is important to note that the packaging type is "ear" in this case. If you leave this out, Maven will default to a standard "jar" packaging type and not build the EAR correctly.
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>
Since our EJB pom declared the dependency on slf4j-api as scope=provided the above exclusion is not necessary but included as an example of how this can be done.
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
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 ...
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>
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
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.
Create the sub-project directory for the RMI Test.
$ mkdir basicejb-test
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>
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>
Like before, the maven pom.xml will not validate until we populate the parent pom with version information for the new dependencies added. Luckily we added these dependencyManagement declaration while adding the unit test within the EJB module.
Notice we will silently also inherit the maven-compiler-plugin definition from the parent. We don't have to repeat any work to get a properly configured compiler.
This begins to show how work we do at the parent pom.xml can be used to keep child modules consistent and allow child modules the flexibility to determine whether they should or should not include a particular dependency.
Add the dependencies required to be an RMI client of JBoss/Wildfly. The dependencies required and how we define them have varied over the years -- so I created a module ("info.ejava.examples.common:jboss-rmi-client") within the course examples as a single entry point.
<!-- dependencies used for remote interface -->
<dependency>
<groupId>info.ejava.examples.common</groupId>
<artifactId>jboss-rmi-client</artifactId>
<type>pom</type>
<scope>test</scope>
</dependency>
The dependency added above is just a "pom" dependency. However, it will bring in dependencies that bring in other dependencies related to a remote EJB and JMS client of JBoss
Recent JBoss/Wildfly packaging has made my use of jboss-rmi-client across the course examples ]less valuable than in the past. If you take a look at the implementation of that module, you will see it simply creates a dependency on org.wildfly.bom:ejb-client-bom and org.wildfly.bom:jms-client-bom. You can reduce your dependencies by declaring a direct dependency on org.wildfly.bom:ejb-client-bom in this case.
Add a definition of the above dependency to your parent pom
<properties>
...
<ejava.version>5.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>
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:5.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:5.0.0-SNAPSHOT -> [Help 1]
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/5.0.0-SNAPSHOT/maven-metadata.xml Downloaded: http://webdev.jhuep.com/~jcs/maven2-snapshot/info/ejava/examples/common/jboss-rmi-client/5.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/5.0.0-SNAPSHOT/jboss-rmi-client-5.0.0-20141001.053140-16.pom Downloaded: http://webdev.jhuep.com/~jcs/maven2-snapshot/info/ejava/examples/common/jboss-rmi-client/5.0.0-SNAPSHOT/jboss-rmi-client-5.0.0-20141001.053140-16.pom (5 KB at 77.3 KB/sec) ... [INFO] BUILD SUCCESS
Maven keeps track of which repositories it has checked for a resource and when so that it can throttle attempts to resolve artifacts during a normal build. Note that in the repository definition we created -- we set the updatePolicy to daily. If you make an error and wish to coldstart Maven's knowledge of that artifact simply delete its directory from the localRepository. You can also try adding the -U (update snapshots) flag to the command line.
$ rm -rf $HOME/.m2/repository/info/ejava/examples/common/jboss-rmi-client/5.0.0-SNAPSHOT
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 java.naming.factory.initial=${java.naming.factory.initial} java.naming.factory.url.pkgs=${java.naming.factory.url.pkgs} java.naming.provider.url=${java.naming.provider.url} #java.naming.security.principal=${jndi.user} #java.naming.security.credentials=${jndi.password}
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 java.naming.factory.initial=${java.naming.factory.initial} java.naming.factory.url.pkgs=${java.naming.factory.url.pkgs} java.naming.provider.url=${java.naming.provider.url} #java.naming.security.principal=${jndi.user} #java.naming.security.credentials=${jndi.password}
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>
The above definition of filtering restricts filtering to a specific pattern of files and leaving all other files unfiltered. This is more verbose but suggested. Filtering some files that were not meant to be filtered causes issues. Binary files (e.g., PKI certs) and variables meant to be expanded in the deployment environment do not work well with filtering.
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 java.naming.factory.initial=${java.naming.factory.initial} java.naming.factory.url.pkgs=${java.naming.factory.url.pkgs} java.naming.provider.url=${java.naming.provider.url} #java.naming.security.principal=${jndi.user} #java.naming.security.credentials=${jndi.password}
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>
<jjava.naming.factory.initial>org.jboss.naming.remote.client.InitialContextFactory</java.naming.factory.initial>
<java.naming.provider.url>http-remoting://${jboss.host}:${jboss.http.port}</java.naming.provider.url>
<java.naming.factory.url.pkgs/>
</properties>
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
java.naming.factory.initial=org.wildfly.naming.client.WildFlyInitialContextFactory
java.naming.factory.url.pkgs=
java.naming.provider.url=http-remoting://127.0.0.1:8080
#java.naming.security.principal=known
#java.naming.security.credentials=password1!
java.naming.factory.initial - an implementation class for the InitialContext() created by the Java code. The WildFlyInitialContextFactory will be able to easily switch between JBoss Remoting and EJB Client JNDI names.
java.naming.factory.url.pkgs= - a list of java packages to search in the classpath to resolve a well-known-named class handler for custom name prefixes (e.g., ejb:). However, since we are using a custom WildFlyInitialContextFactory factory.initial there is no need to list anything here to resolve the "http-remoting" protocol and the "ejb:" naming prefix.
java.naming.provider.url - URL to the JNDI provider. JBoss uses the HTTP port and protocol to initiate communications for JNDI lookups.
java.naming.security.principal and java.naming.security.credentials - credentials to use when interacting with the server. In some cases we have the server locked down so that JNDI lookups require credentials prior to even getting to the application. If your Java code does not supply credentials -- this can be used to authenticate with the server.
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 ***");
}
}
The above JUnit test has been purposely ended with the capital letters "IT" to represent integration test. This will be treated special from JUnit test cases ending with *Test. IT tests run during a later set of phases that account for deployment, testing, undeploy, and results verification in separate phases instead of the single test phase used by *Test test cases. *Test JUnit test cases are used for in-process unit tests. *IT test cases are used for integration tests that could span multiple processes requiring extra work. Unit tests are handled by the maven-surefire-plugin. Integration tests are handled by the maven-failsafe-plugin. More in a moment...
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>
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
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>
Define the failsafe plugin in the parent module. This definition will have...
version for the plugin
goals to execute for the plugin. Other than "help", these are the only two goals the plugin supports.
pre-integration-test - no plugin goals are bound to this build phase yet. This is where we will want to deploy the EAR.
integration-test failsafe goal is automatically bound to the integration-test build phase to run the IT tests.
post-integration-test - no plugin goals are bound to this build phase yet. This is where we will want to undeploy the EAR.
verify goal is automatically bound to the verify goal to evaluate the IT test results and potentially fail the build.
argLine definition that will permit remote debugging of the IT test if needed to be run within the full Maven lifecycle.
<properties>
...
<maven-failsafe-plugin.version>2.22.0</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>
The debugger profile is one we have added before for the surefire plugin. Activating this profile during the build will cause failsafe to suspend until a debugger client connects and commands to continue. This is only useful when you must run the IT test within the full Maven build. Ideally you would instead debug the IT test inside the IDE debugger.
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.22.0: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.22.0: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
The IT test is not yet doing enough to indicate whether the server is running or not.
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");
}
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.22.0:verify (default) @ basicejb-test --- ... [INFO] BUILD FAILURE
Re-run the IT test with the server running.
$ cd basicejb-test; mvn clean install ... [INFO] BUILD SUCCESS
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.
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>
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>
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>
...
<cargo-maven2-plugin.version>1.4.3</cargo-maven2-plugin.version>
<cargo.containerId>wildfly9x</cargo.containerId>
<wildfly-controller-client.version>8.2.1.Final</wildfly-controller-client.version>
<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-controller-client.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>
I have not had time to investigate updating cargo to the latest configurations for wildfly. This is mostly because the older version (and containerId) still work with the newer software and a quick upgrade of cargo version# was not immediately successful.
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.22.0: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.22.0: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
When you application does not correctly deploy, the most valuable information is typically in the server.log and not in the cargo client log. Applications usually fail to deploy because of a missing or mis-configured dependency/resource and the server.log will be necessary to determine what to correct.
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
java: - JNDI naming prefix used to determine which implementation is used to lookup the name. This specific prefix is for local names.
jboss/exported/ - names below this context are available outside the server and exclude this portion of the name.
basicejb-ear-1.0-SNAPSHOT - name of the deployable artifact. In this case the EAR was deployed and the name included the maven full artifact and version name.
basicejb-ejb-1.0-SNAPSHOT - name of the EJB component. It too has its full artifact name and version number applied by maven.
ReservationEJB! - name of the EJB. If not changed by the @Stateless annotation or deployment descriptor -- this will be the same name as the POJO class name.
org.myorg.basicejb.ejb.ReservationRemote - fully qualified class name of the remote interface.
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>ejb:basicejb-ear-${project.version}/basicejb-ejb-${project.version}/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</jndi.name.reservation>
</systemPropertyVariables>
</configuration>
</plugin>
The more EJB-aware and efficient EJB Client is used for communications with our EJB when we prefix the JNDI name with "ejb:". However, leaving it off will work but we would be using JBoss Remoting. We will use JBoss Remoting for non-EJB communications like JMS. For now and always get familiar to always prefixing EJB JNDI names with "ejb:".
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>
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);
}
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);
}
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();
}
}
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.22.0: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.22.0:verify (default) @ basicejb-test --- ... [INFO] BUILD SUCCESS
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() ***
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);
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
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>
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
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);
Your build should now be working again.
$ mvn clean install
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 | |-- jndi.properties | `-- log4j.xml `-- pom.xml
Created an EAR to deploy the EJB
EAR is a packaging construct with no executable code
EARs can deploy EJBs, WARs, and library JARs
Everything within the same EAR share a common classloader and can pass data by reference using local interfaces
Deployed an EAR
Cargo plugin used to automate deploy/undeploy of EAR during build of RMI Test module
deploy/undeploy of EAR occured during pre-integration-test and post-integration-test phases
Looked up @Remote interface in JNDI
Updated JNDI to use EJB-specific EJBClient