Configure your EJB 3 with envirnoment entries using ENC

Very often you can read ” xml descriptors are dead with EJB 3 “. Well, it’s not exactly true. XML descriptors are not dead, they are optional (thank god). But there is still a very good and legitimate use for them when you want to configure an EJB. When you have an attribute in an EJB which value can change at deployment, you don’t want to change your code, recompile it, package it and redeploy the EJB. No, you just want to change an XML file (ejb-jar.xml), package it with your classes and deploy the EJB. So, how do we do that with EJB 3 ? Still using ENC ( environment namming context also sometimes called enterprise namming context ) but in a much easier way.

ENC has been around since EJB 1.0 and can be seen as a local JNDI namespace specific to the container. You can bind many different items in the ENC (EJB references, JMS queues, topics, data source, user transaction…) but I will have a look at how to bind and use primitive values. Anything registered in the ENC can be looked up by name under the java:/com/env context ( comp means component ). Let’s take a simple stateless bean which defines a String str initialized with Hello World !!!.

@Stateless
public class ABean implements ABeanRemote {

    private String str = "Hello  World !!!";

    public String sayHello() {
        return ">>> " + str;
    }
}

Now, if you want to override the “Hello World !!!” value, better to deploy your EJB with a ejb-jar.xml, set an environment entry and assign it to a different value. In the following ejb-jar.xml the name of the entry is my/hello/string and the value Hello From ejb-jar.xml file !!!

<ejb-jar>
<enterprise-beans>
<session>

<ejb-name>ABean</ejb-name>
<ejb-class>stateless.ABean</ejb-class>
<env-entry>
<env-entry-name>my/hello/string</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>Hello From ejb-jar.xml file !!!</env-entry-value>
</env-entry>

</session>
</enterprise-beans>
</ejb-jar>

Now here are the different ways to access this entry from your EJB :

@Stateless
public class ABean implements ABeanRemote {

    @Resource(name="my/hello/string") private String str = "Hello  World !!!";
    @Resource private SessionContext sessionContext;

    public String sayHelloWithResource() {
        return ">>> " + str;
    }

    public String sayHelloWithJNDILookup() {
        String tempString = null;
        try {
            InitialContext ctx = new InitialContext();
            tempString = (String) ctx.lookup("java:comp/env/my/hello/string");
        } catch (NamingException ex) {
            ex.printStackTrace();
        }
        return ">>> " + tempString;
    }

    public String sayHelloWithEJBContextLookup() {
        String tempString = (String) sessionContext.lookup("my/hello/string");
        return ">>> " + tempString;
    }

}

sayHelloWithResource : this method uses the Resource annotation. That means that the str String is annotated with Resource giving the ENC name my/hello/string (without java:/com/env ). That does the lookup for you and overrides the initial value (that‘s the default behavior, XML always overrides). The @Resource annotation is the prefered way when dealing with EJB 3.

sayHelloWithJNDILookup : in this method I use the good old lookup with InitialContext and NamingException. As you can see, I have to prefix the name by the ENC context ( java:comp/env/my/hello/string )

sayHelloWithEJBContextLookup : more elegant than the JNDI lookup, I can use the EJBContext.lookup() method. This method is a shortcut that looks up a resource within the component’s private naming context. If also wraps the NamingException and throws the uncheck exception IllegalArgumentException (no dirty try/catch)

There is a fourth way, it‘s the infection within the ejb-jar.xml. What you can do is directly inject a value into an attribute with the tag :

<ejb-jar>
<enterprise-beans>
<session>

<ejb-name>ABean</ejb-name>
<ejb-class>stateless.ABean</ejb-class>
<env-entry>
<env-entry-name>my/hello/string</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>Hello From ejb-jar.xml file !!!</env-entry-value>
<injection-target>
<injection-target-class>stateless.ABean</injection-target-class>
<injection-target-name>injectedString</injection-target-name>
</injection-target>
</env-entry>

</session>
</enterprise-beans>
</ejb-jar>

This assumes that your EJB has an attribute called injectedString with a getter and a setter. Your EJB code looks like that :

@Stateless
public class ABean implements ABeanRemote {

    private String injectedString = "Hello  World !!!";

    public String sayHelloWithInjection() {
        return ">>> " + getInjectedString();
    }

    public String getInjectedString() {
        return injectedString;
    }

    public void setInjectedString(String injectedString) {
        this.injectedString = injectedString;
    }
}

As you can see, you don‘t even need the Resource annotation, the injection is done automatically. I don't particularly like this solution because it's hard to debug. When you look at the EJB code, there's no way you can see that the method sayHelloWithInjection will return "Hello From ejb-jar.xml file !!!". And because most people take the "xml descriptors are dead with EJB 3" for granted, you will not remember to look at the ejb-jar.cml. I really prefer the nice Resource annotation way.

But, and there is always a but, the Resource annotation doesn't work for helper classes (and god knows that an EJB always comes with helper classes). I know what the Spring guys are saying and they are right. Resource and injection doesn’t work on POJOs. Java EE 5 does not support the environment annotations for helper classes. You will have to look up any entries within the component environment using java:comp/env .

What I‘ve just shown you works for ejb-jar.xml and EJBs as well as web.xml and servlets.

Leave a Reply