Enterprise Java Development@TOPIC@
In this chapter we will keep populating the JNDI ENC via XML but we will revert back to an older configuration technique that requires a programmatic lookup of resources within the JNDI ENC. This technique allows for deployment of an EJB to be separated from the resource availability -- permitting a "lazy" lookup of the resources at runtime. It also blazes a trail for standard POJO classes to perform resource lookups within their hosting component's ENC.
The @Resource
and @PersistenceContext
injections
so far work only with EJB bean classes. We want to show how standard POJO
classes, embedded within the EJB bean, will also have access to resources without
being coupled to a specific EJB or other JavaEE component type. The embedded
POJO can use standard JNDI lookups to locate the resources within the local
component's JNDI ENC context. Although this lab does not instantiate a
POJO class for this purpose -- it does demonstrate what the POJO classes
must do and places that code within the @PostConstruct
.
Since we are still within the EJB bean, we will be using a mixture of
InitialContext/java:comp/env lookups and SessionContext lookups.
One other change made during this chapter is the movement of the
lookup JNDI names have been moved to the vendor-specific
META-INF/jboss-ejb3.xml
file. The core/standard ENC configuration
is placed within the META-INF/ejb-jar.xml
and used on multiple platforms
and platform configurations. The additional file would be unique to the
JBoss platform and our specific configuration of JBoss resources.
We will primarily be working with the following files
src/test/java/.../JNDIAuditorIT.java
src/main/java/.../JNDIAuditorEJB.java
src/main/resources/META-INF/ejb-jar.xml
src/main/resources/META-INF/jboss-ejb3.xml
src/ |-- main | |-- java | | `-- org | | `-- myorg | | `-- encconfig | | `-- ejb | | `-- JNDIAuditorEJB.java | `-- resources | `-- META-INF | |-- ejb-jar.xml | `-- jboss-ejb3.xml `-- test |-- java | `-- org | `-- myorg | `-- encconfig | `-- auditor | `-- ejb | `-- it | `-- JNDIAuditorEJBIT.java
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 still being ignored until we fix some things.
Activate the JNDIAuditorEJBIT
test case.
Since this test case extends the same base class as the previous
two test cases (where we activated all the test methods) all test
methods will immediately become active.
//TODO enc-config 17: activate this testcase
@Ignore
public class JNDIAuditorEJBIT extends AuditorCheckerITBase {
Re-run the IT tests for the EJB module. The test case you just activated above will fail with several errors
java.lang.AssertionError: publishJMS value not injected java.lang.AssertionError: persistence context not injected java.lang.AssertionError: topic not injected java.lang.AssertionError: connection factory not injected java.lang.AssertionError: EJB not properly initialized expected:<2> but was:<0>
Activate the PostCostruct initialization method for the
EJB under test. This method depends on an injected SessionContext
for doing ENC lookups and instantiates a default InitialContext to
simulate what lower-level components can do with the java:comp/env
namespace.
@Stateless
public class JNDIAuditorEJB extends AuditorBase
...
private @Resource SessionContext ctx;
//TODO enc-config 18: activate initialization method to perform ENC lookups
//@PostConstruct
public void init() {
InitialContext jndi = null;
try {
jndi=new InitialContext();
...
} catch (NamingException ex) {
log.error("error looking up resources", ex);
throw new EJBException("error looking up resources:" + ex);
} finally {
close(jndi);
}
super.setLog(log);
super.setConnectionFactory(cf);
super.setEntityManager(em);
super.setTopic(topic);
super.setPublishJMS(isPublishJMS());
}
Correct the first issue by looking up the value
from the JNDI ENC placed there by the META-INF/ejb-jar.xml
<session>
<ejb-name>JNDIAuditorEJB</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>
The value can be looked up using an injected SessionContext
and the ENC name used by the XML and in the previous chapter.
Perform a programmatic lookup of the ENC value and assign
it to the publishJMS
Java attribute.
@Stateless
public class JNDIAuditorEJB extends AuditorBase
...
//TODO enc-config 19: lookup resource value in ENC
//publishJMS = (Boolean) ctx.lookup("val/publishJMS");
Re-deploy the EJB module using the maven cargo plugin
$ mvn pre-integration-test
Re-run the IT tests. The test you addressed above should now pass.
Correct the next issue by looking up the persistence
context from the JNDI ENC placed there by the META-INF/ejb-jar.xml
<persistence-context-ref>
<persistence-context-ref-name>jpa/em</persistence-context-ref-name>
<persistence-unit-name>encconfig-lab</persistence-unit-name>
</persistence-context-ref>
Perform the ENC lookup using the injected SessionContext
and the ENC name. Assign the value to the em
Java
attribute with the JNDIAuditorEJB
class.
@Stateless
public class JNDIAuditorEJB extends AuditorBase
...
//TODO enc-config 20: lookup persistence context in ENC
//em = (EntityManager) ctx.lookup("jpa/em");
Although it would be nicer to simply have the value injected as
we did in the previous chapter -- the lookup is made simple using
the injected SessionContext
. This injected object
is specific to being an EJB component and not available to all
Java classes running in the server. We will address that aspect
for the follow-on properties.
Re-deploy the EJB module using the maven cargo plugin
$ mvn pre-integration-test
Re-run the IT tests. The test you addressed above should now pass.
Correct the next issue by looking up the administered JMS topic
from the JNDI ENC placed there by the META-INF/ejb-jar.xml
<resource-env-ref>
<resource-env-ref-name>jms/topic</resource-env-ref-name>
<resource-env-ref-type>javax.jms.Topic</resource-env-ref-type>
</resource-env-ref>
The JNDI name is being supplied by the META-INF/jboss-ejb3.xml file.
<?xml version="1.0"?>
<jboss:ejb-jar
...
<enterprise-beans>
<session>
<ejb-name>JNDIAuditorEJB</ejb-name>
...
<resource-env-ref>
<resource-env-ref-name>jms/topic</resource-env-ref-name>
<resource-env-ref-type>javax.jms.Topic</resource-env-ref-type>
<jndi-name>java:/topic/test</jndi-name>
</resource-env-ref>
</session>
</jboss:ejb-jar>
By separating the platform-neutral ENC definitions in the ejb-jar.xml from the platform-specific JNDI names in the jboss-ejb3.xml you can better isolate fixed and templated files during module builds if your module must operate in different deployment environments.
Perform the ENC lookup using an InitialContext
and the ENC name prefixed with java:comp/env
.
Assign the value to the topic
Java
attribute with the JNDIAuditorEJB
class.
//TODO enc-config 21: lookup resource in ENC using JNDI java:comp/env
//topic = (Topic) jndi.lookup("java:comp/env/jms/topic");
Notice how this technique is independent of the code being within the EJB bean class. Any Java class can instantiate a default InitialContext and lookup the ENC for its hosting component using the "java:comp/env" context. This is functionally the same as using the the shorter name with the SessionContext.lookup() except that it can be used outside of the EJB.
Re-deploy the EJB module using the maven cargo plugin
$ mvn pre-integration-test
Re-run the IT tests. The test you addressed above should now pass.
Correct the next issue by looking up the administered JMS connection
factory from the JNDI ENC placed there by the META-INF/ejb-jar.xml
<resource-ref>
<res-ref-name>jms/cf</res-ref-name>
<res-type>javax.jms.ConnectionFactory</res-type>
</resource-ref>
The JNDI name is being supplied by the jboss-ejb3.xml
file.
<resource-ref>
<res-ref-name>jms/cf</res-ref-name>
<res-type>javax.jms.ConnectionFactory</res-type>
<jndi-name>java:/JmsXA</jndi-name>
</resource-ref>
Perform the ENC lookup using the InitialContext
and the ENC name prefixed with java:comp/env
.
Assign the value to the cf
Java
attribute with the JNDIAuditorEJB
class.
//TODO enc-config 22: lookup resource in ENC using JNDI java:comp/env
//cf = (ConnectionFactory) jndi.lookup("java:comp/env/jms/cf");
Re-deploy the EJB module using the maven cargo plugin
$ mvn pre-integration-test
Re-run the IT tests. This time all tests for the JNDIAuditorEJBIT
test case should now pass -- including the testAudit since all resources have now
been injected and the @PostConstruct
method has been activated to push
those resources up the to base class helper.
You have finished configuring your EJB using programmatic JNDI lookups of resources populated in the ENC using XML descriptor files.
In this chapter you successfully activated a @PostConstruct
method
in the EJB bean class that performed programmatic JNDI lookups of the ENC
using an injected SessionContext
and alternately an InitialContext
.
Lookups that used the SessionContext
could use the core ENC name. Lookups
that used the InitialContext
had to prefix the ENC name with the
string java:comp/env/
. The SessionContext
is unique to being an EJB
class. The InitialContext
can be used by any Java class delegated to by the EJB.
This technique has the benefit of keeping the global/physical JNDI names away from the Java classes but requires a programmatic lookup -- rather than injection. This would only be desirable for helper classes delegated to by the EJB. In the next section we will use a declarative approach that requires no knowledge of any resource injection by the EJB bean. All will be handled by the XML deployment descriptor.