Basic EJB Development Exercise Developing and Deploying a

Transcription

Basic EJB Development Exercise Developing and Deploying a
Basic EJB Development Exercise
Developing and Deploying a Basic EJB
Revision: v2015-10-12
Built on: 2015-11-18 02:43 EST
Copyright © 2015 jim stafford ([email protected])
This document provides an exercise geared at developing and deploying your first, simple Session
EJB.
Purpose ............................................................................................................................ v
1. Goals .................................................................................................................... v
2. Objectives ............................................................................................................. v
1. Multi-Module JavaEE Project ....................................................................................... 1
1.1. Purpose ............................................................................................................. 1
1.1.1. Goals ...................................................................................................... 1
1.1.2. Objectives ................................................................................................ 1
1.2. Create Root Module ............................................................................................ 1
1.3. Create EJB Module ............................................................................................ 3
1.4. Manage Application Server ................................................................................ 13
1.4.1. Application Server Setup ........................................................................ 14
1.4.2. Standalone Application Server ................................................................ 14
1.4.3. Embedded Application Server ................................................................. 15
1.5. Summary .......................................................................................................... 17
2. EAR Deployment ....................................................................................................... 19
2.1. Purpose ............................................................................................................ 19
2.1.1. Goals ..................................................................................................... 19
2.1.2. Objectives ..............................................................................................
2.2. Create EAR Module ..........................................................................................
2.3. Create RMI Test Module ...................................................................................
2.4. Deploy the EAR ................................................................................................
2.5. Lookup and Invoke @Remote Interface ..............................................................
2.6. EJB Client ........................................................................................................
2.7. Summary ..........................................................................................................
3. WAR Deployment .......................................................................................................
3.1. Purpose ............................................................................................................
3.1.1. Goals .....................................................................................................
3.1.2. Objectives ..............................................................................................
3.2. Create WAR Module .........................................................................................
3.3. Add RMI Test ...................................................................................................
3.4. Embed EJB in WAR Module ..............................................................................
3.5. Summary ..........................................................................................................
4. Build Commands .......................................................................................................
4.1. Purpose ............................................................................................................
4.1.1. Goals .....................................................................................................
4.1.2. Objectives ..............................................................................................
4.2. mvn (phase) .....................................................................................................
4.3. mvn (phase) -rf :module ....................................................................................
4.4. mvn (phase) -f (path to module) ........................................................................
4.5. mvn clean -Pundeploy .......................................................................................
4.6. mvn clean -Dit.test=fully.qualified.ITPath#testMethod ...........................................
4.7. Summary ..........................................................................................................
5. Controlling JNDI Names ............................................................................................
5.1. Purpose ............................................................................................................
19
19
22
35
38
43
47
49
49
49
49
49
55
62
70
73
73
73
73
73
74
74
75
76
77
79
79
iii
Basic EJB Development Exercise
5.1.1. Goals ..................................................................................................... 79
5.1.2. Objectives .............................................................................................. 79
5.2. Eliminate Version# from EAR-based JNDI Name ................................................
5.3. Eliminate Version# from WAR-based JNDI Name ...............................................
5.4. Summary ..........................................................................................................
6. Debug Remote EJB ...................................................................................................
6.1. Purpose ............................................................................................................
6.1.1. Goals .....................................................................................................
6.1.2. Objectives ..............................................................................................
6.2. Running IT Tests in IDE ....................................................................................
6.3. Debugging Deployment to Standalone Server .....................................................
6.4. Debugging Deployment to Embedded Server ......................................................
6.5. Summary ..........................................................................................................
7. EJB Parent POM ........................................................................................................
7.1. Purpose ............................................................................................................
7.1.1. Goals .....................................................................................................
7.1.2. Objectives ..............................................................................................
80
84
86
89
89
89
89
89
95
97
97
99
99
99
99
7.2. Create Root POM ............................................................................................. 99
7.3. Summary ........................................................................................................ 106
iv
Purpose
1. Goals
•
•
•
•
•
Create and deploy a basic EJB (without resource requirements or other dependencies)
Show different deployment options
Show how to access the EJB
Create an integration test for the EJB
Enhance development skills
2. Objectives
At the completion of this topic, the student shall
• be able to:
• Create an EJB module
• Create a simple @Stateless EJB in the EJB module
• Add required and key aspects to the EJB
•
•
•
•
•
Deploy the EJB to the server using an EAR
Deploy the EJB to the server using a WAR
Integration test the EJB deployed to the server
Create deterministic JNDI names
Debug remote EJB code running on the server
v
vi
Chapter 1.
Multi-Module JavaEE Project
1.1. Purpose
1.1.1. Goals
• Create Maven modules to house different artifacts
• Create an EJB
• Ready application server
1.1.2. Objectives
At the completion of this topic, the student shall
• Create a root Maven project to house modules
• Create child Maven EJB module
• Manage start/stop of application server
1.2. Create Root Module
Each component of the Java EE application will be developed as as a separate Maven module.
Each module will be placed in a flat structure under a common parent project. This parent project
will be used to coordinate goals involved of the entire application. The order in which it builds things
can be influenced by the configuration you supply, however, maven will analyze dependencies at
build time and either honor the actual dependency ordering or fail if you have expressed a circular
dependency.
1. Create a root directory for the exercise. This will host the root project and sub-projects for the
Impl, EJB, WAR, EAR, and RMI Test modules.
$ mkdir ejb-basichotel
2. Create a root project pom.xml file. This project will will not have an associated artifact and is
termed a "parent" (simple term) or "reactor" (techy term) project. It uses a special packaging
type called "pom".
This project will also be used as a parent project of all implementation modules so we can
define re-usable definitions. Note that the definitions within this project are passive. They will not
actively add any dependencies or plugins to inheriting child modules unless the child specifically
references the defined artifact. Details for the potentially used artifact can be placed here (and
consistently reused) and briefly referenced in the child poms or inherently referenced by the
child modules packaging type (i.e., packaging=jar projects automatically bring in the mavencompiler-plugin)
<?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"
1
Chapter 1. Multi-Module JavaE...
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>myorg.basicejb</groupId>
<artifactId>basicejbEx</artifactId>
<packaging>pom</packaging>
<name>Basic EJB Exercise</name>
<version>1.0-SNAPSHOT</version>
<description>
This project is the root project for the example Java EE
Application.
</description>
<modules>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
</repositories>
<pluginRepositories>
</pluginRepositories>
<dependencyManagement>
<dependencies>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
</plugins>
</pluginManagement>
</build>
<profiles>
</profiles>
</project>
Note
It is very important that you make the packaging type is "pom" in this case. If
you leave out this specification, Maven will default to packaging=jar type and
attempt to build a Java-based artifact and ignore its responsibility in this project
to be the root project to delegate to the child projects that build artifacts.
3. Add in the maven-compiler-plugin specification to the parent pom to make sure JDK 1.7 or
above is used and we use an up to date version of the plugin.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source.version>1.8</java.source.version>
<java.target.version>1.8</java.target.version>
2
Create EJB Module
<maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
</properties>
...
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.source.version}</source>
<target>${java.target.version}</target>
</configuration>
</plugin>
4. Test your root project by building it at this time.
$mvn clean install
...
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------------------------------------------------------[INFO] Building Basic EJB Exercise 1.0-SNAPSHOT
[INFO] -----------------------------------------------------------------------[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ basicejbEx --[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ basicejbEx --[INFO]
Installing
/home/jcstaff/proj/basicejbEx/pom.xml
to
/home/jcstaff/.m2/repository2/myorg/basicejb/
basicejbEx/1.0-SNAPSHOT/basicejbEx-1.0-SNAPSHOT.pom
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] -----------------------------------------------------------------------[INFO] Total time: 0.682 s
[INFO] Finished at: 2014-10-04T17:45:26-04:00
[INFO] Final Memory: 7M/105M
[INFO] ------------------------------------------------------------------------
1.3. Create EJB Module
The EJB module will be used to develop one of the EJB components. The term "EJB" gets a
little overloaded at times. There are EJB classes, EJB components, and an EJB tier. EJB classes
break out into the business-remote (aka @Remote) and business-local (aka @Local) interfaces,
the EJB implementation class (either @Stateless, @Stateful, @Singleton, or @MessageDriven),
and support classes. It is common to think of each cohesive pairing of @Remote, @Local, and
implementation class as "an EJB". You can have many EJBs (the sets of classes) within an EJB
component. An EJB component is a materialized as a .jar and there can be many EJB components
within your EJB tier. For this exercise we will have only one EJB component and start out with
only one EJB. A second EJB will be added to the single EJB component in a later excercise to
help support testing. A single Maven project can build a single EJB component.
1. Add an EJB module directory to your project tree.
3
Chapter 1. Multi-Module JavaE...
$ mkdir basicejb-ejb
2. Add the outer shell of the EJB module's 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>
<artifactId>basicejbEx</artifactId>
<groupId>myorg.basicejb</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>basicejb-ejb</artifactId>
<packaging>ejb</packaging>
<name>Basic EJB Exercise::EJB</name>
<description>
This project provides example usages of an EJB tier.
</description>
<dependencies>
</dependencies>
<build>
<plugins>
</plugins>
</build>
</project>
Note
It is important to note that the packaging type is "ejb" in this case. If you leave
out the packaging type, Maven will default to "jar" and not handle your module
appropriately within the context of a Java EE application.
3. Add in the maven-ejb-plugin definition to the *parent* pom.xml with all properties and constructs
that would be common across all EJB modules.
• Tell maven explicitly to use EJB 3.x. Anything 3.0 and above should be fine. The plugin
defaults to EJB 2.1 which requires a descriptor in META-INF/ejb-jar.xml. We won't be using
a descriptor until later.
• Tell maven to add all dependencies that have scope=compile to the Class-Path in the METAINF/MANIFEST.MF. This is a pure Java construct that JavaEE takes advantage of in order
to resolve dependencies on the server.
# basicejbEx/pom.xml
<properties>
...
4
Create EJB Module
<maven-ejb-plugin.version>2.3</maven-ejb-plugin.version>
</properties>
<pluginManagement>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<version>${maven-ejb-plugin.version}</version>
<configuration>
<ejbVersion>3.2</ejbVersion>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
4. Add in the maven-ejb-plugin declaration to the EJB/pom.xml. This will contain portions of the
plugin definition that could be unique per EJB module.
• Tell the plugin to create a ejb-client.jar file for remote clients. This will be populated
using a set of include and exclude paths. In this case, we are telling it not to include
any of our deployment descriptors in the META-INF directory as well as leaving out our
EJB implemenation class. This will produce an extra jar in the target directory called
${project.artifactId}-${project.version}-client.jar and can be brought in with a dependency on
the EJB module using a type element set to "ejb-client". We will do this in the RMI Test
module.
<!-- tell the EJB plugin to build a client-jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<configuration>
<generateClient>true</generateClient>
<clientExcludes>
<clientExclude>**/META-INF/*.xml</clientExclude>
<clientExclude>**/ejb/*EJB.class</clientExclude>
</clientExcludes>
</configuration>
</plugin>
</plugins>
</build>
Note
Since the packaging type is "ejb" for this module, the maven-ejb-plugin is
brought in (according to our pluginManagement definition) automatically. We
are just extending the definition to include the ejb-client. If the plugin was
not automatically brought in because of the packaging type -- the above
5
Chapter 1. Multi-Module JavaE...
specification in the build.plugins section of the EJB/pom.xml would have been
enough to activate the plugin.
5. Add several dependencies to the EJB/pom.xml account for use of logging, annotations, and
EJB constructs. Since we will be instantiating this code only on the server and not in a 2-tier
approach, the pure JavaEE API from Sun/Oracle will do fine. Otherwise we should use the
dependencies from Hibernate/JBoss.
# basicejbEx/basicejb-ejb/pom.xml
<dependencies>
<!-- core implementation dependencies -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>javax.ejb-api</artifactId>
<scope>provided</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>
</dependencies>
Note
You should always declare a scope=provided dependency on the JavaEE API
artifacts so that downstream clients of the module are free to supply their own
version/provider of the API.
6. Attempt to validate the EJB module at the child level without futher specification. This will fail
because we are missing version information for the two dependency artifacts we just added.
$ mvn validate
[ERROR]
The project myorg.basicejb:basicejb-ejb:1.0-SNAPSHOT (/home/jcstaff/proj/basicejbEx/basicejb-ejb/
pom.xml) has 5 errors
6
Create EJB Module
[ERROR]
'dependencies.dependency.version' for org.slf4j:slf4j-api:jar is missing. @ myorg.basicejb:basicejb-ejb:
[unknown-version], .../basicejbEx/basicejb-ejb/pom.xml, line 22, column 21
[ERROR] 'dependencies.dependency.version' for javax.ejb:javax.ejb-api:jar is missing. @ myorg.basicejb:basicejbejb:[unknown-version], .../basicejbEx/basicejb-ejb/pom.xml, line 27, column 21
[ERROR] 'dependencies.dependency.version' for junit:junit:jar is missing. @ myorg.basicejb:basicejb-ejb:[unknownversion], .../basicejbEx/basicejb-ejb/pom.xml, line 34, column 21
[ERROR]
'dependencies.dependency.version' for org.slf4j:slf4j-log4j12:jar is missing. @ myorg.basicejb:basicejbejb:[unknown-version], .../basicejbEx/basicejb-ejb/pom.xml, line 39, column 21
[ERROR]
'dependencies.dependency.version' for log4j:log4j:jar is missing. @ myorg.basicejb:basicejb-ejb:
[unknown-version], .../basicejbEx/basicejb-ejb/pom.xml, line 44, column 21
7. Update the parent pom.xml with the version specifications for these artifacts.
# basicejbEx/pom.xml
<properties>
...
<javax.ejb-api.version>3.2</javax.ejb-api.version>
<junit.version>4.11</junit.version>
<log4j.version>1.2.13</log4j.version>
<slf4j.version>1.7.2</slf4j.version>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>javax.ejb-api</artifactId>
<version>${javax.ejb-api.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
8. Validate that maven can now resolve the new dependencies.
$ mvn validate
7
Chapter 1. Multi-Module JavaE...
[INFO] BUILD SUCCESS
Note
We are using only the maven validate phase at this point because we only want
to validate whether maven can resolve all artifacts and not fully build an EJB
component. The build will fail if you use a more advanced maven phase prior to
adding the EJB source code in the following steps.
Tip
If you have not yet done so, you can now import your multi-module project into
the IDE as an existing set of Maven projects. Since we have not yet linked the
two -- you will need to import them in separate steps. Although the instructions
are written using file system commands it is assumed that you can translate
them into IDE actions and work at the level you desire.
9. Create the src tree for the EJB.
$ mkdir mkdir -p basicejb-ejb/src/main/java/org/myorg/basicejb/ejb
10.Add @Remote and @Local interfaces for the EJB. Place a simple ping() method in the
@Remote interface for use as an end-to-end sanity check at the end of this exercise.
$ cat basicejb-ejb/src/main/java/org/myorg/basicejb/ejb/ReservationRemote.java
package org.myorg.basicejb.ejb;
import javax.ejb.Remote;
@Remote
public interface ReservationRemote {
void ping();
}
$ cat basicejb-ejb/src/main/java/org/myorg/basicejb/ejb/ReservationLocal.java
package org.myorg.basicejb.ejb;
import javax.ejb.Local;
@Local
public interface ReservationLocal {
}
11.Add a @Stateless EJB that will implement the provided @Remote and @Local interfaces.
Implement @PostConstruct and @Destroy callbacks to intercept EJB lifecycle events. Add a
logger and log statements to the methods so we can observe activity within the EJB during the
test at the end of this exercise.
$ cat basicejb-ejb/src/main/java/org/myorg/basicejb/ejb/ReservationEJB.java
package org.myorg.basicejb.ejb;
8
Create EJB Module
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Stateless;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Stateless
public class ReservationEJB implements ReservationLocal, ReservationRemote {
private static Logger logger = LoggerFactory.getLogger(ReservationEJB.class);
@PostConstruct
public void init() {
logger.debug("*** ReservationEJB.init() ***");
}
@PreDestroy
public void destroy() {
logger.debug("*** ReservationEJB.destroy() ***");
}
@Override
public void ping() {
logger.debug("ping called");
}
}
12.The EJB can be built at this time. You will notice the following in the output.
• Our 3 Java files (@Remote, @Local, and @Stateless) were compiled
• EJB 3 processing was performed on the target. This largely consisted only of MANIFEST
Class-Path processing and the construction of an ejb-client.jar file at this point.
•
$ mvn package
[INFO] -----------------------------------------------------------------------[INFO] Building Basic EJB Exercise::EJB 1.0-SNAPSHOT
[INFO] -----------------------------------------------------------------------...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ basicejb-ejb --...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ basicejb-ejb --[INFO] Changes detected - recompiling the module!
[INFO] Compiling 3 source files to /home/jcstaff/proj/basicejbEx/basicejb-ejb/target/classes
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ basicejb-ejb --...
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ basicejb-ejb --...
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ basicejb-ejb --...
[INFO] --- maven-ejb-plugin:2.4:ejb (default-ejb) @ basicejb-ejb --[INFO] Building EJB basicejb-ejb-1.0-SNAPSHOT with EJB version 3.2
[INFO] Building jar: .../basicejbEx/basicejb-ejb/target/basicejb-ejb-1.0-SNAPSHOT.jar
[INFO] Building EJB client basicejb-ejb-1.0-SNAPSHOT-client
9
Chapter 1. Multi-Module JavaE...
[INFO] Building jar: .../basicejbEx/basicejb-ejb/target/basicejb-ejb-1.0-SNAPSHOT-client.jar
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
Module structure looks very similar to a POJO/JAR module
.
|-- basicejb-ejb
| |-- pom.xml
| |-- src
| | `-- main
| |
`-- java
| |
`-- org
| |
`-- myorg
| |
`-- basicejb
| |
`-- ejb
| |
|-- ReservationEJB.java
| |
|-- ReservationLocal.java
| |
`-- ReservationRemote.java
| `-- target
|
|-- basicejb-ejb-1.0-SNAPSHOT-client.jar
|
|-- basicejb-ejb-1.0-SNAPSHOT.jar
...
`-- pom.xml
The EJB.jar contains the full set of EJB classes/interfaces
$ jar tf target/basicejb-ejb-1.0-SNAPSHOT.jar
...
org/myorg/basicejb/ejb/ReservationLocal.class
org/myorg/basicejb/ejb/ReservationRemote.class
org/myorg/basicejb/ejb/ReservationEJB.class
...
The EJB-client.jar contains classes/interfaces required by remote clients
$ jar tf target/basicejb-ejb-1.0-SNAPSHOT-client.jar
...
org/myorg/basicejb/ejb/ReservationLocal.class
org/myorg/basicejb/ejb/ReservationRemote.class
...
Note
Technically, Local interfaces should not be needed by remote clients. However,
there was a time when remote clients of JBoss Stateful Session EJBs required
access to all interface types and old assembly habits die hard when it does not
hurt to include the extra interface if not needed.
13.Add the EJB module to the root pom.xml.
<modules>
10
Create EJB Module
<module>basicejb-ejb</module>
</modules>
14.Retest the build from the root.
[INFO] Scanning for projects...
...
[INFO] -----------------------------------------------------------------------[INFO] Reactor Summary:
[INFO]
[INFO] Basic EJB Exercise ................................. SUCCESS [ 0.503 s]
[INFO] Basic EJB Exercise::EJB ............................ SUCCESS [ 2.457 s]
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
15.Add a unit test to the EJB module to show that you can unit test functionality that does not
require the container. This class will go into the src/test tree and will not be a part of the EJB.jar
$ mkdir -p basicejb-ejb/src/test/java/org/myorg/basicejb/ejb
$ cat basicejb-ejb/src/test/java/org/myorg/basicejb/ejb/ReservationTest.java
package org.myorg.basicejb.ejb;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReservationTest {
private static final Logger logger = LoggerFactory.getLogger(ReservationTest.class);
ReservationRemote reservatist;
@Before
public void setUp() {
reservatist=new ReservationEJB();
}
@Test
public void testPing() {
logger.info("*** testPing ***");
reservatist.ping();
}
}
16.Add a log4j.xml file to support logging unit test functionality that does not require the container.
$ mkdir -p basicejb-ejb/src/test/resources
$ cat basicejb-ejb/src/test/resources/log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC
11
Chapter 1. Multi-Module JavaE...
"-//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>
17.Rebuild the EJB and/or entire application. You will notice the unit tests executed during the
build.
$ mvn clean install
...
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ basicejb-ejb --[INFO] Surefire report directory: /home/jcstaff/proj/basicejbEx/basicejb-ejb/target/surefire-reports
------------------------------------------------------TESTS
------------------------------------------------------Running org.myorg.basicejb.ejb.ReservationTest
21:50:10,382 INFO (ReservationTest.java:20) -*** testPing ***
21:50:10,390 DEBUG (ReservationEJB.java:26) -ping called
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.218 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
...
12
Manage Application Server
[INFO] BUILD SUCCESS
Note
Notice the we are automatically getting a surefire execution and our JUnit test
run. We get this because surefire is automatically brought in by the pom's
packaging type *and* we ended the Java class with Test. Looking at the
plugin page [http://maven.apache.org/surefire/maven-surefire-plugin/examples/
inclusion-exclusion.html], the other default name patterns include.
• **/Test*.java
• **/*Test.java
• **/*TestCase.java
As discussed on that same web page -- you can expand or shrink that list with
the use of includes and excludes. This is commonly done to focus your testing
around a specific unit test or to temporarily exclude all unit tests from the build
to focus on a specific IT test (discussed later).
18.Verify this is what you have so far.
.
|-- 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
1.4. Manage Application Server
This set of steps is in preparation for the next chapter where you will be asked to deploy the EJB
built above to the server. In this section you will be shown how to start/stop a standalone server
and how to setup, start/stop an embedded server.
13
Chapter 1. Multi-Module JavaE...
1.4.1. Application Server Setup
1. Depending on the origin of your server setup, either verify or put in place the following in the
JBoss/Wildfly application server configuration file to address logging.
# wildfly-8.1.0.Final/standalone/configuration/standalone.xml
<logger category="org.myorg">
<level name="DEBUG"/>
</logger>
...
<root-logger>
<level name="INFO"/>
<handlers>
<handler name="CONSOLE"/>
<handler name="FILE"/>
</handlers>
</root-logger>
The default configuration will permit all log entries INFO and above to be written to the
CONSOLE and FILE appenders. The extra lines added for org.myorg defined a new logger that
will have messages DEBUG and above logged. The CONSOLE appender is normally throttled
to only log INFO and above so you should not see any impact of the changes above in the
server's console output. The extra debug will be placed in the standalone/log/server.log file.
1.4.2. Standalone Application Server
The standalone application server runs as a separate process either in the same machine as or
remote machine from the development machine. This approach more closely resembles the target
server setup and can help test certain production-like configurations. This is normally managed
through scripts.
1. Start the JBoss/Wildfly application server. In the command shown below
• -Djboss.server.base.dir - used to point to the root of the profile. If you create other profiles
(by copying the standalone directory) you can point the script to that directory using this java
property flag. This flag is not needed if you use the default standalone directory.
• -c standalone.xml - used to point to a specific profile configuration file within the
${jboss.server.base.dir}/configuration directory. If you create alternate configurations (by
copying the standalone.xml file) you can point the script to that configuration using this
argument. This is useful to switch between alternate configurations but not required if you
use the default standalone.xml configuration file.
wildfly-8.1.0.Final]$ ./bin/standalone.sh
=========================================================================
JBoss Bootstrap Environment
JBOSS_HOME: /opt/wildfly-8.1.0.Final
JAVA: /usr/lib/jvm/java-1.7.0/bin/java
14
Embedded Application Server
JAVA_OPTS:
-server
-Xms64m
-Xmx512m
-XX:MaxPermSize=256m
Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true
agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n
-
=========================================================================
Listening for transport dt_socket at address: 8787
01:33:57,008 INFO [org.jboss.modules] (main) JBoss Modules version 1.3.3.Final
01:33:57,508 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.2.Final
01:33:57,970 INFO [org.jboss.as] (MSC service thread 1-1) JBAS015899: WildFly 8.1.0.Final "Kenny" starting
...
01:34:56,423 INFO [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.1.0.Final "Kenny" started in
60065ms - Started 309 of 362 services (113 services are lazy, passive or on-demand)
2. Shutdown the server using the command line interface (CLI)
$ /opt/wildfly-8.1.0.Final/bin/jboss-cli.sh --connect command=:shutdown
{"outcome" => "success"}
3. Restart the server.
wildfly-8.1.0.Final]$ ./bin/standalone.sh
...
4. Shutdown the server by pressing Control-C keys
01:39:20,291 INFO [org.jboss.as.messaging] (MSC service thread 1-4) JBAS011601: Bound messaging object to jndi
name java:jboss/DefaultJMSConnectionFactory
^C 01:39:32,666 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) JBAS017532: Host default-host
stopping
01:39:32,666 INFO [org.jboss.as.messaging] (ServerService Thread Pool -- 57) JBAS011605: Unbound messaging
object to jndi name java:jboss/exported/jms/RemoteConnectionFactory
Note
There is no negative impacts from stopping the application server using ControlC key sequence.
1.4.3. Embedded Application Server
The embedded application server places the server in the same JVM as the IDE. This allows for
easy start, stop, and debug options for managing the server. Since the server is running inside
the IDE, it can differ greatly from what is used in production.
Note
The following steps assumes JBoss AS Tools have been installed. If the server
setup does not list a Wildfly 8.x server option please make install that option.
15
Chapter 1. Multi-Module JavaE...
1. Add a new Wildfly 8.x Server off the Servers tab of the JavaEE IDE profile. Accept all defaults.
2. Start the server by right-clicking on the server and selecting Start.
3. Stop the server by right-clicking on the server and selecting Stop.
16
Summary
1.5. Summary
• Created parent module
• Houses re-usable property, dependency and plugin definitions
• Delegates build to implementation modules
• Has no technical artifacts of its own
• Created EJB module
• EJB modules have one or more EJBs
• an "EJB" is made up of an EJB class and optional @Remote and @Local interfaces
• Functional unit tests can be included in the EJB module
• Server management
• Control logging verbosity
• Start/Stop standalone server
• Create and Start/Stop embedded server
17
18
Chapter 2.
EAR Deployment
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 littleused 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.
2.1. Purpose
2.1.1. Goals
• Create remaining Maven modules to support EAR deployment
• Deploy an EJB
• Create an IT test to communicate with and test the EAR-based EJB
2.1.2. Objectives
At the completion of this topic, the student shall
• Create child Maven EAR module
• Create RMI Test module for EAR
• Setup JNDI access to @Remote interface
• Invoke EAR-based EJB method from IT test thru @Remote interface
2.2. Create EAR Module
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>
19
Chapter 2. EAR Deployment
<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>
Note
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.
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>
Note
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.
4. Verify the EAR builds.
$ mvn clean verify
...
[INFO] Building Basic EJB Exercise::EAR 1.0-SNAPSHOT
20
Create EAR Module
...
[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]
21
Chapter 2. EAR Deployment
[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
2.3. Create RMI Test Module
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"?>
22
Create RMI Test Module
<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>
23
Chapter 2. EAR Deployment
Note
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.
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] ------------------------------------------------------------------------
24
Create RMI Test Module
[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-rmiclient 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.0SNAPSHOT/maven-metadata.xml
Downloaded:
http://webdev.jhuep.com/~jcs/maven2-snapshot/info/ejava/examples/common/jboss-rmi-client/4.0.0SNAPSHOT/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.0SNAPSHOT/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.0SNAPSHOT/jboss-rmi-client-4.0.0-20141001.053140-16.pom (5 KB at 77.3 KB/sec)
...
[INFO] BUILD SUCCESS
Note
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/4.0.0-SNAPSHOT
25
Chapter 2. EAR Deployment
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
Note
JBoss/Wildfly provides two types of interfacing with EJBs thru JNDI; JBoss
Remoting and EJB Client. JBoss Remoting is ignorant of the specifics of EJB
and can be used with any resource type. EJB Client is specific to EJB but tuned
to work more efficiently than the general purpose JBoss Remoting. We are using
the general purpose method here so that it can be re-usable for other resources
-- like JMS -- without changing technologies.
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}
26
Create RMI Test Module
#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>
Restrict Filtering to Targeted Files
The above definition of filtering is restricting 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.
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
27
Chapter 2. EAR Deployment
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/testclasses 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
• java.naming.factory.initial - an implementation class for the InitialContext() created by the
Java code. A default javax.naming implementation will be used if this is not supplied or blank.
• 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., http-remoting). However,
since we are using a custom JBoss Remoting factory.initial there is no need to list anything
here to resolve the http-remoting naming prefix.
28
Create RMI Test Module
• java.naming.provider.url - URL to the JNDI provider. JBoss uses the HTTP port and protocol
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.
• jboss.naming.client.ejb.context=true - required for JBoss Remoting to correctly work with
EJBs.
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 ***");
}
}
Note
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
29
Chapter 2. EAR Deployment
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...
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.
30
Create RMI Test Module
$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...
• 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.17</maven-failsafe-plugin.version>
31
Chapter 2. EAR Deployment
...
<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>
Note
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.
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 ---
32
Create RMI Test Module
...
[INFO] --- maven-failsafe-plugin:2.17:integration-test (default) @ basicejb-test --...
------------------------------------------------------TESTS
------------------------------------------------------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=httpremoting://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
Note
The IT test is not yet doing enough to indicate whether the server is running
or not.
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
TESTS
-------------------------------------------------------
33
Chapter 2. EAR Deployment
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
34
Deploy the EAR
2.4. Deploy the EAR
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>
35
Chapter 2. EAR Deployment
...
<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>
36
Deploy the EAR
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.0SNAPSHOT.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
37
Chapter 2. EAR Deployment
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.0SNAPSHOT.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
Tip
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.
2.5. Lookup and Invoke @Remote Interface
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.
38
Lookup and Invoke @Remote Interface
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 {
...
39
Chapter 2. EAR Deployment
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=httpremoting://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.
40
Lookup and Invoke @Remote Interface
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 [http://maven.apache.org/surefire/
maven-surefire-plugin/examples/inclusion-exclusion.html].
$ 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
------------------------------------------------------TESTS
------------------------------------------------------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
41
Chapter 2. EAR Deployment
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 --------------------------------------------------------TESTS
------------------------------------------------------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
42
EJB Client
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:basicejbear-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
2.6. EJB Client
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.
• Modify jndi properties
• Add jboss-ejb-client.properties
• Change the JNDI name
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
...
43
Chapter 2. EAR Deployment
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
44
EJB Client
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
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
|
grep
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.0SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
...
01:39:27,831 INFO (ReservationIT.java:38) -*** testPing ***
45
Chapter 2. EAR Deployment
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
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
46
Summary
|
|
|
|
|
|
|
|
|
|
|
|
`-- basicejb
|
`-- ejb
|
|-- ReservationEJB.java
|
|-- ReservationLocal.java
|
`-- ReservationRemote.java
`-- test
|-- java
| `-- org
|
`-- myorg
`-- 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
2.7. Summary
• 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
• JNDI names are standardized
• Only remote interface exposed outside of server
• Exercise used older JBoss Remoting to access remote thru JNDI lookup
• JBoss Remoting will work for any remote interface (e.g., JMS)
• JBoss recommends using newer EJBClient for access to EJBs
• Updated JNDI to use EJB-specific EJBClient
• Client library is specific to communicating with JBoss EJBs
47
48
Chapter 3.
WAR Deployment
3.1. Purpose
3.1.1. Goals
• Create remaining Maven modules to support WAR deployment
• Deploy an EJB within WAR
• Create and deploy an EJB within WAR
• Create an IT test to communicate with and test the WAR-based EJB
3.1.2. Objectives
At the completion of this topic, the student shall
• Create child Maven WAR module
• Create RMI Test within WAR module
• Setup JNDI access to @Remote interface
• Invoke WAR-based EJB method from IT test thru @Remote interface
3.2. Create WAR Module
WARs are typically used to deploy web-tier components and this WAR may do that at some point.
However, at this point in time we would like to take advantage of the WAR as a deployment artifact
for EJBs. Starting with JavaEE 6, EJBs can be flexibly deployed embedded within the WAR or
similar to an EAR by hosting EJB archives using dependencies.
1. Create the sub-project directory for the WAR.
$ mkdir basicejb-war
2. Add the initial entries for the WAR 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-war</artifactId>
<packaging>war</packaging>
<name>Basic EJB Exercise::WAR</name>
49
Chapter 3. WAR Deployment
<description>
This project provides a sample WAR for the Java EE components
associated with the overall project.
</description>
<dependencies>
</dependencies>
<build>
</build>
</project>
Note
It is important to note that the packaging type is "war" in this case. If you leave
this out, Maven will default to a standard "jar" packaging type and not build a
WAR.
3. Add the EJB dependency to the WAR. 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>
Note
Since our WAR 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.
4. Attempt to build the WAR. It should fail because we have not yet added a WEB-INF/web.xml
or have not configured the plugin to ignore it.
$ mvn clean package
...
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.2:war (default-war) on project
basicejb-war: Error assembling WAR: webxml attribute is required (or
pre-existing WEB-INF/web.xml if executing in update mode)
50
Create WAR Module
Note
J2EE 1.4 and prior relied heavily and exclusively on XML deployment
descriptors for component deployment definitions. Since JavaEE 5, components
have been allowed to be configured by convention and @Annotations to the
point that we sometimes do not need the XML deployment descriptor at all.
5. Add the following property and pluginManagement to your root pom.xml. The plugin definition
allows our WAR to be deployed without a WEB-INF/web.xml deployment descriptor. The
version is required once we explicitly mention the plugin.
<properties>
...
<maven-war-plugin.version>2.4</maven-war-plugin.version>
<build>
<pluginManagement>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
6. Verify the WAR builds.
$ mvn clean package
...
...
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ basicejb-war --...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ basicejb-war --...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ basicejb-war --...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ basicejb-war --...
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ basicejb-war --...
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ basicejb-war --...
[INFO] --- maven-war-plugin:2.4:war (default-war) @ basicejb-war --[INFO] Packaging webapp
[INFO] Assembling webapp [basicejb-war] in [/home/jcstaff/proj/basicejbEx/basicejb-war/target/basicejb-war-1.0SNAPSHOT]
[INFO] Processing war project
[INFO] Webapp assembled in [34 msecs]
[INFO] Building war: /home/jcstaff/proj/basicejbEx/basicejb-war/target/basicejb-war-1.0-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
51
Chapter 3. WAR Deployment
[INFO] BUILD SUCCESS
7. Inspect the generated WAR 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-war-1.0-SNAPSHOT.war
...
WEB-INF/classes/
WEB-INF/lib/basicejb-ejb-1.0-SNAPSHOT.jar
...
8. Add a cargo-maven-plugin declaration to the WAR module to deploy the WAR. Since we are
deploying the local artifact and a WAR is a deployable -- we do not need to specify this artifact
as a deployable.
...
<build>
<plugins>
...
<!-- artifacts to deploy to server. this module by default -->
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
</plugin>
Cargo will Deploy Local Artifact by Default
Since we are deploying the local artifact, we do not need to specify a
dependency or deployable. This can also make also make it a pain to turn off if
our root pom wired cargo into every module build.
This can also make also make it a pain to turn off if on a per-module basis if our
root pom declared cargo and as a result wired it into every deployable module
type build (i.e., all EJB, WAR, and EAR modules). That is one reason why it is
nice to passively define a consistent use of the plugins using pluginManagement
in the root pom and then actively declare them on a per-module basis in the
implementation modules.
9. Verify the WAR module builds, deploys to the server, and undeploys from the server as part
of the build lifecycle.
$ mvn clean verify
...
[INFO] --- maven-war-plugin:2.4:war (default-war) @ basicejb-war --[INFO] Packaging webapp
[INFO] Assembling webapp [basicejb-war] in [/home/jcstaff/proj/basicejbEx/basicejb-war/target/basicejb-war-1.0SNAPSHOT]
[INFO] Processing war project
[INFO] Webapp assembled in [56 msecs]
[INFO] Building war: /home/jcstaff/proj/basicejbEx/basicejb-war/target/basicejb-war-1.0-SNAPSHOT.war
[INFO]
[INFO] --- cargo-maven2-plugin:1.4.3:redeploy (cargo-prep) @ basicejb-war ---
52
Create WAR Module
Oct 11, 2014 2:11:09 AM org.xnio.Xnio <clinit>
INFO: XNIO version 3.2.2.Final
Oct 11, 2014 2:11:09 AM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.2.2.Final
Oct 11, 2014 2:11:09 AM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 4.0.3.Final
...
[INFO] --- cargo-maven2-plugin:1.4.3:undeploy (cargo-post) @ basicejb-war --...
[INFO] BUILD SUCCESS
10.Note the JNDI names printed in the server console and server.log.
02:15:34,284 INFO
[org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC
service thread 1-4)
JNDI bindings for session bean named ReservationEJB in deployment unit deployment "basicejb-war-1.0SNAPSHOT.war" are as follows:
java:global/basicejb-war-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationLocal
java:app/basicejb-war-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationLocal
java:module/ReservationEJB!org.myorg.basicejb.ejb.ReservationLocal
java:global/basicejb-war-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
java:app/basicejb-war-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
java:module/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
java:jboss/exported/basicejb-war-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
02:15:34,309 INFO [org.jboss.weld.deployer] (MSC service thread 1-4) JBAS016005: Starting Services for CDI
deployment: basicejb-war-1.0-SNAPSHOT.war
02:15:34,317 INFO [org.jboss.weld.deployer] (MSC service thread 1-1) JBAS016008: Starting weld service for
deployment basicejb-war-1.0-SNAPSHOT.war
02:15:34,624 INFO [org.wildfly.extension.undertow] (MSC service thread 1-1) JBAS017534: Registered web context: /
basicejb-war-1.0-SNAPSHOT
02:15:34,636 INFO [org.jboss.as.server] (management-handler-thread - 1) JBAS018559: Deployed "basicejb-war-1.0SNAPSHOT.war" (runtime-name : "basicejb-war-1.0-SNAPSHOT.war")
The JNDI names starting with java:jboss/exported are especially important because they are
available to remote clients.
java:jboss/exported/basicejb-war-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
The following will be the base JNDI name of the EJB deployed by the WAR.
/basicejb-war-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
Compare that to the base name used by the EJB deployed by the EAR. Notice that we have no
application name in the WAR-deployed EJB and the module is named after the hosting WAR
and and not the imported EJB.
/basicejb-war-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
53
Chapter 3. WAR Deployment
basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!
org.myorg.basicejb.ejb.ReservationRemote
11.Add the WAR to the *root* level module and verify everything builds from the root.
<modules>
<module>basicejb-ejb</module>
<module>basicejb-ear</module>
<module>basicejb-test</module>
<module>basicejb-war</module>
</modules>
$ mvn clean install -DskipTests
...
[INFO] Reactor Summary:
[INFO]
[INFO] Basic EJB Exercise ................................. SUCCESS [ 0.448 s]
[INFO] Basic EJB Exercise::EJB ............................ SUCCESS [ 2.550 s]
[INFO] Basic EJB Exercise::EAR ............................ SUCCESS [ 0.432 s]
[INFO] Basic EJB Exercise::Remote Test .................... SUCCESS [ 5.934 s]
[INFO] Basic EJB Exercise::WAR ............................ SUCCESS [ 0.458 s]
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
12.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
|-- basicejb-test
| |-- pom.xml
| `-- src
|
`-- test
|
|-- java
|
| `-- org
54
Add RMI Test
|
|
`-- myorg
|
|
`-- basicejb
|
|
`-- earejb
|
|
`-- ReservationIT.java
|
`-- resources
|
|-- jboss-ejb-client.properties
|
|-- jndi.properties
|
`-- log4j.xml
|-- basicejb-war
| `-- pom.xml
`-- pom.xml
3.3. Add RMI Test
One advantage the WAR module has over the EAR module is that it can contain production code,
unit tests, and IT tests. One could argue that is too much to place into a single module but who
wants to be limited in options before we see what mode is best for our application. One could
create the RMI Test for the WAR-deployed EJB in a separate module -- but we already did that
for the EAR and the steps would be pretty much the same. In this section we will implement the
RMI IT test within the WAR itself. This is a reasonable approach for smaller applications.
1. Add the dependencies to the WAR/pom.xml required to use logging and JUnit.
<!-- core dependencies -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</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>
Note
The parent pom.xml should already have a dependencyManagement definition
for these dependencies.
55
Chapter 3. WAR Deployment
Notice we again will silently inherit the maven-compiler-plugin definition from
the parent. We don't have to repeat any work to get a properly configured
compiler. This continues 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.
The dependency on slf4j-api was made scope=provided instead of scope=test
to allow us to use this dependency in the src/main tree when we later add classes
within the WAR module.
2. Add the dependencies required to be an RMI client of JBoss/Wildfly. We can again leverage
the info.ejava.examples.common:jboss-rmi-client dependency to automatically bring these
dependencies in.
<!-- dependencies used for remote interface -->
<dependency>
<groupId>info.ejava.examples.common</groupId>
<artifactId>jboss-rmi-client</artifactId>
<type>pom</type>
<scope>test</scope>
</dependency>
Note
The above dependency on the jboss-rmi-client should be scope=test. If we made
it scope=compile or scope=runtime it and its dependencies would be included
in the WAR. We don't want these in the WAR. We want these dependencies
made available to the RMI Test client left behind.
We should already have a dependencyManagement definition for the jboss-rmiclient module in the parent pom from an earlier chapter when we did this same
action for the EAR-based RMI IT test.
3. Create a JNDI configuration by copying your jndi.properties and jboss-ejb-client.properties from
the EAR-based RMI Test module. Place these files in src/test/resources of the WAR.
$ mkdir -p basicejb-war/src/test/resources
$ cp basicejb-test/src/test/resources/*.properties basicejb-war/src/test/resources/
$ cat basicejb-war/src/test/resources/jndi.properties
#jndi.properties
java.naming.factory.initial=${jboss.remoting.java.naming.factory.initial}
java.naming.factory.url.pkgs=${jboss.ejbclient.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
56
Add RMI Test
$ cat basicejb-war/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 a log4j.xml file to configure Log4j loggers. You may use a copy of the file you put into your
EJB and RMI Test.
$ cp basicejb-test/src/test/resources/log4j.xml basicejb-war/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"/>
57
Chapter 3. WAR Deployment
</root>
</log4j:configuration>
5. Add resource filtering to test resources in the WAR/pom.xml. This will cause the jndi.properties
file to have variables replaced 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>
Restrict Filtering to Targeted Files
As done before in the RMI Test module...the above will filter files that match a
specific name pattern and then copy the remainder of files without filtering. It
is important that you do not accidentally filter a file that was not meant to be
filtered. This can corrupt a binary file or expand a variable during compile time
that was meant to be expanded at runtime.
6. Rebuild just the WAR and verify the two JNDI/EJBClient files were filtered and their variables
properly expanded.
$ mvn clean process-test-resources
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ basicejb-war --[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO] Copying 1 resource
..
[INFO] BUILD SUCCESS
$ cat basicejb-war/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!
58
Add RMI Test
jboss.naming.client.ejb.context=true
$ cat basicejb-war/target/test-classes/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=localhost
remote.connection.default.port=8080
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
#remote.connection.default.username=known
#remote.connection.default.password=password1!
7. Copy your JUnit IT test from the EAR-based RMI Test module and place it in your src/test tree.
Use a new package directory (warejb versus earejb) for the copied IT test.
$ mkdir -p basicejb-war/src/test/java/org/myorg/basicejb/warejb/
$ cp basicejb-test/src/test/java/org/myorg/basicejb/earejb/ReservationIT.java basicejb-war/src/test/java/org/myorg/
basicejb/warejb/ReservationIT.java
Modify the Java package spec to match the new directory. The rest is exactly what we covered
in the EAR deploy section.
package org.myorg.basicejb.warejb; <<<<<<<<<<<<<<<<<<<<<<
import static org.junit.Assert.*;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.junit.Before;
import org.junit.Test;
import org.myorg.basicejb.ejb.ReservationRemote;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReservationIT {
private static final Logger logger = LoggerFactory.getLogger(ReservationIT.class);
private static final String reservationJNDI = System.getProperty("jndi.name.reservation");
private InitialContext jndi;
private ReservationRemote reservationist;
@Before
public void setUp() throws NamingException {
assertNotNull("jndi.name.reservation not supplied", reservationJNDI);
logger.debug("getting jndi initial context");
jndi=new InitialContext();
logger.debug("jndi={}", jndi.getEnvironment());
jndi.lookup("jms");
59
Chapter 3. WAR Deployment
logger.debug("jndi name:{}", reservationJNDI);
reservationist = (ReservationRemote) jndi.lookup(reservationJNDI);
logger.debug("reservationist={}", reservationist);
}
@Test
public void testPing() throws NamingException {
logger.info("*** testPing ***");
reservationist.ping();
}
}
8. Attempt to build at this point. The IT test will be compiled but not run because we have not yet
declared the failsafe plugin in our WAR module to execute the JUnit IT tests.
$ mvn clean verify
...
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ basicejb-war --...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ basicejb-war --...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ basicejb-war --...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ basicejb-war --...
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ basicejb-war --[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /home/jcstaff/proj/basicejbEx/basicejb-war/target/test-classes
...
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ basicejb-war --...
[INFO] --- maven-war-plugin:2.4:war (default-war) @ basicejb-war --...
[INFO] --- cargo-maven2-plugin:1.4.3:redeploy (cargo-prep) @ basicejb-war --...
[INFO] --- cargo-maven2-plugin:1.4.3:undeploy (cargo-post) @ basicejb-war --...
[INFO] BUILD SUCCESS
9. Declare the failsafe plugin to your WAR/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>
<configuration>
<systemPropertyVariables>
<jndi.name.reservation>ejb:/basicejb-war-1.0-SNAPSHOT/
ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</jndi.name.reservation>
</systemPropertyVariables>
</configuration>
</plugin>
60
Add RMI Test
Remember to pass a jndi.name.reservation system property into the JVM using the failsafe
configuration. The JNDI name differs slightly from the EAR-based form and can be obtained
from the names printed on the JBoss console or server.log.
java:jboss/exported/basicejb-war-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
Note
This JNDI name does not contain an EAR application name are the beginning
because there is no EAR. It contains a WAR module name instead of an EJB
module name and then the rest of the EJB is the same.
Be sure to include the leading "/" prior to the module name when there is no
EAR (e.g., "ejb:/".
10.Attempt to build at this point. The WAR should be deployed during the pre-integration-test
phase, the IT test run during the integration-test phase, the WAR undeployed during the postintegration-test phase, and the results checked for failure during the verify phase.
$ mvn clean verify
...
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ basicejb-war --...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ basicejb-war --...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ basicejb-war --...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ basicejb-war --...
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ basicejb-war --...
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ basicejb-war --...
[INFO] --- maven-war-plugin:2.4:war (default-war) @ basicejb-war --...
[INFO] --- cargo-maven2-plugin:1.4.3:redeploy (cargo-prep) @ basicejb-war --...
[INFO] --- maven-failsafe-plugin:2.17:integration-test (default) @ basicejb-war --...
Running org.myorg.basicejb.warejb.ReservationIT
...
02:30:58,927 DEBUG (ReservationIT.java:26) -jndi={
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory,
java.naming.provider.url=http-remoting://localhost:8080,
java.naming.factory.url.pkgs=org.jboss.ejb.client.naming,
jboss.naming.client.ejb.context=true}
...
02:30:59,231 DEBUG (ReservationIT.java:29) -jndi name:ejb:/basicejb-war-1.0-SNAPSHOT/ReservationEJB!
org.myorg.basicejb.ejb.ReservationRemote
02:30:59,247 DEBUG (ReservationIT.java:31) -reservationist=Proxy for remote EJB StatelessEJBLocator{
61
Chapter 3. WAR Deployment
appName='',
moduleName='basicejb-war-1.0-SNAPSHOT',
distinctName='',
beanName='ReservationEJB', view='interface org.myorg.basicejb.ejb.ReservationRemote'}
02:30:59,248 INFO (ReservationIT.java:36) -*** testPing ***
...
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
...
[INFO] --- cargo-maven2-plugin:1.4.3:undeploy (cargo-post) @ basicejb-war --...
[INFO] --- maven-failsafe-plugin:2.17:verify (default) @ basicejb-war --...
[INFO] BUILD SUCCESS
Note
Notice the we are now getting a failsafe execution and our JUnit IT test run
after the cargo deployment. We get this because we added a declaration of
the failsafe plugin to the WAR module *and* we ended the Java class with IT.
Looking at the plugin page [http://maven.apache.org/surefire/maven-failsafeplugin/examples/inclusion-exclusion.html], the other default name patterns
include.
• **/IT*.java
• **/*IT.java
• **/*ITCase.java
As discussed on that same web page -- you can expand or shrink that list with
the use of includes and excludes. This is commonly done to focus your testing
around a specific IT test or to exclude a IT test that requires further work for later.
11.Verify that everything builds from the root module.
$ cd ..; mvn clean install
...
[INFO] Basic EJB Exercise ................................. SUCCESS [ 0.178 s]
[INFO] Basic EJB Exercise::EJB ............................ SUCCESS [ 3.014 s]
[INFO] Basic EJB Exercise::EAR ............................ SUCCESS [ 0.412 s]
[INFO] Basic EJB Exercise::Remote Test .................... SUCCESS [ 5.144 s]
[INFO] Basic EJB Exercise::WAR ............................ SUCCESS [ 2.571 s]
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
3.4. Embed EJB in WAR Module
In the previous section we deployed an EJB imported from an external EJB Module. In this section
we will embed a new EJB within the WAR module. This type of packaging can be used by JSP/
Servlet writers to invoke lightweight POJOs injected by the container that can form transaction
boundaries and other EJB functionality -- without having to create a separate EJB module. The
more you think of this as a "for internal use only" and the smaller/self-contained your application
62
Embed EJB in WAR Module
-- the more this packaging scheme makes sense even though it can also blur the boundaries
between the architectural layers.
1. Create a source directory for Java classes that will be included in the production WAR. This
source is placed in src/main/java but will end up in WEB-INF/classes once the WAR is built.
$ mkdir -p basicejb-war/src/main/java
2. Create a package directory for our EJB and interface classes.
$ mkdir -p basicejb-war/src/main/java/org/myorg/basicejb/webejb/
3. Create the following EJB @Remote interface in the WAR/src/main/java directory.
$ cat basicejb-war/src/main/java/org/myorg/basicejb/webejb/ShopperRemote.java
package org.myorg.basicejb.webejb;
import javax.ejb.Remote;
@Remote
public interface ShopperRemote {
int ping();
}
4. Create the following Stateful EJB class in the WAR/src/main/java directory. In the previous
example our ReservationEJB was stateless and could not maintain any conversation state
with the client. In this example we will make the EJB stateful -- which means there will be a
memory allocated for each client to house information specific to their conversation with the
EJB instance.
$ cat basicejb-war/src/main/java/org/myorg/basicejb/webejb/ShopperEJB.java
package org.myorg.basicejb.webejb;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Stateful;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Stateful
public class ShopperEJB implements ShopperRemote {
private static Logger logger = LoggerFactory.getLogger(ShopperEJB.class);
//we can only track conversation state here if we are stateful
private int counter=0;
@PostConstruct
public void init() {
logger.debug("*** ShopperEJB({}).init() ***", super.hashCode());
}
63
Chapter 3. WAR Deployment
@PreDestroy
public void destroy() {
logger.debug("*** ShopperEJB({}).destroy() ***", super.hashCode());
}
@Override
public int ping() {
logger.debug("ping({}) called, returned {}", super.hashCode(), counter);
return counter++;
}
}
Note
We have added information to the ping call debug text that will provide us an
indication which EJB instance is being called.
5. Deploy the WAR at this point so we can be sure the EJB was created correctly and to be sure
of the JNDI name created. Note that we are not yet done with the module. The problem is we
have transitioned from a IT test-only module to one that also hosts EJB code. We are missing
a few dependencies.
$ mvn clean pre-integration-test
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ basicejb-war --[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /home/jcstaff/proj/basicejbEx/basicejb-war/target/classes
[INFO] ------------------------------------------------------------[ERROR] COMPILATION ERROR :
[INFO] ------------------------------------------------------------[ERROR]
/home/jcstaff/proj/basicejbEx/basicejb-war/src/main/java/org/myorg/basicejb/webejb/ShopperEJB.java:
[6,17] package javax.ejb does not exist
[ERROR] /home/jcstaff/proj/basicejbEx/basicejb-war/src/main/java/org/myorg/basicejb/webejb/ShopperRemote.java:
[3,17] package javax.ejb does not exist
[ERROR] /home/jcstaff/proj/basicejbEx/basicejb-war/src/main/java/org/myorg/basicejb/webejb/ShopperRemote.java:
[5,2] cannot find symbol
symbol: class Remote
[ERROR]
/home/jcstaff/proj/basicejbEx/basicejb-war/src/main/java/org/myorg/basicejb/webejb/ShopperEJB.java:
[11,2] cannot find symbol
symbol: class Stateful
[INFO] 4 errors
...
[INFO] BUILD FAILURE
6. Add several dependencies to the WAR/pom.xml account for use of EJB types.
# basicejb-war/pom.xml
<!-- for EJBs embedded in WAR module -->
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>javax.ejb-api</artifactId>
64
Embed EJB in WAR Module
<scope>provided</scope>
</dependency>
Note
You should always declare a scope=provided dependency on the JavaEE API
artifacts so they are not unnecessarily deployed in the WAR to the server. The
server already has a compliant version of these APIs and implementations for
those APIs.
7. Re-deploy the WAR with the compilation dependency corrected and look for the newly added
EJB to show up in the console or server.log.
$ mvn clean pre-integration-test
...
[INFO] BUILD SUCCESS
java:global/basicejb-war-1.0-SNAPSHOT/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote
java:app/basicejb-war-1.0-SNAPSHOT/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote
java:module/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote
java:jboss/exported/basicejb-war-1.0-SNAPSHOT/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote
java:global/basicejb-war-1.0-SNAPSHOT/ShopperEJB
java:app/basicejb-war-1.0-SNAPSHOT/ShopperEJB
java:module/ShopperEJB
We are specially interested in the java:jboss/exported name.
java:jboss/exported/basicejb-war-1.0-SNAPSHOT/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote
This will form the base of our JNDI name used for the IT test.
/basicejb-war-1.0-SNAPSHOT/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote
The main difference between the stateless and stateful JNDI names are when we use EJB
Client. When using the "ejb:" naming prefix we must append "?stateful" to the end of the name
to tell EJB Client we are communicating with a stateful EJB. EJB Client, unlike JBoss Remoting,
understands EJB communication and will attempt to setup for communication with the EJB in
an efficient manner. The extra text is not appropriate for using outside of the "ejb:" naming.
ejb:/basicejb-war-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
ejb:/basicejb-war-1.0-SNAPSHOT/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote?stateful
8. Create the following IT test in your WAR/src/test/java directory tree.
$ cat basicejb-war/src/test/java/org/myorg/basicejb/warejb/ShopperIT.java
65
Chapter 3. WAR Deployment
package org.myorg.basicejb.warejb;
import static org.junit.Assert.*;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.junit.Before;
import org.junit.Test;
import org.myorg.basicejb.webejb.ShopperRemote;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ShopperIT {
private static final Logger logger = LoggerFactory.getLogger(ShopperIT.class);
private static final String shopperJNDI = System.getProperty("jndi.name.shopper");
private InitialContext jndi;
@Before
public void setUp() throws NamingException {
assertNotNull("jndi.name.reservation not supplied", shopperJNDI);
logger.debug("getting jndi initial context");
jndi=new InitialContext();
logger.debug("jndi={}", jndi.getEnvironment());
}
@Test
public void testPing() throws NamingException {
logger.info("*** testPing ***");
ShopperRemote shopper1= (ShopperRemote) jndi.lookup(shopperJNDI);
for (int i=0; i<10; i++) {
int counter1=shopper1.ping();
assertEquals("unexpected count from shopper1", i, counter1);
}
}
}
Notice the difference in the way we constructed the stateful instance for our IT test. Since the
EJB is stateful, we create a reference to it close to where we will use it. Stateful EJBs have (inmemory) state related to a specific client and will be unique to each client.
9. Add the new EJB's jndi name to the failsafe configuration using the "jndi.name.shopper" token
found in the IT class' System.getProperty() statement. Remember to add the "?stateful" to the
end of the JNDI name. This will help the client library setup appropriately for communication
with this EJB.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<systemPropertyVariables>
66
Embed EJB in WAR Module
<jndi.name.reservation>ejb:/basicejb-war-1.0-SNAPSHOT/
ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</jndi.name.reservation>
<jndi.name.shopper>ejb:/basicejb-war-1.0-SNAPSHOT/
ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote?stateful</jndi.name.shopper>
</systemPropertyVariables>
</configuration>
</plugin>
10.Build the WAR and note our IT test passes -- verifying the stateful behavior of the EJB. Note
the output in the server.log. It shows each call returning to the same bean instance.
$ mvn clean verify
...
Running org.myorg.basicejb.warejb.ReservationIT
...
Running org.myorg.basicejb.warejb.ShopperIT
...
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
...
[INFO] BUILD SUCCESS
2014-10-11 15:28:09,702 DEBUG [org.myorg.basicejb.ejb.ReservationEJB] (EJB default - 2) ***
ReservationEJB.destroy() ***
2014-10-11 15:28:09,734 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 10) ***
ShopperEJB(626691370).init() ***
2014-10-11 15:28:09,831 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 9) ping(626691370) called,
returned 0
2014-10-11 15:28:09,835 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 4) ping(626691370) called,
returned 1
...
2014-10-11 15:28:09,864 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 2) ping(626691370) called,
returned 8
2014-10-11 15:28:09,868 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 10) ping(626691370)
called, returned 9
11.Update the IT test to add a second instance used concurrently with the first.
@Test
public void testPing() throws NamingException {
logger.info("*** testPing ***");
ShopperRemote shopper1= (ShopperRemote) jndi.lookup(shopperJNDI);
ShopperRemote shopper2= (ShopperRemote) jndi.lookup(shopperJNDI);
for (int i=0; i<10; i++) {
int counter1=shopper1.ping();
int counter2=shopper2.ping();
assertEquals("unexpected count from shopper1", i, counter1);
assertEquals("unexpected count from shopper2", i, counter2);
}
}
12.Re-build the WAR and note the IT test continues to pass -- showing we have two independent
instances. Note the output in the server.log showing the two sets of calls went to separate,
consistent instances on the server.
67
Chapter 3. WAR Deployment
$ mvn clean verify
...
Running org.myorg.basicejb.warejb.ReservationIT
...
Running org.myorg.basicejb.warejb.ShopperIT
...
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
...
[INFO] BUILD SUCCESS
2014-10-11 15:38:44,380 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 4) ***
ShopperEJB(2010115892).init() ***
2014-10-11 15:38:44,457 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 3) ***
ShopperEJB(741113002).init() ***
2014-10-11 15:38:44,467 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 5) ping(2010115892)
called, returned 0
2014-10-11 15:38:44,472 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 6) ping(741113002) called,
returned 0
2014-10-11 15:38:44,477 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 8) ping(2010115892)
called, returned 1
2014-10-11 15:38:44,483 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 7) ping(741113002) called,
returned 1
...
2014-10-11 15:38:44,548 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 10) ping(2010115892)
called, returned 8
2014-10-11 15:38:44,556 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 9) ping(741113002) called,
returned 8
2014-10-11 15:38:44,561 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 4) ping(2010115892)
called, returned 9
2014-10-11 15:38:44,566 DEBUG [org.myorg.basicejb.webejb.ShopperEJB] (EJB default - 3) ping(741113002) called,
returned 9
13.Take a look at the produced WAR. Notice it contains the imported EJB archive in the WEB-INF/
lib directory and embedded EJB classes in WEB-INF/classes directory. These are standard
locations in a WAR for deploying executable code in the WAR's classloader.
$ jar tf basicejb-war/target/basicejb-war-1.0-SNAPSHOT.war
...
WEB-INF/lib/basicejb-ejb-1.0-SNAPSHOT.jar
WEB-INF/classes/org/myorg/basicejb/webejb/ShopperRemote.class
WEB-INF/classes/org/myorg/basicejb/webejb/ShopperEJB.class
...
14.Now that you have this working, experiment by removing the "?stateful" from the JNDI name
in the WAR/pom.
<jndi.name.shopper>ejb:/basicejb-war-1.0-SNAPSHOT/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote</
jndi.name.shopper>
15.Attempt to build and IT test your stateless EJB. Needless to say you will not get beyind the first
call to ping where the client library is trying to broker something in the call.
68
Embed EJB in WAR Module
Running org.myorg.basicejb.warejb.ShopperIT
java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.String
at org.jboss.ejb.client.remoting.ProtocolMessageHandler.readAttachments(ProtocolMessageHandler.java:55)
at
org.jboss.ejb.client.remoting.InvocationExceptionResponseHandler
$MethodInvocationExceptionResultProducer.getResult(InvocationExceptionResponseHandler.java:82)
at org.jboss.ejb.client.EJBClientInvocationContext.getResult(EJBClientInvocationContext.java:276)
...
at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:144)
at com.sun.proxy.$Proxy6.ping(Unknown Source)
at org.myorg.basicejb.warejb.ShopperIT.testPing(ShopperIT.java:35)
...
Tests in error:
ShopperIT.testPing:35 » ClassCast java.lang.Class cannot be cast to java.lang....
Tests run: 2, Failures: 0, Errors: 1, Skipped: 0
[INFO] BUILD FAILURE
16.Restore the JNDI name and verify your project tree looks like the following at this point.
.
|-- 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
69
Chapter 3. WAR Deployment
|
`-- resources
|
|-- jboss-ejb-client.properties
|
|-- jndi.properties
|
`-- log4j.xml
|-- basicejb-war
| |-- pom.xml
| `-- src
|
|-- main
|
| |-- java
|
| | `-- org
|
| |
`-- myorg
|
| |
`-- basicejb
|
| |
`-- webejb
|
| |
|-- ShopperEJB.java
|
| |
`-- ShopperRemote.java
|
| `-- webapp
|
`-- test
|
|-- java
|
| `-- org
|
|
`-- myorg
|
|
`-- basicejb
|
|
`-- warejb
|
|
|-- ReservationIT.java
|
|
`-- ShopperIT.java
|
`-- resources
|
|-- jboss-ejb-client.properties
|
|-- jndi.properties
|
`-- log4j.xml
`-- pom.xml
3.5. Summary
• Deploy EJB Dependency
• Part of the flexible deploymemt enhancement made in JavaEE 6
• Avoids requirement for separate EAR module just to add EJB behavior to an existing WAR
• Retains encapsulation of the EJB. It is deployed as an EJB.jar within the WAR.
• Deploy Embedded EJB
• Another option in the flexible deployment feature
• Avoids requirement for separate EJB module just to add EJB behavior to an existing WAR
• Conceptually, the EJBs in this mode are likely a small extension of the web code
• No built-in feature for creating an EJB-client for remote clients. Must be done manually using
JAR plugin
• Stateless EJB
• No per-client conversational state maintained
• Like calling a function with supporting backend resources initialized and enterprise
requirements (e.g., security, transactions) enforced
• All information passed into and returned from the call except what is accessed/stored in
backend resources (i.e., database)
• Easier to scale and load balance because each call may go to a separate instance and server
• Stateful EJB
• Dedicated resources allocated on the server per instance
70
Summary
• Holds state in-memory or serialized to temporary storage
• Like calling a method of an object which is caching information in a multi-step transaction
• Harder to scale since all calls either must return to the same instance or the state must be
shared across the cluster
71
72
Chapter 4.
Build Commands
In the previous sections we used several commands as a part of the build. We will now take a
moment to describe a few of them so it is more evident they exist and how they can be useful.
4.1. Purpose
4.1.1. Goals
• Use a few key commands
4.1.2. Objectives
At the completion of this topic, the student shall
• Re-start build at a certain module
• Deploy and leave module artifact deployed
• Undeploy deployed module artifact
4.2. mvn (phase)
Maven builds modules in phases and in a specific phase ordering [http://maven.apache.org/
guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference]. We won't discuss all
of them here, but we will hit the ones of importance to server-side development. Try each of the
following commands from the root/parent directory and also watch the output in the server.log.
$ tail -n 999 -f standalone/log/server.log
$mvn (phase)
mvn clean
Delete previously built artifacts below the target tree(s).
mvn process-test-classes
mvn test
Run unit tests (i.e., classes matching surefire file name pattern [http://maven.apache.org/
surefire/maven-surefire-plugin/examples/inclusion-exclusion.html])
mvn package
Build Java, EJB, WAR, and EAR archives
mvn pre-integration-test
Deploy deployable archives for the module. Note this is a per-module basis. If you have
separate IT modules, each module is responsible for deploying its required artifacts. This is a
very useful target when running the IT test within the IDE and outside of the Maven framework
once deployed to the server.
73
Chapter 4. Build Commands
mvn integration-test
Run integration tests (IT tests) (i.e., classes matching failsafe file name pattern [http://
maven.apache.org/surefire/maven-failsafe-plugin/examples/inclusion-exclusion.html])
mvn post-integration-test
Undeploy deployed archives for the module. Note this is a per-module basis. If you have
separate IT modules, each module is responsible for undeploying its required artifacts. If these
artifacts are needed by a downstream module build they will need to be re-deployed by that
downstream module.
mvn verify
Evaluate the IT test results and wait to fail until this point so that the deployed artifacts can
be undeployed.
mvn install
Install the built artifacts into the local repository to make them available to downstream
modules when building outside the context of a common parent. i.e., If you build from the
parent of an EJB and EAR, the EAR will be given knowledge of the EJB through the parent.
If you build only the EAR, the latest installed version of the EJB in the local repository will
be used.
4.3. mvn (phase) -rf :module
It is common, when building a multi-module application that the first set of modules build fine but
an error is encountered when building one of the later modules.
1. Re-build just the WAR and RMI Test modules by naming the module to start with. Based on
the order specified in the parent pom, we will start with the RMI Test module.
<modules>
<module>basicejb-ejb</module>
<module>basicejb-ear</module>
<module>basicejb-test</module>
<module>basicejb-war</module>
</modules>
$ mvn clean install -rf :basicejb-test
...
[INFO] Reactor Summary:
[INFO]
[INFO] Basic EJB Exercise::Remote Test .................... SUCCESS [ 8.018 s]
[INFO] Basic EJB Exercise::WAR ............................ SUCCESS [ 3.030 s]
...
[INFO] BUILD SUCCESS
4.4. mvn (phase) -f (path to module)
There are times when you are looking to build one of the child modules or a branch of the child
module tree but you do not want the build to continue from that point. You likely know that you
74
mvn clean -Pundeploy
can execute a build in that module's directory or you can stay in the same directory and point
to the module you would like built. This technique is useful when you configure a build server to
build specific bootstrap modules (in your tree checked out from CM) prior to moving on to build
the remaining modules.
Note
The parameter to -f is a file reference and not a module name. You can use this to
refer to a parent (e.g., mvn clean -f ../pom.xml), siblings, as well as children. Lets
say you are in src/test/resources actively editing jndi.properties. You can issue a
building command that looks like
$ mvn clean process-test-resources -f ../../../pom.xml
1. Re-build just the RMI Test module from the root directory by specifying the module to the build.
$ mvn clean install -f basicejb-test/pom.xml
...
[INFO] BUILD SUCCESS
4.5. mvn clean -Pundeploy
There are times when you would like to undeploy a deployed archive without re-deploying again
first. This is common as part of the "mvn clean" lifecycle and allows you to undeploy everything
you have before deploying the next version on a multi-deployment setup.
1.
<profiles>
<!-- this profiles allow the EAR to be undeployed before it is deleted
during the clean target. This behavior requires the EAR to be
present, so it cannot be part of the default behavior. It is
only activated when -Pundeploy is present so that
normal cleans do not fail. -->
<profile>
<id>undeploy</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<executions>
<execution>
<id>undeploy-ear</id>
<phase>pre-clean</phase>
<goals>
<goal>undeploy</goal>
</goals>
</execution>
</executions>
</plugin>
75
Chapter 4. Build Commands
</plugins>
</build>
</profile>
</profiles>
2. Deploy the EAR and WAR artifacts to the server and note that they are deployed using the
console or server.log.
$ mvn clean pre-integration-test
# server.log
2014-10-11 17:24:02,553 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")
...
2014-10-11 17:24:04,157 INFO [org.jboss.as.server] (management-handler-thread - 1) JBAS018559: Deployed
"basicejb-war-1.0-SNAPSHOT.war" (runtime-name : "basicejb-war-1.0-SNAPSHOT.war")
3. This is the point in which we can run our IT tests within the IDE once we resolve a few remaining
dependencies on the failsafe plugins (later...)
4. Undeploy the EAR and WAR artifacts from the server and note they are undeployed using the
output of the console or server.log.
$ mvn clean -Pundeploy
2014-10-11 17:24:15,019 INFO [org.jboss.as.server] (management-handler-thread - 1) JBAS018558: Undeployed
"basicejb-ear-1.0-SNAPSHOT.ear" (runtime-name: "basicejb-ear-1.0-SNAPSHOT.ear")
...
2014-10-11 17:24:15,662 INFO [org.jboss.as.server] (management-handler-thread - 1) JBAS018558: Undeployed
"basicejb-war-1.0-SNAPSHOT.war" (runtime-name: "basicejb-war-1.0-SNAPSHOT.war")
4.6. mvn clean Dit.test=fully.qualified.ITPath#testMethod
To execute a single IT test case use the following
$ mvn verify -f basicejb-test -Dit.test=org.myorg.basicejb.earejb.ReservationIT
To execute a single IT test case method use the following
$ mvn verify -f basicejb-test -Dit.test=org.myorg.basicejb.earejb.ReservationIT#testPing
Note
Maven and surefire have made it very tough to skip unit tests and only run a specific
IT test. However, you can always add a temporary set of includes/excludes to the
surefire configuration in order to achieve the sometimes-desired result of turning
76
Summary
off any preceding unit tests. Note that this is not an issue when IT tests are hosted
in a separate module.
4.7. Summary
• Leverage build lifecycle phases to build what is needed
• Keeps from executing the entire build lifecycle
• Required in some cases (deployment) to achieve desired results (stay deployed)
• Re-start the build at a specific module
• Saves time from building unchanged modules
• User must know that skipped modules are not needed
• Undeploy module artifact
• Enables cleanup prior to a re-deploy
• Good for when having multiple application deployments with dependencies between them
77
78
Chapter 5.
Controlling JNDI Names
Although the instructions thus far have provided details at the filesystem and Maven level, you
have surely started importing and developing the projects within your IDE by now. The one gotcha
remaining to solve are those pesky JNDI names and keeping our Java code ignorant of version
numbers. Version numbers have a place but they rarely have a place in lookups when the version#
can be updated with even trivial releases.
basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
We initially solved the problem by getting the pom involved with expanding the version# and
passing the result to the IT test as a system property. That worked well within the Maven build.
However, we want to develop and run our IT tests outside of Maven once we have leveraged
enough of Maven to get started.
<jndi.name.reservation>
ejb:basicejb-ear-${project.version}/basicejb-ejb-${project.version}/
ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
</jndi.name.reservation>
private static final String reservationJNDI = System.getProperty("jndi.name.reservation");
In this section we are going to add configuration options that will eliminate the version#s from the
JNDI names
basicejb-ear/basicejb-ejb/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
This will permit our IT tests to form a reasonable default. We can still override this default with
a system property.
private static final String reservationJNDI = System.getProperty("jndi.name.reservation",
"ejb:basicejb-ear/basicejb-ejb/ReservationEJB!"+ReservationRemote.class.getName());
5.1. Purpose
5.1.1. Goals
• Create deterministic JNDI names
5.1.2. Objectives
At the completion of this topic, the student shall
• Eliminate version# from EAR application name
• Eliminate version# from EJB module name
79
Chapter 5. Controlling JNDI Names
• Eliminate version# from WAR module name
5.2. Eliminate Version# from EAR-based JNDI Name
1. Build the RMI Test module and notice the application/EAR portion of the JNDI name printed to
the server's console and server.log contains the version# of the EAR.
$ mvn verify -f basicejb-test
java:jboss/exported/basicejb-ear-1.0-SNAPSHOT/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!
org.myorg.basicejb.ejb.ReservationRemote
2. Add the following declaration of the EAR plugin and add a configuration option to change the
applicationName to be the artifactId for the module.
<build>
<plugins>
<!-- provide properties here to impact the EAR packaging -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<configuration>
<!-- eliminates use of version in EAR JNDI name portion -->
<applicationName>${project.artifactId}</applicationName>
</configuration>
</plugin>
</plugins>
</build>
3. Since this is our first reference to the EAR plugin, add a pluginManagement definition of this
plugin in the parent pom.xml.
<properties>
...
<maven-ear-plugin.version>2.9.1</maven-ear-plugin.version>
...
<build>
<pluginManagement>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>${maven-ear-plugin.version}</version>
<configuration>
<version>7</version>
</configuration>
</plugin>
4. Notice that with the lack of specification, the EAR plugin defaults to generate an application.xml
according to a J2EE 1.3 DTD.
80
Eliminate Version# from EAR-based JNDI Name
$ cat basicejb-ear/target/application.xml
<?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>
5. Once we add the plugin definition to use JavaEE 7, the EAR plugin generates an application.xml
according to a JavaEE 7 schema. There is no noticeable difference other than the change in
schema versus DTD reference.
$ cat basicejb-ear/target/application.xml
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
application_7.xsd" version="7">
http://xmlns.jcp.org/xml/ns/javaee/
<application-name>basicejb-ear</application-name>
<description>This project provides a sample EAR for the Java EE components
associated with the overall project.</description>
<display-name>basicejb-ear</display-name>
<module>
<ejb>basicejb-ejb-1.0-SNAPSHOT.jar</ejb>
</module>
</application>
6. Rebuild and redeploy the EAR and note the application portion of the JNDI name is now a fixed
value. Since our IT test still references the older name it fails.
java:jboss/exported/basicejb-ear/basicejb-ejb-1.0-SNAPSHOT/ReservationEJB!
org.myorg.basicejb.ejb.ReservationRemote
$ mvn clean install -rf basicejb-ear
...
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@395c1703
...
at org.myorg.basicejb.earejb.ReservationIT.testPing(ReservationIT.java:37)
...
[INFO] Reactor Summary:
[INFO]
[INFO] Basic EJB Exercise::EAR ............................ SUCCESS [ 1.533 s]
[INFO] Basic EJB Exercise::Remote Test .................... FAILURE [ 5.656 s]
[INFO] Basic EJB Exercise::WAR ............................ SKIPPED
81
Chapter 5. Controlling JNDI Names
[INFO] -----------------------------------------------------------------------[INFO] BUILD FAILURE
7. Update the RMI Test/pom.xml to eliminate the version# of the application and re-run the prior
build. We should be working again but still with a version# in the EJB.
<jndi.name.reservation>ejb:basicejb-ear/basicejb-ejb-${project.version}/
ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</jndi.name.reservation>
$ mvn clean install -rf basicejb-ear
...
[INFO] Reactor Summary:
[INFO]
[INFO] Basic EJB Exercise::EAR ............................ SUCCESS [ 1.573 s]
[INFO] Basic EJB Exercise::Remote Test .................... SUCCESS [ 5.851 s]
[INFO] Basic EJB Exercise::WAR ............................ SUCCESS [ 3.068 s]
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
8. Add a modules section to the EAR plugin declaration. The default for this relied on the
dependencies and this caused the EJB.jar to be hosted in the EAR with its fully qualified version.
Here we supply a specific name we want for the EJB.jar. This will get reflected in the JNDI name.
I also ask that you add a defaultLibBundleDir specification the to descriptor at this time. This
will define a directory within the EAR (similar to WEB-INF/lib) whose contents will be added to
the global classpath of components loaded by the EAR. We don't have a use for that yet, but
this is the last tweak we will be making to EARs for a while.
<build>
<plugins>
<!-- provide properties here to impact the EAR packaging -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<configuration>
<!-- names directory within EAR to place jars to be in classpath -->
<defaultLibBundleDir>lib</defaultLibBundleDir>
<!-- eliminates use of version in EAR JNDI name portion -->
<applicationName>${project.artifactId}</applicationName>
<modules>
<!-- eliminates use of the version in the EJB JNDI name -->
<ejbModule>
<groupId>${project.groupId}</groupId>
<artifactId>basicejb-ejb</artifactId>
<bundleFileName>basicejb-ejb.jar</bundleFileName>
</ejbModule>
</modules>
</configuration>
</plugin>
</plugins>
</build>
82
Eliminate Version# from EAR-based JNDI Name
9. Rebuild and redeploy the EAR and note the module portion of the JNDI name is now a fixed
value. Since our IT test still references the older name it fails.
java:jboss/exported/basicejb-ear/basicejb-ejb/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote
$ mvn clean install -rf basicejb-ear
...
java.lang.IllegalStateException: EJBCLIENT000025: No EJB receiver available for handling [appName:basicejbear,
moduleName:basicejb-ejb-1.0-SNAPSHOT,
distinctName:]
combination
for
invocation
context
org.jboss.ejb.client.EJBClientInvocationContext@3d01e5eb
at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:749)
...
at org.myorg.basicejb.earejb.ReservationIT.testPing(ReservationIT.java:37)
...
[INFO] Reactor Summary:
[INFO]
[INFO] Basic EJB Exercise::EAR ............................ SUCCESS [ 1.369 s]
[INFO] Basic EJB Exercise::Remote Test .................... FAILURE [ 5.643 s]
[INFO] Basic EJB Exercise::WAR ............................ SKIPPED
[INFO] -----------------------------------------------------------------------[INFO] BUILD FAILURE
10.Update the RMI Test/pom.xml to eliminate the version# of the module and re-run the prior build.
We should be working again but still with a version# in the EJB.
<jndi.name.reservation>ejb:basicejb-ear/basicejb-ejb/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</
jndi.name.reservation>
$ mvn clean install -rf basicejb-ear
...
[INFO] Reactor Summary:
[INFO]
[INFO] Basic EJB Exercise::EAR ............................ SUCCESS [ 1.424 s]
[INFO] Basic EJB Exercise::Remote Test .................... SUCCESS [ 5.811 s]
[INFO] Basic EJB Exercise::WAR ............................ SUCCESS [ 3.000 s]
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
11.Update your IT test to assign a reasonable default for the JNDI name. To test the change,
comment out the system property specification in the failsafe configuration.
public class ReservationIT {
private static final String reservationJNDI = System.getProperty("jndi.name.reservation",
"ejb:basicejb-ear/basicejb-ejb/ReservationEJB!"+ReservationRemote.class.getName());
<systemPropertyVariables>
<!-<jndi.name.reservation>ejb:basicejb-ear/basicejb-ejb/ReservationEJB!
org.myorg.basicejb.ejb.ReservationRemote</jndi.name.reservation>
-->
</systemPropertyVariables>
83
Chapter 5. Controlling JNDI Names
$ mvn clean install -rf basicejb-ear
...
[INFO] Reactor Summary:
[INFO]
[INFO] Basic EJB Exercise::EAR ............................ SUCCESS [ 1.522 s]
[INFO] Basic EJB Exercise::Remote Test .................... SUCCESS [ 5.810 s]
[INFO] Basic EJB Exercise::WAR ............................ SUCCESS [ 3.119 s]
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
If you are not sure if this is using your default JNDI name you can optionally munge the value
in the IT test and note it will then fail.
12.You may optionally re-enable the system property for the JNDI name but I would suggest
against it until there is a need to derive the name a different way. It can get confusing when a
default gets derived from multiple locations.
5.3. Eliminate Version# from WAR-based JNDI Name
1. Note the JNDI name of the WAR when it was deployed above. We want to remove the version#
from the deployed WAR.
java:jboss/exported/basicejb-war-1.0-SNAPSHOT/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote
If we were deploying the WAR within an EAR, we would have added the following to the EAR
plugin.
<webModule>
<groupId>${project.groupId}</groupId>
<artifactId>basicejb-war</artifactId>
<contextRoot>basicejb-war</contextRoot>
</webModule>
2. Since we deploy a naked WAR, we cannot use the standard EAR technique. However, we can
accomplish our goal by adding a jboss-specific deployment descriptor (jboss-web.xml) to the
WAR (WEB-INF directory).
$ mkdir basicejb-war/src/main/webapp/WEB-INF/
$ cat basicejb-war/src/main/webapp/WEB-INF/jboss-web.xml
<jboss-web>
<!-- needed to always assure that version# does not get into JNDI name -->
<context-root>basicejb-war</context-root>
</jboss-web>
Note
The source directory for the WEB-INF and WAR content data is in src/main/
webapp. Artifacts in src/main/resources will end up in WEB-INF/classes.
84
Eliminate Version# from WAR-based JNDI Name
3. Rebuild and redeploy the WAR and note the module portion of the JNDI name is now a fixed
value. Since our IT test still references the older name it fails.
java:jboss/exported/basicejb-war/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote
$ mvn clean install -rf basicejb-war
...
testPing(org.myorg.basicejb.warejb.ReservationIT) Time elapsed: 0.766 sec <<< ERROR!
java.lang.IllegalStateException:
EJBCLIENT000025:
No
EJB
receiver
available
for
handling
[appName:, moduleName:basicejb-war-1.0-SNAPSHOT, distinctName:] combination for invocation context
org.jboss.ejb.client.EJBClientInvocationContext@1fafcae2
at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:749)
...
at com.sun.proxy.$Proxy5.ping(Unknown Source)
at org.myorg.basicejb.warejb.ReservationIT.testPing(ReservationIT.java:37)
testPing(org.myorg.basicejb.warejb.ShopperIT) Time elapsed: 0 sec <<< ERROR!
javax.naming.NamingException: Failed to create proxy
at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:813)
...
at javax.naming.InitialContext.lookup(InitialContext.java:411)
at org.myorg.basicejb.warejb.ShopperIT.testPing(ShopperIT.java:32)
...
Tests in error:
ReservationIT.testPing:37 » IllegalState EJBCLIENT000025: No EJB receiver avai...
ShopperIT.testPing:32 » Naming Failed to create proxy
...
[INFO] BUILD FAILURE
Note
I thought it was interesting that the IT test for the stateless EJB does not fail until
the call is actually made to ping(). The IT test for the stateful EJB fails during
the JNDI lookup().
4. Update the WAR/pom.xml to eliminate the version# of the module for both JNDI names and rerun the prior build. We should be working again but still with a version# in the WAR.
<jndi.name.reservation>ejb:/basicejb-war/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</
jndi.name.reservation>
<jndi.name.shopper>ejb:/basicejb-war/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote?stateful</
jndi.name.shopper>
$ mvn clean install -rf basicejb-war
...
[INFO] BUILD SUCCESS
85
Chapter 5. Controlling JNDI Names
Note
Be sure to append "?stateful" to your stateful EJB JNDI name. We need this for
"ejb:" naming contexts for EJB Client to work correctly. there should be no issue
promoting those details to the client since a client will be designed differently
when working with a stateless or stateful EJB.
5. Update your IT tests to assign a reasonable default for the JNDI name. To test the change,
comment out the system property specification in the failsafe configuration.
public class ReservationIT {
private static final String reservationJNDI = System.getProperty("jndi.name.reservation",
"ejb:/basicejb-war/ReservationEJB!"+ReservationRemote.class.getName());
public class ShopperIT {
private static final String shopperJNDI = System.getProperty("jndi.name.shopper",
"ejb:/basicejb-war/ShopperEJB!"+ShopperRemote.class.getName()+"?stateful");
<systemPropertyVariables>
<!-<jndi.name.reservation>ejb:/basicejb-war/ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</
jndi.name.reservation>
<jndi.name.shopper>ejb:/basicejb-war/ShopperEJB!org.myorg.basicejb.webejb.ShopperRemote?stateful</
jndi.name.shopper>
-->
</systemPropertyVariables>
$ mvn clean install -rf basicejb-ear
...
[INFO] BUILD SUCCESS
If you are not sure if this is using your default JNDI name you can optionally munge the value
in the IT test and note it will then fail.
6. You may optionally re-enable the system property for the JNDI names but I would suggest
against it until there is a need to derive the name a different way. It can get confusing when a
default gets derived from multiple locations. If we end up with several IT tests forming the same
JNDI name, I would suggest moving the construction of the JNDI name to a test utility class.
5.4. Summary
• EAR-based names
• impacted using the META-INF/application.xml
• application.xml can be auto-built and configured using EAR plugin
• EAR name configured with application-name element of application.xml
• EJB name configured with the name of the EJB module within the EAR
• EJB module can be renamed using ejbModule.bundleFileName of EAR plugin
86
Summary
• WAR name configured with module.web.context-root element of application.xml
• WAR-based names
• no JavaEE/WAR standard for naming a naked-deployed WAR
• could deploy WAR within WAR to help control JNDI name
• JBoss uses the context-root supplied in WEB-INF/jboss-web.xml
• Reasonable defaults in IT classes
• Makes them IDE-friendly
• Reduces dependency on full Maven build lifecycle for everything you do
• Can be developed, run, and re-factored more quickly
Figure 5.1. Sample META-INF/application.xml
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
application_7.xsd" version="7">
http://xmlns.jcp.org/xml/ns/javaee/
<application-name>basicejb-ear</application-name>
<description>This project provides a sample EAR for the Java EE components
associated with the overall project.</description>
<display-name>basicejb-ear</display-name>
<module>
<web>
<web-uri>(example web module).war</web-uri>
<context-root>(example-fixed-web-module-name)</context-root>
</web>
</module>
<module>
<ejb>basicejb-ejb.jar</ejb>
</module>
<library-directory>lib</library-directory>
</application>
Figure 5.2. Sample EAR plugin configuration
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<configuration>
<!-- names directory within EAR to place jars to be in classpath -->
<defaultLibBundleDir>lib</defaultLibBundleDir>
<!-- eliminates use of version in EAR JNDI name portion -->
<applicationName>${project.artifactId}</applicationName>
<modules>
<webModule>
<groupId>${project.groupId}</groupId>
<artifactId>(example-web-module-artifactId)</artifactId>
<contextRoot>(example-fixed-web-module-name)</contextRoot>
</webModule>
<!-- eliminates use of the version in the EJB JNDI name -->
<ejbModule>
87
Chapter 5. Controlling JNDI Names
<groupId>${project.groupId}</groupId>
<artifactId>basicejb-ejb</artifactId>
<bundleFileName>basicejb-ejb.jar</bundleFileName>
</ejbModule>
</modules>
</configuration>
</plugin>
Figure 5.3. Sample WEB-INF/jboss-web.xml
<jboss-web>
<!-- needed to always assure that version# does not get into JNDI name -->
<context-root>basicejb-war</context-root>
</jboss-web>
88
Chapter 6.
Debug Remote EJB
Up until now we have kept our focus away from the IDE and onto the filesystem and Maven
configurations to give better insight into the structure of a multi-module application and to show
how IDE-agnostic the build process is. This will be very important when you check in your modules
to be built/tested by a build server (e.g., CruiseControl [http://cruisecontrol.sourceforge.net/],
Hudson, [http://hudson-ci.org/] or Jenkins [http://jenkins-ci.org/]). However, we want to speed up
the process to do actual development. We want to spend as much of our time as possible within
a development/test-bench that promotes the efficient development of Java code. The commandline builds will still be leveraged but all those copy/pastes/edits I asked you do would more
than likely be created with the use of a Java/JavaEE-knowledgeable IDE. The examples in this
section use Eclipse [https://www.eclipse.org/ide/]. Any other credible IDE (e.g., NetBeans [https://
netbeans.org/], IntelliJ [http://www.jetbrains.com/idea/]) should work in a similar manner and offer
comparable functionality.
6.1. Purpose
6.1.1. Goals
• Develop and test IT tests within IDE
• Debug remote deployed application
6.1.2. Objectives
At the completion of this topic, the student shall
• Run IT tests within the IDE
• Debug remote deployment to standalone server
• Debug remote deployment to embedded server
6.2. Running IT Tests in IDE
In this section we want to break the cycle of having to issue a heavyweight Maven build every time
we want to run an IT test. We can do this by making JUnit tests "IDE-friendly". What I mean by
that is ... allow the IDE environment to derive usable values without relying on system properties
being passed in from failsafe. Failsafe won't be involved with running your JUnit tests within the
IDE. That means you can either get the usable values thru reasonable defaults or through the use
of filtered property files in the classpath. Looking up the JNDI name for a remote EJB provides
an excellent example of both techniques.
To derive properties for the InitialContext -- we used filtered property files.
#jndi.properties
java.naming.factory.initial=${jboss.remoting.java.naming.factory.initial}
java.naming.factory.url.pkgs=${jboss.ejbclient.java.naming.factory.url.pkgs}
java.naming.provider.url=${jboss.remoting.java.naming.provider.url}
#java.naming.security.principal=${jndi.user}
89
Chapter 6. Debug Remote EJB
#java.naming.security.credentials=${jndi.password}
jboss.naming.client.ejb.context=true
That got expanded by out initial Maven build to contain the following. The Maven/Eclipse plugin
(m2e [https://www.eclipse.org/m2e/]) within Eclipse will continue to keep this up to date. It has a
plugin the provides understanding of what the maven-resource-plugin would do outside of the IDE
and re-creates that functionality within the IDE environment.
#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
There is nothing magical about jndi.properties. We can add more properties to that file (not
common) or create other property files (e.g., it.properties) to be filtered and made available to
the IT test. However, our other option was to pass in a system property (-DpropertyName=value)
using the failsafe configuration.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<jndi.name.reservation>ejb:basicejb-ear/basicejb-ejb/
ReservationEJB!org.myorg.basicejb.ejb.ReservationRemote</jndi.name.reservation>
</systemPropertyVariables>
</configuration>
</plugin>
Anticipating the system property might not always be available or necessary in 99-100% of the
IT tests once we stablized some values, we started processing the system properties by deriving
a reasonable default. This can be overriden but when not overriden it will work 99%-100% of the
time as well.
private static final String reservationJNDI = System.getProperty("jndi.name.reservation",
"ejb:basicejb-ear/basicejb-ejb/ReservationEJB!"+ReservationRemote.class.getName());
Let's show this in action by loading our application into the IDE, deploying the EAR and WAR, and
executing the IT tests from within Eclipse.
1. If you have not already done so, import your multi-module project into the IDE. For Eclipse that
involves using Right-Click, Import Existing Maven Projects,
Tip
We made several incremental changes to the plugins as we built the end-toend application. If you initially loaded the project prior to making many of these
90
Running IT Tests in IDE
incremental changes Eclipse could have some issues upgrading the "facet" of
a particular module on the fly. That was the case in my environment so I ...
a. Removed all modules from Eclipse
b. Deleted all .settings directories and .project and .classpath files
c. Re-imported the modules fresh and everything cleared with the "facet"
JavaEE versions.
a. Right Click on a Working Group or Select File
b. Select "Import Existing Maven Projects"
c. Navigate to the root module
d. Click OK. Eclipse will show you a list of modules at and under the root module.
e. Select all modules in the tree. They should now show up in the various explorer tabs
2. Leverage existing Maven configuration to deploy the EAR and WAR artifacts to the server.
This can be done at the command line command line using techniques you are already familiar
with.
$ mvn pre-integration-test
The Eclipse/Maven integration can also provide access to that functionality through a re-usable
Maven Build Run Configuration.
a. Right Click on the Root Module and select "Run As"
Figure 6.1. Run As Maven Build...
b. Select "Maven build...". An "Edit Configuration" panel is displayed
91
Chapter 6. Debug Remote EJB
Figure 6.2. Edit Configuration Panel Options: Main
c. Type "mvn pre-integration-test" for Name
d. Type or select the variable "${project_loc}" for Base directory
e. Type "pre-integration-test" for Goals
f. Select "Skip Tests" option
g. Click on the "Common" tab
h. Select "Run" in the Display in favorites menu
Figure 6.3. Edit Configuration Panel Options: Common
i. Click Apply to save the run configuration
j. Click Run to have it start and deploy the WAR and EAR to the server. This should provide
the same output you experienced previously at the command line. The Maven command
92
Running IT Tests in IDE
output is displayed in an Eclipse Console window and the server output is available in the
server console and server.log
Figure 6.4. Maven Build Run Output
k. Select the root module again
l. With the root module actively selected, click the down-arrow to the right of the green circle
with the white arrow.
m.Select the "mvn pre-integration-test" build configuration you pinned to this menu by selecting
it as a favorite in the "Common" tab
Figure 6.5. Maven Build Run Output
n. Notice that the EAR and WAR get re-built and re-deployed again. You can use this or the
command-line technique at any time to achieve the same goal of deploying the application(s)
to the server. If you want to deploy just the EAR or WAR -- select that specific module.
93
Chapter 6. Debug Remote EJB
However, keep in mind that the build will be constrained to that module and will not pick up
changes in upsteam modules that have not installed those changes into the local repository.
3. Run the IT tests by selecting either the module, folder, or package with the IT tests or the
specific IT test or specific test method and right-click, Run As, JUnit test.
You should see the IT test run, the IT test communicate with the EJB on the server, and see
activity in the server.log.
Figure 6.6. Maven Build Run Output
4. Make a change to the ReservationIT to invoke ping() several times in a row in a for-loop.
@Test
public void testPing() throws NamingException {
logger.info("*** testPing ***");
for (int i=0; i<10; i++) {
reservationist.ping();
}
}
5. Rerun the ReservationIT#testPing() @Test method once your changes are complete. Notice
you get multiple sets of debug on the server.
2014-10-11 23:42:19,474 DEBUG [org.myorg.basicejb.ejb.ReservationEJB] (EJB default - 8) *** ReservationEJB.init()
***
2014-10-11 23:42:19,475 DEBUG [org.myorg.basicejb.ejb.ReservationEJB] (EJB default - 8) ping called
2014-10-11 23:42:19,475 DEBUG [org.myorg.basicejb.ejb.ReservationEJB] (EJB default - 8) ***
ReservationEJB.destroy() ***
2014-10-11 23:42:19,521 DEBUG [org.myorg.basicejb.ejb.ReservationEJB] (EJB default - 7) *** ReservationEJB.init()
***
2014-10-11 23:42:19,522 DEBUG [org.myorg.basicejb.ejb.ReservationEJB] (EJB default - 7) ping called
94
Debugging Deployment to Standalone Server
2014-10-11
23:42:19,522
DEBUG
[org.myorg.basicejb.ejb.ReservationEJB]
(EJB
default
-
7)
***
ReservationEJB.destroy() ***
...
6. Set a breakpoint inside the for-loop where the method calls reservationist.ping() and chose
Debug As this time.
Figure 6.7. Debug IT test
7. Keep clicking the green, right-facing arrow at the top or the yellow step over/into arrows to
move forward. Notice that you have access to the state variables of the IT test while you move
forward with the test.
6.3. Debugging Deployment to Standalone Server
Getting to the point where you can run and debug the IT test within the IDE is a significant amount
of the work involved with being able to debug the code on the server-side. At this point you have
that code surrounded and now all you have to do is peek inside. Lets look at how this is done
with a remote server first.
1.
wildfly-8.1.0.Final/bin/standalone.conf
JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n"
wildfly-8.1.0.Final/bin/standalone.conf.bat
set "JAVA_OPTS=%JAVA_OPTS% -agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n"
=========================================================================
Listening for transport dt_socket at address: 8787
00:09:23,260 INFO [org.jboss.modules] (main) JBoss Modules version 1.3.3.Final
95
Chapter 6. Debug Remote EJB
Note
The use of suspend=n allows the server to run normally until connected to a
debugger client and a breakpoint is hit. Until then there is no noticable impact
so you can leave this setting active on your development server instance all the
time.
2. Attach a debugger client to the server using the following...
a. Select the RMI Test module to make it the active project within Eclipse.
b. Click on the arrow to the right of the green bug icon on the top menu bar and select Debug
Configurations...
c. Create a new "Remote Java Application"
d. Change the Port to 8787 to match your JBoss configuration.
e. Select the Source tab, click Add..., select Java Project and press Add. A selection box of
Eclipse projects is displayed for your to select from.
f. Select all modules that are a part of this application and press OK to continue.
g. Click Debug to start the debugging session. Nothing exiting will occur until we set and cause
a breakpoint to be hit.
3. Create a breakpoint in the ReservationEJB ping() call and re-run the IT test in either Run As or
Debug As mode. Your selection there only pertains to the IT test and not what you do on the
client. Notice you are now stopped on the server-side.
Figure 6.8. Debug IT test
4. Click the red bent line icon on the top menu bar to detach from the server and release control
of the execution.
96
Debugging Deployment to Embedded Server
6.4. Debugging Deployment to Embedded Server
In the previous section we showed how the stand-alone server required some setup but was very
easy to establish a debugging session once much of the one-time setup was complete. In this
method we are going to show how that setup can get even easier if you are willing and able to
run the application server in the same JVM as the IDE.
1. Shutdown the standalone server instance.
2. Activate the JavaEE profile, select the server you setup in the environment setup instructions,
right click, and select Debug. The server should start as usual.
3. The server will restart and likely still have the EAR and WAR deployed from the previous section.
Ignore that they are already deployed and re-deploy using the "mvn pre-integration-test" Build
Configuration you created earlier. Be sure to have the appropriate module selected prior to
executing this Build Configuration so that ${project.loc} can get assigned a project.
4. Select the ReservationIT and execute it using Debug As. It matters what you select this time
because both the client IT test and server will be part of the same debugging session.
5. You should be soon looking at a breakpoint in ReservationIT.ping(). Select continue in order to
hit the breakpoint on the server-side. If you get a prompt asking for the path to the source for
the EJB register the java projects as you did in the section prior to this.
Note
I have found that I always have to restart the test for the new source paths to
take effect.
6. At this point you are in a similar state as with the stand-alone server. However, if you click
the red box icon for stop, you will kill both the IT test and the server. In the standalone server
environment the kill only stops the IT test.
6.5. Summary
• Running IT tests within IDE
• Much faster code, compile, test development cycle
• Enables debugging
• Standalone server
• More complex but not complicated
• Consistent with debugging true remote server
• Some independence between remote and local code
• Embedded Server
• Easy to setup
• Requires server to be local
• Local and remote code accessed seemlessly
97
98
Chapter 7.
EJB Parent POM
One last point before we wrap up. During the Maven module setup chapters you went through
some extra hoops to separate plugin and dependency definitions from their declaration in the
implementation modules. We placed those definitions, by default into the parent module for the
exercise. In this chapter we will gut that parent of the generic setup that can be used in other
projects based on EJB and related technologies.
7.1. Purpose
7.1.1. Goals
• Create a re-suable root pom that defines dependencies and plugins suitable for use by multiple
applications
• Releave the parent pom of the multi-module project of tracking those details
7.1.2. Objectives
At the completion of this topic, the student shall
• Have a root pom suitable for reuse in different EJB-based applications
7.2. Create Root POM
1. Create a new module that will be used as a root pom. To cut down on redundancy, have that
pom inherit from the jpa-parent pom [https://github.com/ejavaguy/ejava-student/blob/master/
jpa/jpa-parent/pom.xml] created in the jpa portion of the course. Our EJB solutions will soon
need the details contained in the data tier parent pom.
$ mkdir ../ejb-parent
$ cat ../ejb-parent/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>info.ejava.examples.jpa</groupId>
<artifactId>jpa-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.myorg</groupId>
<artifactId>ejb-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>EJB Parent POM</name>
<description>
This parent pom is intended to provide common and re-usable
99
Chapter 7. EJB Parent POM
definitions and constructs across EJB related modules.
It extends and does not repeat the details contained in
the jpa-parent module.
</description>
<properties>
</properties>
<repositories>
</repositories>
<pluginRepositories>
</pluginRepositories>
<dependencyManagement>
<dependencies>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
</plugins>
</pluginManagement>
</build>
<profiles>
</profiles>
</project>
2. Update the current root/parent pom to inherit from this new pom.
<parent>
<groupId>org.myorg</groupId>
<artifactId>ejb-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../ejb-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>myorg.basicejb</groupId>
<artifactId>basicejbEx</artifactId>
<packaging>pom</packaging>
3. Verify the exercise still builds.
$ mvn clean install
4. Remove the properties already defined in the jpa-parent that we have defined in this parent
pom.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source.version>1.8</java.source.version>
<java.target.version>1.8</java.target.version>
<junit.version>4.11</junit.version>
100
Create Root POM
<log4j.version>1.2.13</log4j.version>
<maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
5. Define the properties unique to EJB development in the ejb-parent pom. Remove them from
the current parent pom.
<properties>
<ejava.version>4.0.0-SNAPSHOT</ejava.version>
<javax.ejb-api.version>3.2</javax.ejb-api.version>
<slf4j.version>1.7.2</slf4j.version>
<wildfly.version>8.1.0.Final</wildfly.version>
<cargo-maven2-plugin.version>1.4.3</cargo-maven2-plugin.version>
<maven-ear-plugin.version>2.9.1</maven-ear-plugin.version>
<maven-ejb-plugin.version>2.4</maven-ejb-plugin.version>
<maven-failsafe-plugin.version>2.17</maven-failsafe-plugin.version>
<maven-war-plugin.version>2.4</maven-war-plugin.version>
<cargo.containerId>wildfly8x</cargo.containerId>
<jboss.host>localhost</jboss.host>
<jboss.http.port>8080</jboss.http.port>
<jboss.remoting.port>${jboss.http.port}</jboss.remoting.port>
<jboss.naming.port>${jboss.remoting.port}</jboss.naming.port>
<jboss.mgmt.host>${jboss.host}</jboss.mgmt.host>
<jboss.mgmt.port>9990</jboss.mgmt.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/>
<jboss.ejbclient.java.naming.factory.url.pkgs>org.jboss.ejb.client.naming</
jboss.ejbclient.java.naming.factory.url.pkgs>
</properties>
6. Verify the exercise still builds.
$ mvn clean install
7. Remove the dependencyManagement definitions already defined in the jpa-parent that we have
defined in this parent pom.
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
101
Chapter 7. EJB Parent POM
8. Define the dependencyManagement definitions unique to EJB development in the ejb-parent
pom. Remove them from the current parent pom.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>javax.ejb-api</artifactId>
<version>${javax.ejb-api.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>info.ejava.examples.common</groupId>
<artifactId>jboss-rmi-client</artifactId>
<version>${ejava.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
9. Verify the exercise still builds.
$ mvn clean install
10.Remove the pluginManagement definitions already defined in the jpa-parent that we have
defined in this parent pom.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.source.version}</source>
<target>${java.target.version}</target>
</configuration>
</plugin>
11.Define the pluginManagement definitions unique to EJB development in the ejb-parent pom.
Remove them from the current parent pom.
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
102
Create Root POM
<version>${maven-ejb-plugin.version}</version>
<configuration>
<ejbVersion>3.2</ejbVersion>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>${maven-ear-plugin.version}</version>
<configuration>
<version>7</version>
</configuration>
</plugin>
<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>
<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>
103
Chapter 7. EJB Parent POM
<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>
</plugins>
</pluginManagement>
</build>
12.Verify the exercise still builds.
$ mvn clean install
13.Define the repository declarations we added for EJB development that were not in the the jpaparent pom. Remember that we used the class maven repository to locate the jboss-rmi-client
archive in order to more easily bring in JBoss dependencies into the RMI client.
<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>
104
Create Root POM
</repositories>
14.Verify the exercise still builds.
$ mvn clean install
15.Define the profiles we added for EJB development that were not in the jpa-parent pom. We
added the following profile definition to be able to perform remote debugging on an IT test
running within a full Maven build lifecycle.
<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>
</profiles>
16.Verify the exercise still builds.
$ mvn clean install
17.The following is what should remain in the application's root pom. Most other root application
poms may also look as simple now that we have off-loaded the dependency and plugin specifics
to the ejb-parent and jpa-parent root poms.
<?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>org.myorg</groupId>
<artifactId>ejb-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../ejb-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>myorg.basicejb</groupId>
<artifactId>basicejbEx</artifactId>
<packaging>pom</packaging>
<name>Basic EJB Exercise</name>
<version>1.0-SNAPSHOT</version>
<description>
This project is the root project for the example Java EE
Application.
</description>
<modules>
105
Chapter 7. EJB Parent POM
<module>basicejb-ejb</module>
<module>basicejb-ear</module>
<module>basicejb-test</module>
<module>basicejb-war</module>
</modules>
</project>
7.3. Summary
• Extracted non-application-specific definitions to new root pom
• Extends our previous JavaSE/JPA consistency to JavaEE module concerns
• Reduced complexity of local application
106