5 Ways to Get Resources in EJB 3

1. Use resource injection with runtime info mapping.
For example,
package com.foo.ejb;
import javax.ejb.Remote;

@Remote public interface ResourceRemote {
public void hello();
}

package com.foo.ejb;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.sql.DataSource;

@Stateless
public class ResourceBean implements ResourceRemote {
@Resource(name="jdbc/employee")
private DataSource employeeDataSource;
You don't need ejb-jar.xml. For portable applications, you will need appserver-specific deployment plan to map the logical name (jdbc/employee) to the actual DataSource configured in the target runtime environment. For JavaEE SDK 5, Glassfish, and Sun Java System Application Server 9, it's sun-ejb-jar.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 EJB 3.0//EN"
"http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
<sun-ejb-jar>
<enterprise-beans>
<ejb>
<ejb-name>ResourceBean</ejb-name>
<jndi-name>ResourceBean</jndi-name>
<resource-ref>
<res-ref-name>jdbc/employee</res-ref-name>
<jndi-name>jdbc/__default</jndi-name>
</resource-ref>
</ejb>
</enterprise-beans>
</sun-ejb-jar>

2. Non-portable applications don't even need this appserver-specific deployment plan; they can just use mappedName() field in @Resource:
@Stateless
public class ResourceBean implements ResourceRemote {
@Resource(name="jdbc/employee",
mappedName="jdbc/__default")
private DataSource employeeDataSource;
If application portability is not a big concern, you don't need any descriptor in this example. mappedName() field maps the logical name jdbc/employee to its counterpart (jdbc/__default) in the target runtime server environment. Be aware that application servers are not required by JavaEE platform to support mappedName() field. So it may cause trouble when you later try to migration your applications to another appserver. Glassfish, JavaEE SDK, and SJSAS 9 support mappedName().


3. Yet another option is to use default mapping rules in some application servers, without using runtime deployment plan. This is not portable either and some appservers may not have this functionality at all. In Glassfish, JavaEE SDK, and SJSAS 9, basically if resource logical name (without prefix) is the same as its physical name, then they are mapped together even without sun-ejb-jar.xml. For example,
@Stateless
public class ResourceBean implements ResourceRemote {
@Resource(name="jdbc/__default")
private DataSource defaultDataSource;
You don't need any descriptor, and it just works thanks to the default resource mapping.


4. Use EJBContext.lookup(String name), a new convenience method in EJB 3. The name parameter is relative to java:comp/env. For example,
package com.foo.ejb;
import java.sql.Connection;
import java.sql.SQLException;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.sql.DataSource;

@Stateless
public class ResourceBean implements ResourceRemote {
public void hello() {
DataSource
employeeDataSource =
(DataSource) sctx.lookup("jdbc/employee");
try {
Connection conn = employeeDataSource.getConnection();
} catch(SQLException ex) {
ex.printStackTrace();
}
}
ejb-jar.xml is needed to declare this resource reference. Appserver-specific deployment plan is also needed for mapping, unless you use the default mapping mechanism above.
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
metadata-complete="false" version="3.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<enterprise-beans>
<session>
<ejb-name>ResourceBean</ejb-name>
<resource-ref>
<res-ref-name>jdbc/employee</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
</session>
</enterprise-beans>
</ejb-jar>
sun-ejb-jar.xml is the same as in listing 1.

5. Use traditional JNDI lookup. This approach is basically the same as EJBContext.lookup(String name), except that JNDI lookup requires more lines of code, and uses an absolute reference name starting with java:comp/env or java:comp/
@Stateless
public class ResourceBean implements ResourceRemote {
@Resource private SessionContext sctx;

public void hello() {
DataSource ds = null;
try {
InitialContext ic = new InitialContext();
ds =
(DataSource) ic.lookup("java:comp/env/jdbc/employee");
} catch (NamingException ex){
throw new IllegalStateException(ex);
}
try {
Connection conn = ds.getConnection();
} catch(SQLException ex) {
throw new IllegalStateException(ex);
}
}
You need to declare this resource reference in ejb-jar.xml, and map it in sun-ejb-jar.xml, the same as in listing 4.

The biggest annoyance in JNDI lookup is that I have to try-catch javax.naming.NamingException, a checked exception. Since it's a low-level exception, it's not appropriate to just further throw it out. Probably for this reason, EJBContext.lookup(String name) just throws java.lang.IllegalArgumentException if name not found.

Followers

Pageviews Last 7 Days