Enterprise Java Development@TOPIC@
Unit tests test POJO classes in a single Maven phase
IT tests test components deployed to the server using multiple phases Maven Lifecycle Reference
pre-integration-test
Deploy artifacts
integration-test
Execute tests
post-integration-test
Undeploy artifacts
verify
Evaluate test results (possibly fail build after artifacts undeployed)
IT tests require remote access
Access Type
JBoss Remoting
EJB Client
JNDI Properties
JNDI Name
IT tests test EJB deployed to server
Java client may access @Remote interface of EJB by first going through Java Naming and Directory Interface (JNDI)
JBoss uses two different types of remote interface techniques -- both accessed thru JNDI; JBoss Remoting and EJB Client
Obtain JNDI properties through combination of jndi.properties file and supplied Properties object.
java.util.Properties jndiProperties=...; // varies whether using Remoting or EJBClient
|-- jndi.properties java.naming.factory.initial=... java.naming.factory.url.pkgs=... java.naming.provider.url=... #java.naming.security.principal=... #java.naming.security.credentials=...
Security properties shown here for completeness. Can be statically defined.
Get InitialContext to server JNDI tree
Context jndi = new InitialContext(jndiProperties); //jndi.properties augmented
Context jndi = new InitialContext(); //jndi.properties is complete
Obtain JNDI-name using deployment-specific name and access technique
String jndiName; // varies whether accessing EJB, WAR or EAR deployment and access
Lookup EJB's remote interface
GreeterRemote greeter = (GreeterRemote) jndi.lookup(jndiName);
Legacy approach
Follows published standards
Consistent with other resource types (e.g., JMS resource lookups)
Knows nothing of EJB
Stated to be less efficient than EJB Client
java.naming.factory.initial=org.wildfly.naming.client.WildFlyInitialContextFactory java.naming.factory.url.pkgs= java.naming.provider.url=http-remoting://127.0.0.1:8080 #java.naming.security.principal=known #java.naming.security.credentials=password1!
Credentials are strictly for JNDI lookups
Credentials are optional when client connecting from same machine as server (i.e., development mode)
ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
ejb-basic-war/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
ejb-basic-ear/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
ejb-basic-war/WebGreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
Custom API for accessing EJBs
Can perform certain actions more efficiently with that knowledge and specialization
Uses separate naming context (ejb:)
Uses different and separate properties
Specifics can be shielded from client with minimal effort
#java.naming.factory.initial java.naming.factory.url.pkgs=org.jboss.ejb.client.naming java.naming.provider.url=http-remoting://127.0.0.1:8080
java.naming.factory.initial
can be anything legal and not blank
The WildFlyInitialContextFactory can handle both Remoting and EJB Client JNDI setup with the same jndi.properties configuration. The specific extension shown above was necessary in past versions and only shown here as an example to show more details of how the "ejb:" prefix and "java.naming.factory.url.pkgs" is being used.
EJB Client contains other details like alternate URLs, authentication configuration, and encryption options that go beyond basic communications but address more detailed use of remote interfaces. These details are configured in a separate configuration file that is specific to JBoss and will not be covered here.
ejb:/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
ejb:/ejb-basic-war/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
ejb:ejb-basic-ear/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
ejb:/ejb-basic-war/WebGreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
The EJB Client implementation understands JBoss/Wildfly EJBs and to further help optimize the communication with Stateful Session EJBs the following string ("?stateful")must be appended to the JNDI name
InitialContextFactory looks for custom naming extensions when encountering naming prefix ("ejb:")
java.naming.factory.url.pkgs lists base package names to start looking for extensions
jboss-ejb-client.jar must be in the classpath
Provider extracts "ejb" prefix from "ejb:/..." JNDI name
ejb:/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
Provider obtains list of Java package prefixes from "jndi.naming.factory.url.pkgs"
#jndi.properties java.naming.factory.url.pkgs=org.jboss.ejb.client.naming
Provider searches for "org.jboss.ejb.client.naming.ejb" Java package in classpath
$ mvn dependency:tree ... [INFO] +- info.ejava.examples.common:jboss-rmi-client:pom:5.0.0-SNAPSHOT:test [INFO] | | +- org.jboss:jboss-ejb-client:jar:4.0.10.Final:test [INFO] | | +- org.jboss.remoting:jboss-remoting:jar:5.0.5.Final:test [INFO] | | +- org.wildfly:wildfly-naming-client:jar:1.0.9.Final:test [INFO] | | +- org.wildfly.wildfly-http-client:wildfly-http-naming-client:jar:1.0.12.Final:test ...
Provider locates an ObjectFactory class in the "org.jboss.ejb.client.naming" Java package that starts with "ejb"
$ jar tf ~/.m2/repository/org/jboss/jboss-ejb-client/4.0.10.Final/jboss-ejb-client-4.0.10.Final.jar | grep org.jboss.ejb.client.naming org/jboss/ejb/client/naming/ejb/ejbURLContextFactory.class
Provider uses that ObjectFactory to resolve the remaining portion of the JNDI name
ejb:/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
EJB Client only works for EJBs
InitialContext may need to locate other non-EJB resources (e.g., JMS)
Replace default InitialContextFactory with JBoss Remoting
Extend JBoss Remoting InitialContextFactory with EJB Client
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
However, ejbURLContextFactory is deprecated in favor of using WildFlyInitialContextFactory. The following is a bit easier to remember and configure.
java.naming.factory.initial=org.wildfly.naming.client.WildFlyInitialContextFactory java.naming.factory.url.pkgs= java.naming.provider.url=http-remoting://localhost:8080
IT tests require...
Server running (possibly started)
EJB deployed
Tests run
EJB undeployed (proper cleanup)
Server stopped (if started as part of the test)
Results evaluated
IT test can also be written with JUnit
@BeforeClass runs at start of test case and before all test methods. This must be public and static
@Before runs before each test method. This must be public and non-static
@After runs after each test method. This must be public and non-static
@AfterClass runs after all test methods are complete and prior to test case shutdown. This must be public and static
...
import static org.junit.Assert.*;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import info.ejava.examples.ejb.basic.dto.Greeting;
import info.ejava.examples.ejb.basic.dto.Name;
import info.ejava.examples.ejb.basic.ejb.BadRequestException;
import info.ejava.examples.ejb.basic.ejb.GreeterRemote;
...
public class GreeterIT {
private static final Logger logger = LoggerFactory.getLogger(GreeterBase.class);
protected Properties jndiProperties; // varies whether using Remoting or EJBClient
protected String jndiName; // varies whether accessing EJB, WAR or EAR deployment and access
protected Context jndi;
protected GreeterRemote greeter;
@Before
public void setUp() throws Exception {
jndi = new InitialContext(jndiProperties);
greeter = (GreeterRemote) jndi.lookup(jndiName);
}
@After
public void tearDown() throws Exception {
if (jndi != null) {
jndi.close(); // produces errors with JBoss Remoting
}
}
@Before method sets up JNDI context and performs lookup for @Remote before each @Test
@After method closes out JNDI Context between @Tests
Same as before except they must use the EJB remote interface rather than instantiate bean
@Test
public void pojoGreeter() throws BadRequestException {
logger.info("*** pojoGreeter ***");
String name = "cat inhat";
String greeting = greeter.sayHello(name);
assertTrue("greeter did not say my name", greeting.contains(name));
}
@Test(expected = BadRequestException.class)
public void badName() throws BadRequestException {
logger.info("*** badName ***");
greeter.sayHello("");
}
@Test
public void dto() throws BadRequestException {
logger.info("*** dto ***");
Name name = new Name("thing", "one");
Greeting greeting = greeter.sayHello(name);
assertTrue("greeter did not say my name", greeting.getGreeting()
.contains(name.getFirstName()));
}
IT tests housed in separate modules (can be part of EJB and WAR modules)
jndi.properties part of IT test tree -- not the production code
. |-- pom.xml `-- src `-- test |-- java | `-- info | `-- ejava | `-- examples | `-- ejb | `-- basicejb | `-- ejbclient | |-- GreeterIT.java `-- resources |-- jndi.properties `-- log4j.xml
surefire/Unit Tests run prior to deployment
failsafe/IT tests run after deployment
$ mvn clean verify ... [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ ejb-basic-test --- [INFO] Deleting .../ejb-basic-example/ejb-basic-test/target ... [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ ejb-basic-test --- [INFO] No sources to compile [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ ejb-basic-test --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 4 resources [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ ejb-basic-test --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 5 source files to .../ejb-basic-example/ejb-basic-test/target/test-classes [INFO] [INFO] --- maven-surefire-plugin:2.17:test (default-test) @ ejb-basic-test --- [INFO] [INFO] --- maven-jar-plugin:2.5:jar (default-jar) @ ejb-basic-test --- [WARNING] JAR will be empty - no content was marked for inclusion! [INFO] Building jar: .../ejb-basic-example/ejb-basic-test/target/ejb-basic-test-4.0.0-SNAPSHOT.jar ... [INFO] --- cargo-maven2-plugin:1.4.3:redeploy (cargo-prep) @ ejb-basic-test --- Sep 28, 2014 11:58:23 PM org.xnio.Xnio <clinit> INFO: XNIO version 3.2.2.Final Sep 28, 2014 11:58:23 PM org.xnio.nio.NioXnio <clinit> INFO: XNIO NIO Implementation Version 3.2.2.Final Sep 28, 2014 11:58:23 PM org.jboss.remoting3.EndpointImpl <clinit> INFO: JBoss Remoting version 4.0.3.Final ... [INFO] --- maven-failsafe-plugin:2.17:integration-test (integration-tests) @ ejb-basic-test --- [INFO] Failsafe report directory: .../ejb-basic-example/ejb-basic-test/target/failsafe-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running info.ejava.examples.ejb.basicejb.ejbclient.GreeterRemotingWARIT 23:58:26,441 INFO (GreeterIT.java:43) -using jndiName=ejb-basic-war/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote ... 23:58:27,811 INFO (GreeterIT.java:43) -using jndiName=ejb:ejb-basic-ear/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote ... 23:58:28,167 INFO (GreeterIT.java:43) -using jndiName=ejb-basic-ear/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote ... 23:58:28,289 INFO (GreeterIT.java:43) -using jndiName=ejb:/ejb-basic-war/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote Results : Tests run: 12, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] --- cargo-maven2-plugin:1.4.3:undeploy (cargo-post) @ ejb-basic-test --- [INFO] [INFO] --- maven-failsafe-plugin:2.17:verify (verify) @ ejb-basic-test --- [INFO] Failsafe report directory: .../ejb-basic-example/ejb-basic-test/target/failsafe-reports [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 11.036 s