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