|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 6
While ENC is a welcome change, it throws a wrench into the forward compatibility of EJB 1.0 beans that access environment properties, resources, and other beans. In other words, beans developed in EJB 1.0 that access those things may not be portable to an EJB 1.1 container. Now, I will present a solution to this problem that will work in any EJB 1.0- and EJB 1.1-compliant container. This solution is portable and automatic: it requires no additional work once a bean is designed to utilize it.
PortableContext. EJBContext (as shown in List 1-A). In EJB 1.1, they can be obtained using the same mechanism, which has been deprecated, or through
the JNDI environment context (as shown in List 1-B).Support for obtaining environment properties via the EJBContext.getEnvironment() method is optional for EJB 1.1 servers, so some vendors will maintain this mechanism while others will not -- the latter
will instead deprecate the EJBContext.getEnvironment() method. To avoid dependence on the EJBContext.getEnvironment() method, we will provide the enterprise beans with the PortableContext, which abstracts the mechanism for obtaining environment variables. List 2 shows the abstract class that defines our PortableContext.
import javax.ejb.EJBContext;
public abstract class PortableContext {
EJBContext ejbContext;
public void setEJBContext(EJBContext ctx){
ejbContext = ctx;
}
public abstract Object lookup(String name, Class type)throws PortableContextException;
}
|
This is the abstraction that enterprise beans will use in both EJB 1.0 and EJB 1.1 containers. The concrete implementation of the abstraction is different in EJB 1.0 than in EJB 1.1. These differences are illustrated in Lists 3-A and 3-B.
import javax.ejb.EJBContext;
import java.util.Properties;
public class PortableContext1_0 extends PortableContext {
public Object lookup(String name, Class type)
throws PortableContextException {
try {
Properties props = ejbContext.getEnvironment();
String value = props.getProperty(name);
if (value == null)
return null;
else {
if (type == String.class)
return value;
else
return primitiveWrapper(value, type);
}
} catch(Exception e) {
throw new PortableContextException(e);
}
}
private Object primitiveWrapper(String value, Class type)
throws PortableContextException {
if (type == Double.class)
return new Double(value);
if (type == Integer.class)
return new Integer(value);
if (type == Boolean.class)
return new Boolean(value);
if (type == Long.class)
return new Long(value);
if (type == Byte.class)
return new Byte(value);
if (type == Short.class)
return new Short(value);
if (type == Float.class)
return new Float(value);
else
throw new PortableContextException();
}
}
|
import javax.ejb.EJBContext;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class PortableContext1_1 extends PortableContext {
InitialContext jndiContext;
public Object lookup(String name, Class type)
throws PortableContextException {
try{
jndiContext = new InitialContext();
Object value = jndiContext.lookup(name);
return value;
} catch (NamingException ne) {
throw new PortableContextException(ne);
}
}
}
|
Each concrete implementation of the abstract PortableContext obtains the environment properties differently, but the behavior is the same from the bean developer's perspective, because
the details of the implementation are hidden. The bean developer is concerned only with the behavior described by the PortableContext type.
One of the interesting things about the PortableContext.lookup() method is that it is designed to return the EJB 1.1 range of types instead of being limited to EJB 1.0 types as you might
expect. The PortableContext1_0 implementation is enhanced to allow primitive wrappers as well as simple String types. Of course, the enhancement requires a little more discipline since you must pass in the correct Class type parameter in the lookup() method call; however, it allows the EJB 1.0 beans to take advantage of EJB 1.1's more advanced feature: support for primitive
wrappers as properties. It's not just forward compatible, it's forward featured.
In List 4, the withdraw() method of the AccountBean from List 1 is rewritten to use the new PortableContext class.
public class AccountBean implements javax.ejb.EntityContext {
int id;
double balance;
EntityContext ejbContext;
PortableContext portableContext;
...
public void withdraw(Double withdraw)
throws WithdrawLimitException {
Double limit = (Double)
portableContext.lookup("java:comp/env/withdraw_limit", Double.class);
if (withdraw.doubleValue() > limit.doubleValue())
throw new WithdrawLimitException(limit);
else
balance = balance - withdraw.doubleValue();
}
...
}
|
Here we use the EJB 1.1 naming conventions in our forward-compatible bean, which will work in EJB 1.0 if we simply use these conventions to name the properties. This ensures that neither the EJB 1.0 nor EJB 1.1 concrete implementations requires name translations to look up a property.
The Class parameter passed with every method invocation may seem a little clumsy at first, but it actually proves useful as we increase
the functionality of the PortableContext later in this article.
PortableContext1_0 implementation while the EJB 1.1 beans use the PortableContext1_1 implementation. We could explicitly instantiate the correct implementation within the bean code, but this would require code
changes when the bean is upgraded from an EJB 1.0 to an EJB 1.1 container. To avoid having to change the bean code when porting
the bean, we give the PortableContext a factory method, which automatically chooses the correct concrete implementation at runtime.List 5 shows a new static method, getInstance(), in PortableContext. The getInstance() method automatically chooses the correct implementation based on the system property java.ejb.portable_context, which is set to the fully qualified class name of the appropriate concrete implementation. With an EJB 1.0 server, the java.ejb.portable_context property is set to PortableContext1_0; if it's an EJB 1.1 server, the system property is set to PortableContext1_1. Once the class is loaded using Class.forName(), the proper concrete implementation can be instantiated and initiated with the EJBContext. Below, in List 5, is the static method that is added to the PortableContext class.
import javax.ejb.EJBContext;
public abstract class PortableContext {
final static String SYSTEM_PROPERTY_NAME = "java.ejb.portable_context";
EJBContext ejbContext;
public static PortableContext getInstance(EJBContext context)
throws PortableContextException{
String className =
System.getProperty(SYSTEM_PROPERTY_NAME);
if(className == null)
throw new PortableContextException("No system property for impl");
try{
Class clazz = Class.forName(className);
PortableContext portableCtx =
(PortableContext)clazz.newInstance();
portableCtx.setEJBContext(context);
return portableCtx;
}catch(Exception e){
throw new PortableContextException(e);
}
}
public void setEJBContext(EJBContext ctx){
ejbContext = ctx;
}
public abstract Object lookup(String name, Class type)
throws PortableContextException;
}
|
While the foregoing algorithm for dynamically loading the concrete implementation works in most servers, access to system
properties and dynamic class loading may not be universally supported. In these cases, hard coding the instantiation of the
proper concrete implementation into the getInstance() method is suitable.
You can use the PortableContext factory in the bean code so that it hides the concrete implementation. Unfortunately, you must reset the EJBContext used by the PortableContext in a couple of different areas, namely in the setEntityContext() or setSessionContext() method and in the ejbActivate() method. That is because of the specified life cycles of the different bean types and the latitude given to EJB vendors on
EJBContext preservation through activation in EJB 1.0. By resetting the EJBContext, you ensure that it is always current. List 6 shows how this would work in an enterprise bean. The strategy would be the
same in session and entity beans.
import javax.ejb.SessionContext;
public class TellerBean implements javax.ejb.SessionBean {
SessionContext ejbContext;
PortableContext portableContext = null;
public void setSessionContext(SessionContext ctx) {
ejbContext = ctx;
setPortableContext();
}
public void ejbActivate() {
setPortableContext();
}
public void setPortableContext(){
if (portableContext == null)
portableContext =
PortableContext.getInstance(ejbContext);
else
portableContext.setEJBContext(ejbContext);
}
...
}
|
import javax.ejb.EntityContext;
public class AccountBean implements javax.ejb.EntityBean {
EntityContext ejbContext;
PortableContext portableContext = null;
public void setEntityContext(EntityContext ctx){
ejbContext = ctx;
setPortableContext();
}
public void ejbActivate() {
setPortableContext();
}
public void setPortableContext() {
if (portableContext == null)
portableContext =
PortableContext.getInstance(ejbContext);
else
portableContext.setEJBContext(ejbContext);
}
...
}
|
DriverManager.getConnection() to obtain either a pooled JDBC driver or an exclusive connection. EJB 1.1 changes how JDBC connections are accessed by providing
a standard mechanism for obtaining a JDBC connection factory (javax.sql.DataSource) through the JNDI ENC.To make an EJB 1.0 bean forward compatible, it is necessary to encapsulate these differences, which you can conveniently do
in PortableContext. In Lists 7-A and 7-B, the lookup() method in the two concrete implementations have been modified to provide access to JDBC connections.