Enterprise Java Development@TOPIC@
This chapter will take a look at the easiest way to configure your EJBs -- using Annotation lookups and injection. This technique provides a 100% declarative approach to resolve resource dependencies -- which removes the need for tedious JNDI lookups and extra XML configuration files. The only negative is that vendor/configuration-specific global JNDI names are baked into the Java class as an override-able default. We will specifically...
Inject Value Resources
Inject Persistence Context
Inject Administered Resources
We will primarily be working with the following files
src/test/java/.../LookupAuditorIT.java
src/test/java/.../AuditorCheckerITBase.java
src/main/java/.../LookupAuditorEJB.java
src/main/resources/META-INF/ejb-jar.xml
src/main/resources/META-INF/persistence.xml
src/ |-- main | |-- java | | `-- org | | `-- myorg | | `-- encconfig | | `-- ejb | | |-- AuditorBase.java | | `-- LookupAuditorEJB.java | `-- resources | `-- META-INF | |-- ejb-jar.xml | `-- persistence.xml `-- test |-- java | `-- org | `-- myorg | `-- encconfig | `-- auditor | `-- ejb | |-- AuditorCheckerITBase.java | `-- it | `-- LookupAuditorEJBIT.java
All steps of this exercise do not work properly when the EJB module is deployed to JBoss within the Eclipse environment. You can run and debug the client JUnit code and server-side EJB classes within Eclipse, but you must build and deploy the changes to the EJB module using maven/cargo to achieve consistent and repeatable container behavior at this time.
Locate the encconfig-labex-ejb
module
Start JBoss. If you run JBoss within Eclipse, you may want to start it in debug mode.
Deploy the EJB module to JBoss using the maven cargo plugin
$ mvn pre-integration-test ... [INFO] --- cargo-maven2-plugin:1.4.3:redeploy (cargo-prep) @ encconfig-labex-ejb --- ... [INFO] BUILD SUCCESS
Run all IT tests within Eclipse or at the command line. They should all pass, but if you look closely -- many are being ignored until we fix some things.
You will be making edits to both IT and EJB classes. Both have a base implementation with derived classes providing technique-specific implementations for how things get injected.
Since we have consolidated all common IT @Tests into a single, common base class -- the base class can be often mistaken as being a JUnit IT test. It is not. When running the IT tests be sure to run them from the perspective of a derived IT test so the test knows how to operate in a technique-specific manner.
Try to always re-run the entire test case from JUnit and keep the concrete test case in view to make it easier to advance to the next testMethod.
Lets begin by injecting a basic boolean value into one of the Java attributes.
We have defined a java.lang.Boolean
value in the EJB's ENC using ejb-jar.xml
and assigned it to the val/publishJMS
ENC name.
<session>
<ejb-name>LookupAuditorEJB</ejb-name>
<env-entry>
<env-entry-name>val/publishJMS</env-entry-name>
<env-entry-type>java.lang.Boolean</env-entry-type>
<env-entry-value>true</env-entry-value>
</env-entry>
</session>
Remove the @Ignore on the testPublishJMS
test method within AuditorCheckerITBase
to enable it.
public abstract class AuditorCheckerITBase {
...
//TODO enc-config 01: run this test
@Ignore
@Test
public void testPublishJMS() throws NamingException {
log.info("*** testPublishJMS() ***");
assertTrue("publishJMS value not injected",
getAuditor().isPublishJMS());
}
Re-run the IT tests. You should get a failure for the test you just activated.
java.lang.AssertionError: publishJMS value not injected
The test checks to see if the publishJMS
EJB property has been set and fails because nothing
was injected into the EJB.
Activate the injection for the property within the
LookupAuditorEJB
class using a @Resource annotation.
This will inject the value from the val/publishJMS
ENC name put in place earlier by the XML deployment descriptor.
@Stateless
public class LookupAuditorEJB extends AuditorBase
...
//TODO enc-config 02: define a resource value injection here
//@Resource(name="val/publishJMS")
private Boolean publishJMS;
Re-deploy the EJB module to the server.
$ mvn pre-integration-test
Re-run the JUnit tests. The previous failing test should succeed
You can get a better sense of what is and is not being set by setting a breakpoint in the EJB method on the server, pausing the EJB during the business method, and inspecting the variable values for the EJB during the call.
Activate the following test within AuditorCheckerITBase
public abstract class AuditorCheckerITBase {
...
//TODO enc-config 03: run this test
@Ignore
@Test
public void testPersistenceContext() throws NamingException {
Re-run the IT tests for the EJB module. The test you just activated should now fail with the following error.
java.lang.AssertionError: persistence context not injected
The injection source for the persistence context is being
supplied by a META-INF/persistence.xml
file
within the module
`-- META-INF
|-- ejb-jar.xml
|-- jboss-ejb3.xml
|-- MANIFEST.MF
`-- persistence.xml
...
<persistence-unit name="encconfig-lab">
...
Inject the persistence context into the EJB property
using the @javax.persistence.Persistence
annotation
and supply the encconfig-lab
persistence unit name
that comes from the persistence.xml
file
@Stateless
public class LookupAuditorEJB extends AuditorBase
...
//TODO enc-config 04: define a persistence context injection here
//@PersistenceContext(unitName="encconfig-lab")
private EntityManager em;
Re-deploy the EJB module to the server.
$ mvn pre-integration-test
Re-run the IT tests for the EJB module. They should now pass.
Activate the following IT test within AuditorCheckerITBase
public abstract class AuditorCheckerITBase {
...
//TODO enc-config 05: run this test
@Ignore
@Test
public void testTopic() throws NamingException {
Re-run the IT tests for the EJB module. The test you just activated should fail with the following error.
java.lang.AssertionError: topic not injected
The injection source for the JMS topic comes from the administered JMS resources within the application server. The profile we are using comes with a stock set of example resources to use during demonstrations such as this.
# standalone/configuration/standalone.xml
<jms-topic name="testTopic">
<entry name="topic/test"/>
<entry name="java:jboss/exported/jms/topic/test"/>
</jms-topic>
Inject the JMS Topic located at the java:/topic/test
using the @javax.annotation.Resource
annotation and the
lookup attribute. You can locate the definition of this resource within the JBoss
standalone-full.xml
profile.
@Stateless
public class LookupAuditorEJB extends AuditorBase
...
//TODO enc-config 06: define a resource value lookup and injection here
//@Resource(lookup="java:/topic/test")
private Topic topic;
Injecting administered server resources using direct JNDI names is a quick and convenient default but ties the deployed code the the specific names of the server. We will look at how to override that injection later.
Re-deploy the EJB module to the server.
$ mvn pre-integration-test
Re-run the IT tests for the EJB module. They should now pass.
Activate the following IT test within AuditorCheckerITBase
public abstract class AuditorCheckerITBase {
...
//TODO enc-config 07: run this test
@Ignore
@Test
public void testConnectionFactory() throws NamingException {
Re-run the IT tests for the EJB module. The test you just activated should fail with the following error.
java.lang.AssertionError: connection factory not injected
The injection source for the JMS connection factory also comes from the administered JMS resources within the application server.
# standalone/configuration/standalone.xml
<pooled-connection-factory name="hornetq-ra">
<transaction mode="xa"/>
<connectors>
<connector-ref connector-name="in-vm"/>
</connectors>
<entries>
<entry name="java:/JmsXA"/>
<entry name="java:jboss/DefaultJMSConnectionFactory"/>
</entries>
</pooled-connection-factory>
Inject the JMS ConnectionFactory located at the java:/JmsXA
using the @javax.annotation.Resource
annotation and the
lookup attribute. You can locate the definition of this resource within the JBoss
standalone-full.xml
profile.
@Stateless
public class LookupAuditorEJB extends AuditorBase
...
//TODO enc-config 06: define a resource lookup and injection here
//@Resource(lookup="java:/JmsXA")
private ConnectionFactory cf;
Re-deploy the EJB module to the server.
$ mvn pre-integration-test
Re-run the IT tests for the EJB module. They should now pass.
The above step registered the last of the required resources. It is
now time to see if our EJB is ready to function with these resources by invoking
a business method that makes use of all of the above. Enable to last test
method in AuditorCheckerITBase
.
public abstract class AuditorCheckerITBase {
...
//TODO enc-config 09: run this test
@Ignore
@Test
public void testAudit() throws NamingException {
Re-run the IT tests for the EJB module. The test you just activated should fail with the following error.
java.lang.AssertionError: EJB not properly initialized expected:<2> but was:<0>
The (successes and current) error indicates that although you have successfully in injected the resources into EJB properties -- there is still some unfinished EJB initialization that needs to take place.
Activate the EJB's initialization method using the
@javax.annotation.PostConstruct
annotation.
This tells the EJB container to call out init()
method after all injections are complete. At this point all
injected resources are safe to use. The method is required to
accept and return no arguments and to not throw any checked exceptions.
All resources and the EJB will be discarded if this method throws
an unchecked exception.
@Stateless
public class LookupAuditorEJB extends AuditorBase
...
//TODO enc-config 10: activate this method after injection occurs
//@PostConstruct
public void init() {
super.setLog(log);
super.setConnectionFactory(cf);
super.setEntityManager(em);
super.setTopic(topic);
super.setPublishJMS(isPublishJMS());
}
The container...
Determines an instance of the EJB is needed to satisfy client request
Instantiates the EJB class
Performs injection
Calls the EJB's @PostConstruct method
Makes EJB available for dispatch of business methods
Re-deploy the EJB module to the server.
$ mvn pre-integration-test
Re-run the IT tests for the EJB module. They should now pass.
You have finished configuring an EJB with value resources, a persistence context, and administered JMS resources using annotations.
In this chapter you successfully injected various resources using just Java class annotations. Where necessary, you supplied a JNDI name to the lookup attribute so the container could find the resources and inject them into your EJB. The technique used here is attractive because it is simple -- everything is within a single Java class. In the next chapters we will look at techniques to remove the JNDI name specifications from the Java class.