More key strategies for developing portable EJB 1.0 beans for EJB 1.1 servers
While the Enterprise JavaBeans specification (EJB) 1.1 provides a more stable and concrete specification than its predecessor, EJB 1.0, does, it also introduces forward-compatibility problems. Beans developed for EJB 1.0-compliant servers today will not automatically port to EJB 1.1 servers tomorrow.
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.
PortableContext
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.
Bean access to security
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.
|
|
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.
|
|
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
.
|
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.
|
|
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.
|
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
.
|
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
.
|
Using the PortableContext across brands of EJB servers
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.
|
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.
Other portability changes in EJB 1.1
Changes specific to entity beans
The most obvious change in EJB 1.1 is its mandated support for entity beans. This support must be complete, embracing support for both container-managed and bean-managed persistent entities. As big and burly as this change is, it’s not going to significantly affect the portability of beans developed in 1.0 servers. If entity beans weren’t available before, then the new version simply makes them available to you; if you’re already using entity beans, then in all likelihood you will need to make some cumbersome but simple changes to your code.
The entity bean did not make it into EJB 1.1 unchanged. In addition to changes in the EJBContext
and access to beans and resources (addressed above), entity beans also lost bean-managed transactions and changed the return value of ejbCreate(..)
methods.
Bean-managed transactions
In EJB 1.0 and 1.1, you can manage transactions both by declaration and by explicit use of a transactional bean-container interface. This declarative transaction control, called container-managed transactions (CMT), is the simplest of the two options because it doesn’t require that a bean developer explicitly control the transactional bounds — it’s automatic. In bean-managed transactions (BMT), the bean uses the EJBContext
and the UserTransaction
object to explicitly control the transaction. In EJB 1.0, both stateful and entity beans had the option of using BMT instead of CMT; this was deemed necessary in order to accommodate fairly complicated and unusual scenarios in which declarative transaction control was too restrictive. In practice, the use of BMT in stateful session beans is more common than BMT in entity beans. This is good, because EJB 1.1 prohibits the use of BMT in entity beans. If you are developing beans to the EJB 1.0 specification, you must not use BMT if you want your bean to port to EJB 1.1 servers.
ejbCreate(..): Return type changed
In EJB 1.0, the ejbCreate()
methods in bean-managed persistence (BMP) and container-managed persistence (CMP) entities are required to have different return values. In CMP entities, the bean must return a void type, while in BMP entities, the bean must return the primary key type. The logic for this discrepancy was that CMP entities are not responsible for manufacturing their own primary key — that’s the container’s job — so the return value should reflect that aspect of the bean-container contract. BMP entities, however, are responsible for manufacturing their own primary keys (instantiating them with the right values), so they need to return the key to the container and thus must have a return value of the primary key type.
Unfortunately, this design choice has proven problematic for bean and container developers. In Java, method overloading of return values is prohibited, which means that a CMP entity can never be extended to create a BMP entity. But it turns out that this is just what some container vendors need to happen in order to easily generate implementation objects at deployment time. Bean developers also need this to extend prepackaged CMP entities with BMP behavior. To satisfy these needs, Sun changed the specification so that ejbCreate()
in entity beans always returns the primary key type. With EJB 1.1, CMP beans simply return a null
value to the container, while BMP entities return the primary key. List 9 illustrates the differences in return types for CMP entities.
|
|
Do you see a problem here? If ejbCreate(..)
is required to return void for CMP in EJB 1.0, but is required to have a return type of the primary key in EJB 1.1, then 1.0 CMP entities will not run unmodified in a 1.1 server. In other words, you are going to have to change the return types of all the ejbCreate()
methods in your CMP beans to be the primary key type and the actual value returned to null
. This isn’t so bad, but will cause headaches for bean developers, because they will have to change the ejbCreate(..)
methods in all of their entity beans. (On a related topic, note that CMP entities can also return Object
s and allow the deployer to define the primary key type.)
Changes to the deployment descriptor
EJB 1.0 used serializable classes defined in the javax.ejb.deployment
package to specify declarative attributes for enterprise beans. This approach was abandoned in EJB 1.1 in favor of a more flexible XML format. As a result, deployment descriptors defined using EJB 1.0 serializable classes will be of little use in EJB 1.1 servers. EJB 1.0 deployment descriptors will have to be converted to the XML format for EJB 1.1 deployments. Sun has promised to provide a converter that will read EJB 1.0 deployment descriptors and generate them for EJB 1.1. You can wait for Sun to release this utility, or you can write one yourself — it’s not that complicated. Below is a purely illustrative sample of an application that would perform this type of conversion, transforming an EJB 1.0 serialized DeploymentDescriptor
into an EJB 1.1-compliant XML file. A full implementation is left as an exercise for the reader.
|
The code above illustrates the fundamental differences between EJB 1.0’s deployment descriptors and those of EJB 1.1. The EJB 1.1 XML deployment descriptors actually cover a lot more ground than the EJB 1.0 java.ejb.deployment
package, so a converted deployment descriptor will require additional information not previously available in EJB 1.0.
Conclusion
Several changes in EJB 1.1 prevent beans developed in EJB 1.0 servers from porting to EJB 1.1 servers. Some of these changes affect the bean code while others are found in the new options offered by the XML deployment descriptor. In this article, we have focused on issues involving the portability of bean code, which is the topic that will cause the most grief for developers. By using the PortableContext
or some similar construct, you can reduce the risk of forward-compatibility problems. The PortableContext
can also be used to make beans portable across EJB vendors by creating custom implementations for specific brands of servers.
It is not necessary that you use the PortableContext
developed in this article. This PortableContext
is a good solution, but not the only solution. You may develop your own strategies based on the concepts developed in here, or you can simply use the PortableContext
as defined. Proprietary idiosyncrasies may force you to modify the PortableContext
with special vendor-specific implementation classes, or you may discover improvements to the general-purpose classes defined in this article. Either way, please relay your discoveries to me. I will keep a bulletin of proprietary extensions and improvements at my Website, EJBNow.com, for the entire EJB community. (See the Resources section below for the URL.)
The authors of this month’s server-side Java computing articles will be holding a free online seminar on January 13 at 11:00 a.m. PST. Register to join at https://seminars.jguru.com.