For example,
package com.foo.ejb;You don't need
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;
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:
@StatelessIf 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().
public class ResourceBean implements ResourceRemote {
@Resource(name="jdbc/employee",
mappedName="jdbc/__default")
private DataSource employeeDataSource;
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,
@StatelessYou don't need any descriptor, and it just works thanks to the default resource mapping.
public class ResourceBean implements ResourceRemote {
@Resource(name="jdbc/__default")
private DataSource defaultDataSource;
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;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.
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();
}
}
<?xml version="1.0" encoding="UTF-8"?>sun-ejb-jar.xml is the same as in listing 1.
<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>
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/
@StatelessYou need to declare this resource reference in ejb-jar.xml, and map it in sun-ejb-jar.xml, the same as in listing 4.
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);
}
}
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.
Tags: