Some reader favorites:
EJB fundamentals and session beans
Create a scrollable virtual desktop in Swing
Wizard API updated!
Tim Boudreau has released a new version of the Swing Wizard library (version 0.997) that fixes the WizardException bug reported in JavaWorld's recent Open Source Java Project profile. The article's examples have been reworked to test out the new, improved WizardException. Thanks, Tim, for this helpful fix!
Open Source Java Projects: The Wizard API
TEXTBOX: TEXTBOX_HEAD: Create forward-compatible beans in EJB: Read the whole series! ![]()
In the first installment of this article, we developed the PortableContext, an abstraction that insulates bean developers from changes that affect forward compatibility. At runtime, PortableContext automatically changes its behavior to support either EJB 1.0 or EJB 1.1 conventions for accessing properties, JDBC connections,
and other beans. This article will continue to enhance the PortableContext in order to encapsulate access to security from a bean. I will also demonstrate how you can use the PortableContext to make enterprise beans portable between brands of servers, as well as different versions of the specification. In addition,
I'll address issues specific to entity bean portability and the changes to XML deployment descriptors.
This article covers some advanced topics, and is not intended for individuals new to Enterprise JavaBeans. Readers should already be familiar with Java development, JNDI, JDBC, and Enterprise JavaBeans. In addition, I have simplified exception handling so that example code is clear and easy to follow.
Last month, we developed the PortableContext so that it provided a common interface that hid implementation differences between EJB 1.0 and EJB 1.1 when accessing environment
properties, JDBC connections, and other beans. Now we will extend the PortableContext to encapsulate differences between EJB 1.0 and 1.1 when utilizing the EJBContext security methods.
Enterprise JavaBeans provides beans with limited access to authorization-based (access control) security. In EJB, a bean can
obtain the security identity of its client and make sure that the client is a member of a specific security role or identity.
EJB 1.0 and EJB 1.1 maintain slightly different semantics in the EJBContext to support these security features.
In EJB 1.0, the EJBContext is specifically designed to use the java.security.Identity type for identifying clients and verifying membership in a role; in EJB 1.1, the EJBContext uses the java.security.Principal type for this purpose. In EJB 1.1, the Identity methods used in EJB 1.0 are still available but are deprecated, which presents a forward-compatibility problem. Lists 1-A
and 1-B show the security methods in the EJBContext for EJB 1.0 and EJB 1.1.
public interface EJBContext {
public java.security.Identity getCallerIdentity();
public boolean isCallerInRole(Identity role);
...
}
|
public interface EJBContext {
public java.security.Principal getCallerPrincipal();
public boolean isCallerInRole(java.lang.String roleName);
// Deprecated
public java.security.Identity getCallerIdentity();
// Deprecated
public boolean isCallerInRole(Identity role);
...
}
|
The change in the new spec is a result of change in the security architecture of the Java 2 Platform. However, the differences
are cosmetic for most EJB developers, since the underlying objective is the same. The code fragments below demonstrate how
a bean might use the EJBContext to determine a caller's identity and verify membership in a role under both the EJB 1.0 and EJB 1.1 specs.
public class AccountBean implements EntityBean {
int id;
double balance;
String modifiedBy;
EntityContext ejbContext;
PortableContext portableContext;
public void withdraw(Double withdraw)
throws WithdrawLimitException, AccessDeniedException {
// only tellers can withdraw more than 10k
if(withdraw.doubleValue() > 10000) {
Identity tellerIdnty = new RoleIdentity("teller");
boolean isTeller = ejbContext.isCallerInRole(tellerIdnty)
if(!isTeller)
throw new AccessDeniedException();
}
Double limit = (Double)
portableContext.getEnvironmentEntry(
"java:comp/env/withdraw_limit", Double.class);
if (withdraw.doubleValue() > limit.doubleValue())
throw new WithdrawLimitException(limit);
else
balance = balance - withdraw.doubleValue();
Identity identity = ejbContext.getCallerIdentity( );
String modifiedBy = identity.getName();
}
...
}
|
public class AccountBean implements EntityBean {
int id;
double balance;
String modifiedBy;
EntityContext ejbContext;
PortableContext portableContext;
public void withdraw(Double withdraw)
throws WithdrawLimitException, AccessDeniedException {
// only tellers can withdraw more than 10k
if(withdraw.doubleValue() > 10000) {
boolean isTeller = ejbContext.isCallerInRole("teller")
if (!isTeller)
throw new AccessDeniedException( );
}
Double limit = (Double)
portableContext.getEnvironmentEntry(
"java:comp/env/withdraw_limit",Double.class);
if (withdraw.doubleValue() > limit.doubleValue())
throw new WithdrawLimitException(limit);
else
balance = balance - withdraw.doubleValue();
Principal principal = ejbContext.getCallerPrincipal( );
String modifiedBy = principal.getName();
}
...
}
|
PortableContext can hide the EJB 1.0 and EJB 1.1 security models from the bean. To accomplish this, the PortableContext models its abstraction around the EJB 1.1 security model. Below, the abstract PortableContext class has been modified to include two new methods, getCallerPrincipal() and isCallerInRole(), which mimic the new security methods in the EJB 1.1 EJBContext.
import javax.ejb.*; import java.lang.reflect.Method; import java.security.Principal; |
In the PortableContext class, the security methods are abstract, which means that the PortableContext implementations (PortableContext1_0 and PortableContext1_1) must implement these methods.
In Lists 4-A and 4-B below, the PortableContext implementations are modified to implement the security methods. Notice that the EJB 1.1 implementation (PortableContext1_1) simply delegates the method requests to the EJB 1.1 EJBContext, while the EJB 1.0 implementation converts requests from the Principal-based model of EJB 1.1 to the Identity model of EJB 1.0.
import javax.ejb.EJBContext;
...
import java.security.Principal;
import java.security.Identity;
public class PortableContext1_0 extends PortableContext {
public Principal getCallerPrincipal( ) {
return (Principal)ejbContext.getCallerIdentity( );
}
public boolean isCallerInRole(String roleName){
Identity identity = new RoleIdentity(roleName);
return ejbContext.isCallerInRole(identity);
}
...
}
|
import javax.naming.InitialContext; ... import java.security.Principal; |
The java.security.Identity class implements the java.security.Principal interface, so the getCallerPrincipal( ) method in the PortableContext1_0 class simply casts the Identity object returned from the EJBContext.getCallerIdentity( ) method to its Principal type. This is simple enough; the implementation of the isCallerInRole() method in the PortableContext1_0 class is more complicated, however.
EJB 1.0 required the use of java.security.Identity to verify membership in a security role. As the code in Lists 2-A and 2-B demonstrates, checking a client's role can provide
valuable authorization logic that the access control declarations in the deployment descriptor can't address. Unfortunately,
while the EJB 1.0 specification requires the use of the Identity type as a role identifier, it doesn't specify how a bean should acquire the Identity object specific to the role being tested. The Identity class is an abstract class, so simply instantiating it is not possible. In the examples above, a mysterious RoleIdentity object was instantiated with the name of the role being tested. This provided us with an Identity object that could be used in the isCallerInRole(Identity role) method. But where did the RoleIdentity object come from?
The RoleIdentity class is an extension of the java.security.Identity class, and provides us with a simple, concrete implementation of Identity that we can instantiate with a string name. (A similar RoleIdentity class was originally defined by Jian Lin in a post to the ejb-interest mailing list on September 24, 1999.) Below is the
definition of this class.
import java.security.Identity;
public class RoleIdentity extends Identity {
public RoleIdentity(String name) {
super(name);
}
}
|
Use of the RoleIdentity class works in those EJB servers that limit comparison operations of Identity to the name attribute. In other words, these servers simply compare the string values returned by the getName() methods of the Identity objects.
Some EJB vendors may enlist more complicated mechanisms for comparing the Identity objects. In these cases, you may have to enhance the RoleIdentity defined here, or use a vendor-specific mechanism for verifying membership in a role. BEA's Weblogic Server, for example,
works wonderfully with the RoleIdentity, but it also provides a proprietary mechanism for obtaining group Identity objects (i.e., roles to which identities belong). List 6 shows how the PortableContext1_0.isCallerInRole() method would be coded to use the Weblogic security API instead of RoleIdentity.
public class PortableContext1_0 extends PortableContext {
public boolean isCallerInRole(String roleName){
// Weblogic specific solution
Identity identity = (Identity) weblogic.security.acl.Security.getRealm().getGroup(roleName);
if(identity==null) {
return false;
}
return ejbContext.isCallerInRole(identity);
}
...
}
|
The PortableContext insulates the bean developer from the differences in the security procedures of EJB 1.0 and EJB 1.1. In addition, because
we are using the EJB 1.1 conventions, EJB 1.0 beans will support security in a forward-featured manner. Below is an example
of how a forward-compatible bean would use the security methods defined in the PortableContext.
public class AccountBean implements EntityBean {
int id;
double balance;
String modifiedBy;
EntityContext ejbContext;
PortableContext portableContext;
public void withdraw(Double withdraw)
throws WithdrawLimitException, AccessDeniedException {
// only tellers can withdraw more than 10k
if(withdraw.doubleValue() > 10000) {
boolean isTeller = portableContext.isCallerInRole("teller")
if(!isTeller)
throw new AccessDeniedException( );
}
Double limit = (Double)
portableContext.getEnvironmentEntry(
"java:comp/env/withdraw_limit",Double.class);
if (withdraw.doubleValue() > limit.doubleValue())
throw new WithdrawLimitException(limit);
else
balance = balance - withdraw.doubleValue();
Principal principal = portableContext.getCallerPrincipal( );
String modifiedBy = principal.getName();
}
...
}
|
The PortableContext isn't just able to encapsulate differences between versions of the EJB specification; it can also do so across various brands
of EJB servers. Although all EJB vendors strive for full compliance with the EJB specification, few accomplish this Herculean
task overnight. As a result, servers support the specification in varying degrees, which can make portability between servers
difficult. This problem was exacerbated by the recent introduction of the EJB 1.1 specification. Porting beans between brands
of servers in this climate will be difficult, but many of the brand-to-brand portability problems can be solved easily using
the PortableContext. Concrete implementations of the PortableContext can be created for any brand of EJB server and loaded dynamically at runtime by simply changing the system property java.ejb.portable_context so that it points at the correct implementation class.
For example, the Gemstone/J server supports most of the EJB 1.1 features, with one notable exception: it does not use of the
JNDI ENC to obtain a JDBC database connection. When a database connection is needed, it is instead obtained using the DriverManager.getConnection() method. To support beans deployed in a Gemstone/J server, you simply define a Gemstone/J concrete implementation. List 8
is an example of just such an vendor-specific implementation.
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.ejb.EJBContext;
import javax.ejb.EJBException;
import java.sql.SQLException;
import javax.rmi.PortableRemoteObject;
import java.sql.DriverManager;
import java.security.Principal;
public class GSPortableContext3_0 extends PortableContext {
InitialContext jndiContext;
public Principal getCallerPrincipal( ){
return ejbContext.getCallerPrincipal();
}
public boolean isCallerInRole(String roleName){
return ejbContext.isCallerInRole(roleName);
}
public Object lookup(String name, Class type)throws PortableContextException{
try{
jndiContext = new InitialContext();
Object value = jndiContext.lookup(name);
if(name.startsWith("java:comp/env/jdbc")){ return DriverManager.getConnection((String)value);
}
else if(name.startsWith("java:comp/env/ejb")){
return PortableRemoteObject.narrow(value,type);
}
else
return value;
}catch(NamingException ne){
throw new PortableContextException(ne);
}catch(SQLException se){
throw new PortableContextException(se);
}
}
}
|
If you were to run a forward-compatible bean (one that uses PortableContext) in BEA's Weblogic Server 4.5, for example, you would use the PortableContext1_0 concrete implementation. The same bean could later be deployed in Gemstone/J 3.0 server by simply changing the system property
java.ejb.portable_context to point at the GSPortableContext3_0 concrete implementation.
Free Download - 5 Minute Product Review. When slow equals Off: Manage the complexity of Web applications - Symphoniq
![]()
Free Download - 5 Minute Product Review. Realize the benefits of real user monitoring in less than an hour. - Symphoniq