Using Glassfish, I can setup a string jndi entry:
JNDI name: "com/xyzcompany/echo/EchoServiceBean/viewName" Factory Class: org.glassfish.resources.custom.factory.PrimitivesAndStringFactory Properties: value="Testing123"
I can then inject this container configured string into my EJB:
@Resource(lookup = "com/xyzcompany/echo/EchoServiceBean/viewName") String viewName;
The lookup= appears to internally do an InitialContext.lookup(...). However, this uses ejb3.1, but unfortunately my prod environment is only ejb3.0.
I guess i'm trying to figure out is there a way to use @Resource(name=) or @Resource(mappedName=) to do something similar? name= appears to be application specific, so I should be able to somehow map a relative name to a global JNDI name, but I can't figure out what annotation does the mapping.
Thanks!
All 8 primitive wrappers and String are supported @Resource types and are available for lookup or injection via declaring them in the standard ejb-jar.xml file.
This is done with the <env-entry>
xml element in deployment descriptor.
In EJB 3.0 you have to do this for each bean that wishes to reference the same name/value pairs. This is because EJB was originally designed different than Servlets and each EJB literally gets its own private JNDI namespace, java:comp/env
, whereas all Servlets in the same module share the same java:comp/env
.
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>MySessionBean</ejb-name>
<env-entry>
<env-entry-name>myBoolean</env-entry-name>
<env-entry-type>java.lang.Boolean</env-entry-type>
<env-entry-value>true</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>myString</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>hello world</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>myDouble</env-entry-name>
<env-entry-type>java.lang.Double</env-entry-type>
<env-entry-value>1.1</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>myLong</env-entry-name>
<env-entry-type>java.lang.Long</env-entry-type>
<env-entry-value>12345678</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>myFloat</env-entry-name>
<env-entry-type>java.lang.Float</env-entry-type>
<env-entry-value>1.3</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>myInteger</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>1024</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>myShort</env-entry-name>
<env-entry-type>java.lang.Short</env-entry-type>
<env-entry-value>42</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>myByte</env-entry-name>
<env-entry-type>java.lang.Byte</env-entry-type>
<env-entry-value>128</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>myCharacter</env-entry-name>
<env-entry-type>java.lang.Character</env-entry-type>
<env-entry-value>D</env-entry-value>
</env-entry>
</session>
</enterprise-beans>
</ejb-jar>
For readers lucky enough to be using EJB 3.1, you can use global JNDI and declare them in the application.xml and look them up from anywhere via java:app/myString
. A feature most vendors have had for years which is now finally standard as of Java EE 6. Injection of those entries is also possible via @Resource(lookup="java:app/myString")
Also new in Java EE 6 is support for two extra env-entry-type
types, java.lang.Class and any enum. For example:
<env-entry>
<env-entry-name>myPreferredListImpl</env-entry-name>
<env-entry-type>java.lang.Class</env-entry-type>
<env-entry-value>java.util.ArrayList</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>myBillingStragety</env-entry-name>
<env-entry-type>java.lang.Class</env-entry-type>
<env-entry-value>org.superbiz.BiMonthly</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>displayElapsedTimeAs</env-entry-name>
<env-entry-type>java.util.concurrent.TimeUnit</env-entry-type>
<env-entry-value>MINUTES</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>myFavoriteColor</env-entry-name>
<env-entry-type>org.superbiz.ColorEnum</env-entry-type>
<env-entry-value>ORANGE</env-entry-value>
</env-entry>
Any of the above can be injected via @Resource
. Just don't forget to fill in the name
attribute to match the <env-entry-name>
@Stateless
public class MySessionBean implements MySessionLocal {
@Resource(name="myString")
private String striing;
@Resource(name = "myDouble")
private Double doouble;
@Resource(name = "myLong")
private Long loong;
@Resource(name = "myName")
private Float flooat;
@Resource(name = "myInteger")
private Integer inteeger;
@Resource(name = "myShort")
private Short shoort;
@Resource(name = "myBoolean")
private Boolean booolean;
@Resource(name = "myByte")
private Byte byyte;
@Resource(name = "myCharacter")
private Character chaaracter;
}
These names can also be standardly looked up via the javax.naming.InitialContext in the EJBs private and portable java:comp/env
namespace.
@Stateless
public class MySessionBean implements MySessionLocal {
@PostConstruct
private void init() {
try {
final InitialContext initialContext = new InitialContext();// must use the no-arg constructor
final String myString = (String) initialContext.lookup("java:comp/env/myString");
final Boolean myBoolean = (Boolean) initialContext.lookup("java:comp/env/myBoolean");
final Double myDouble = (Double) initialContext.lookup("java:comp/env/myDouble");
final Long myLong = (Long) initialContext.lookup("java:comp/env/myLong");
final Float myFloat = (Float) initialContext.lookup("java:comp/env/myFloat");
final Integer myInteger = (Integer) initialContext.lookup("java:comp/env/myInteger");
final Short myShort = (Short) initialContext.lookup("java:comp/env/myShort");
final Byte myByte = (Byte) initialContext.lookup("java:comp/env/myByte");
final Character myCharacter = (Character) initialContext.lookup("java:comp/env/myCharacter");
} catch (NamingException e) {
throw new EJBException(e);
}
}
}
In EJB 3.0 as part of the simplification effort we added the ability to use the javax.ejb.SessionContext
to do lookups. It is essentially the same, but has a little bit of sugar on it.
java:comp/env
prefix is not requiredService Locator patterns were all the buzz in 2003 so we decided to build a little bit of convenience into the EJB API.
@Stateless
public class MySessionBean implements MySessionLocal {
@Resource
private SessionContext sessionContext;
@PostConstruct
private void init() {
final String myString = (String) sessionContext.lookup("myString");
final Boolean myBoolean = (Boolean) sessionContext.lookup("myBoolean");
final Double myDouble = (Double) sessionContext.lookup("myDouble");
final Long myLong = (Long) sessionContext.lookup("myLong");
final Float myFloat = (Float) sessionContext.lookup("myFloat");
final Integer myInteger = (Integer) sessionContext.lookup("myInteger");
final Short myShort = (Short) sessionContext.lookup("myShort");
final Byte myByte = (Byte) sessionContext.lookup("myByte");
final Character myCharacter = (Character) sessionContext.lookup("myCharacter");
}
}
Also, with my vendor hat on, I can tell you that there's a fair bit of slow plumbing that can be avoided under the hood with the SessionContext
lookup.
When you do 'java:' lookups on an InitialContext
, the call goes to the VM, through a bunch of hoops to find who can resolve that name, then eventually to the vendor who will have to lookup state from the thread to figure out who asked and what namespace they're supposed to get. It does this on each and every call no matter what properties you pass into the InitialContext and what context the vendor initialized in its construction. The 'java:' simply jumps over all that. It's a rather frustrating part of being a vendor. It's also why the new javax.ejb.embedded.EJBContainer
API does not use InitialContext
anywhere at all and just references javax.naming.Context
which is an actual interface rather than a concrete "factory" class with intense and obtuse plumbing.
Doing the call on SessionContext should be much faster if the vendor did it right. In OpenEJB at least, all the above including the ThreadLocal is skipped and the call goes right into that bean's JNDI namespace which is already attached to the SessionContext
.
Another way to avoid the InitialContext
overhead is to simply lookup java:comp/env
once in the @PostConstruct and keep that resulting Context
object and only use that. Then don't prefix lookups with java:comp/env/
and just lookup the names directly such as myString
and myInteger
. It will be faster, guaranteed.