Enterprise Java Development@TOPIC@

Chapter 55. Configuring EJB Using Annotation Lookups

55.1. Setup
55.2. Inject Resources
55.3. Summary

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...

We will primarily be working with the following files

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

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.

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>
  1. 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());
    }
  2. 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.

  3. 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;
  4. Re-deploy the EJB module to the server.

    $ mvn pre-integration-test
  5. Re-run the JUnit tests. The previous failing test should succeed


  6. Activate the following test within AuditorCheckerITBase

    public abstract class AuditorCheckerITBase {
    
    ...
    //TODO enc-config 03: run this test
    @Ignore
    @Test 
    public void testPersistenceContext() throws NamingException {
  7. 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">
    ...
  8. 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;
  9. Re-deploy the EJB module to the server.

    $ mvn pre-integration-test
  10. Re-run the IT tests for the EJB module. They should now pass.

  11. 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 {
  12. 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>
  13. 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;
  14. Re-deploy the EJB module to the server.

    $ mvn pre-integration-test
  15. Re-run the IT tests for the EJB module. They should now pass.

  16. 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 {
  17. 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>
  18. 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;
  19. Re-deploy the EJB module to the server.

    $ mvn pre-integration-test
  20. Re-run the IT tests for the EJB module. They should now pass.

  21. 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 {
  22. 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.

  23. 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...

  24. Re-deploy the EJB module to the server.

    $ mvn pre-integration-test
  25. 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.