Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Dynamically Binding EJBs Using the Java Reflection API


Java Solutions October 2002/Dynamically Binding EJBs Using the Java Reflection API


The software development profession has moved towards making the assembly of an application independent from the development of the application’s components. Though this component-based approach has proven unworkable in many situations, in other cases, a component-based design leads to a lower-cost and higher-speed application. If the design of the original component is sufficiently flexible, you can even combine this component with new components in ways you didn’t anticipate when you developed the original component. (This quest for flexibility can also become a burden. Sometimes it is impossible to have sufficient flexibility and a reasonable interface without the component becoming so heavyweight that it is unusable.)

Achieving immutable source code is an important step in the evolution of software development. To meet this requirement, component architectures such as EJB (Enterprise JavaBeans) must provide the means to dynamically bind components. Dynamic binding is the deferral until deployment — or even run time — of a client object’s association with a specific enterprise bean. With dynamic binding, a developer implements a client class that interacts with an enterprise bean through a particular interface. Any enterprise bean that implements this interface may be associated with the client at run time — without requiring the alteration of the client’s source code.

The EJB architecture contains a framework for binding enterprise beans statically (at compile time). However, this framework provides no obvious means for performing dynamic binding.

Last year, I designed and implemented a JMS (Java Message Service) framework that I used numerous times in several different systems. For each instance of this framework, I had to couple the message-driven bean in the framework to a session bean that could process the set of message types. Since this session bean would be developed long after the JMS framework, I needed some method to dynamically bind the message-driven bean to the session bean. This article describes the resulting method for dynamically binding beans using Java’s Reflection API.

Static Binding

The following discussion presumes some familiarity with EJB. See the sidebar for information on implementing and deploying a session bean.

In the conventional approach, the EJB Specification stipulates that every session bean must expose at least two interfaces to its clients. The first interface is a proxy interface that extends either the javax.ejb.EJBObject or javax.ejb.EJBLocalObject interface. The second interface is a home interface that extends either the javax.ejb.EJBHome or javax.ejb.EJBLocalHome interface [4]. The exposed home interface must contain one or more create methods that return a reference to an instance of the bean’s proxy interface. The proxy interface contains the set of methods that the bean exposes to its clients. The EJB specification further requires that clients use the javax.rmi.PortableRemoteObject class to cast from the generic java.lang.Object reference returned by the namespace to the specific home interface reference needed to interact with the desired enterprise bean [5].

Imagine a session bean named StaticLiaisonBean that relies on an UpperCaseConverterBean to help it accomplish some unit of work. At run time, the client StaticLiaisonBean needs to look up the home interface for UpperCaseConverterBean in the EJB container’s namespace, resolve this generic reference to a usable home-interface reference, and invoke the desired create method exposed by that home interface to acquire a reference to a proxy object for an instance of UpperCaseConverterBean (see Listing 1).

The StaticLiaisonBean class knows, at compile time, everything it needs to know about the session bean it plans to work with:

  • The JNDI name that maps to the home interface (string "ejb/TargetBean" in Listing 1, line B).
  • The actual home interface name and its package (UpperCaseConverterHome in Listing 1, line C and its corresponding import statement).
  • The proxy interface name and its package (UpperCaseConverterRemoteProxy in Listing 1, line A and its corresponding import statement).

Thus, StaticLiaisonBean is statically bound to UpperCaseConverterBean. To reuse the logic contained in StaticLiaisonBean, but have it interact with a LowerCaseConverterBean, all three of these lines (and supporting import statements) need to be modified and the bean recompiled.

Dynamic Binding

To permit dynamic binding, one must implement StaticLiaisonBean such that it does not require recompilation when associated with a session bean other than UpperCaseConverterBean. The problem can be broken into a set of intertwined challenges:

  1. Abstract away from a specific JNDI name that maps to an object that implements an arbitrary session bean’s home interface.
  2. Provide the javax.rmi.PortableRemoteObject.narrow method with a java.lang.Class object that represents the home interface for an arbitrary session bean.
  3. Devise a way to acquire a reference to the home of an arbitrary session bean and create a reference to the remote proxy.
  4. Devise a way to declare a variable to hold a reference to the remote proxy of an arbitrary session bean in order to call the session bean’s methods.

To address the first challenge, I added a layer of indirection by introducing a new environment entry to the liaison bean’s deployment descriptor to store the JNDI name string for the desired target bean’s home interface.

<env-entry>
  <env-entry-name>targetJNDI</env-entry-name>
  <env-entry-type>java.lang.String</env-entry-type>
  <env-entry-value>ejb/TargetBean</env-entry-value>
</env-entry>

The liaison bean’s source code still has a hard-coded JNDI name string (Listing 7, line F). However, instead of referencing the target bean’s home interface, the liaison bean now references a second JNDI name string. This second JNDI name string references the target bean’s home interface (Listing 7, line G). Since this second JNDI name string resides in the deployment descriptor, it is easily configurable until run time. Thus, I have deferred until application assembly the need to specify the JNDI name this bean should use to locate its associate.

I applied this same approach along with Reflection [6] to solve the second challenge. I add another environment entry to the liaison bean’s deployment descriptor to store the fully specified name for the target bean’s home interface.

<env-entry>
  <env-entry-name>targetHomeClass</env-entry-name>
  <env-entry-type>java.lang.String</env-entry-type>
  <env-entry-value>
    ...converter.LowerCaseConverterHome
  </env-entry-value>
</env-entry>

At run time, the liaison bean fetches this home-interface name (Listing 7, line H) and passes it as an argument to the reflective static method java.lang.Class.forName( String converterHomeInterface ) to acquire an instance of the home interface’s associated java.lang.Class (Listing 7, line I). The liaison bean also acquires the generic java.lang.Object reference to the desired target bean’s home interface (Listing 7, line J). The liaison bean ensures it acquired what it expected by passing the home-interface reference and the java.lang.Class instance to the static method javax.rmi.PortableRemoteObject.narrow in order to safely downcast the reference to the target bean’s home-interface type (Listing 7, line K).

I also made use of Reflection to solve the third challenge. My approach assumes the set of interchangeable beans can all employ a common set of arguments for the create methods in their home interfaces. (Given the objective of immutable source code, I cannot modify the client bean to accommodate new arguments.) For my purposes, I am content to use a create method with no arguments. I use the java.lang.Class instance of the target bean’s home interface (acquired while solving the second challenge) and invoke its getMethod( String name, Class[] parameterTypes ) method to acquire a java.lang.reflect.Method instance for this create method (Listing 7, line L). I then call the invoke method of this java.lang.reflect.Method instance, passing in the home-interface instance, and cast the returned remote proxy reference to the ConverterInterface common interface (Listing 7, line M).

The common interface design strategy described by Richard Monson-Haefel [7] provides part of the solution to the fourth challenge. The motivation for this design strategy is the desire to ensure that the set of business methods exposed by the enterprise bean is consistent with the business methods declared by the enterprise bean’s remote interface [8]. To apply the strategy, one collects the set of business methods exposed by the enterprise bean in an interface (Listing 8). One then extends this common interface with the proxy interface (Listing 9) and implements the interface via the bean class (Listing 7, line D). Since the remote interface is tied to the bean class by this common interface, the Java compiler can ensure their consistency, and anyone reviewing the code will immediately discern the relationship.

I can take advantage of this design strategy in another way. The common interface provides a data type you can use to declare a variable that holds a reference to an arbitrary remote proxy. I need only use a single common interface for all of the beans that collaborate with the liaison bean. I can then provide a reference to this common interface in the liaison bean’s source code and interact with the different session beans through this common interface (Listing 7, line E).

Using the common interface, ConverterInterface, I add a sibling bean to UpperConverterBean called LowerConverterBean. To demonstrate dynamic binding, I assembled a liaison component called DynamicLiaison (Listing 10, bold stanzas). This component contains two beans, both instances of DynamicLiaisonBean. One dynamically binds to UpperConverterBean, while the other binds dynamically to LowerConverterBean. If you download the application that complements this article (<www.cuj.com/java/code.htm>, you may exercise DynamicLiaisonClient and see this component in action.

Conclusion

The solution I have described is a useful technique that belongs in every EJB developer’s toolbox. However, you should use this technique judiciously. Although Reflection offers a powerful means to loosely couple object associations, this power comes with a price. Reflection will degrade an application’s performance if you deploy it carelessly. This technique can also obscure your view of an application’s design, which can complicate knowledge transfer and troubleshooting.

As I wrote this article, I found two other reports [9, 10] from EJB developers who independently encountered problems requiring the application of dynamic binding. Each of these developers came up with a solution that relied on the Java Reflection API. I would be interested in hearing from any developers who have used other strategies.

Notes and References

[1] Richard Monson-Haefel. Enterprise JavaBeans (O’Reilly & Associates, Inc., 2001).

[2] If you elect to use a J2EE application server other than the J2EE reference implementation, you will need to modify and possibly extend the component and application deployment descriptors I have provided to conform to your vendor’s augmentation of the specification.

[3] Richard Monson-Haefel describes this bean adaptor design strategy in [1] (see chapter 15).

[4] Prior to the EJB 2.0 specification, interaction between client objects, session beans, and entity beans could only occur through a component interface that derived from java.rmi.Remote. In using this component interface, enterprise beans residing in the same EJB container needlessly incurred the overhead associated with RMI. The EJB 2.0 specification addressed this issue by defining a local component interface that is not based on a distributed object communication protocol.

[5] This is because the EJB Specification employs Java RMI-IIOP, which requires compliance with the IIOP 1.2 protocol.o

[6] I presume that the reader is familiar with the Java Reflection API. If an overview is desired, I would suggest section 11.2 of Ken Arnold’s book [11]. A quick search of the Web will also yield a number of useful articles. I have suggested a few in the references too.

[7] Richard Monson-Haefel describes this common interface design strategy in [1] (see chapter 15).

[8] The EJB architecture does not link a bean class and its associated remote interface through a common ancestor. Rather, it relies on the automated deployment tool associated with an EJB container to enforce consistency between the two.

[9] Sriram Chakravarthy. “Bringing Together the Power of Application Servers and JMS Messaging,” <www.theserverside.com/resources/article.jsp?l=Bringing-Together-Application-Servers-and-JMS-Messaging>.

[10] Gorsen Huang. “Java Tip 118: Utilize the EjbProxy,” Java World, <www.javaworld.com/javaworld/javatips/jw-javatip118.html>.

[11] Ken Arnold, James Gosling, and David Holmes. The Java Programming Language, Third Edition (Addison-Wesley, 2000).

[12] Linda G. DeMichiel, L.Yalcinalp, and Sanjeev Krishnan. EJB Specification, Version 2.0 (Sun Microsystems, 2001).

Douglas A. Clark is a consulting software developer working in the Kansas City area. He holds a master’s degree in Computer Science and has more than eight years experience designing and implementing distributed systems. His areas of interest include component-based software and transaction-oriented systems. He can be reached at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.