HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

Universal Wrapper For Entity Beans, by Andrei Povodyrev and Alan Askew

This article presents a design approach for multitier applications implemented with Enterprise JavaBeans. These entity EJBs inherit bulk set-and-get methods from a single parent class that takes advantage of the java.lang.reflect package. This approach reduces the number of network round-trips, simplifies application maintenance, and significantly reduces the lines of code in an EJB application.

Introduction
Any funky new Web site, no matter how appealing its dynamic introduction or its promise of a more efficient, exciting, and communal lifestyle, must culminate eventually in "the form." Internet users surf new sites with a subconscious dread of "the form," that page where, in spite of 21st-century technology, you must still type in last name, first name, middle initial, address, and so on to receive mailings.

Distributed Web application developers share this dread, hoping to avoid the coding ennui that accompanies such pages. Furthermore, developers must contend with the performance and maintenance of the distributed code that handles these pages. While we can't yet help users save the time invested in last name, first name, and middle initial, we'd like to offer relief to application developers trying to efficiently and elegantly move bulk data from EJB clients to their database.

Setup
Let's consider a Web-based EJB architecture in which the user's browser submits HTTP requests to a servlet, which processes these requests via calls to EJBs, which in turn communicates with persistent storage (see Figure 1).

Figure 1
Figure 1:  Simple workflow for a multitier, EBJ-enabled application

Suppose your application users arrive at a page on which they'll subscribe to a magazine. After they submit the 15 fields your application requires, your servlet will service a request laden with 15 parameters or attributes ready for processing. There are several common ways to write this new data to the database. Each presents its own performance or maintenance problems, all of which our technique eliminates.

Typical Approaches
In the most direct approach your client code can find the appropriate remote interface of your fine-grained entity bean and call a set method for each of the required data fields. Explicit transaction management (Java Transaction API) preserves the transactional integrity of this operation, but each set call on the stub still requires a round-trip network interaction with the skeleton resulting in some latency, a delay between command execution and completion. The more method invocations used to perform a logical piece of work, the greater the latency in the application (see Listing 1, Style 1).

To take advantage of an application server's transaction management, you might hide all the set methods required for this operation behind a single EJB method with an extended parameter list. This approach reduces your network communication to a single round-trip. As form fields on the page change, however, the maintenance of these methods renders this approach frustrating and cumbersome (see Listing 1, Style 2).

Note: This article assumes a thorough understanding of Java and the Enterprise JavaBeans component model and is intended for enterprise application developers.

Custom data classes, whose data members mimic those of an entity bean, prove more elegant than the other approaches on the client side, but rather awkward and repetitive on the EJB side. First, every bean requires an individually tailored class that developers must update and recompile whenever the bean changes. Second, the bean developer must still retrieve each field from the wrapper object and call the corresponding set method on the bean. Thus the client developer calls 15 set methods on the wrapper, then the bean developer calls 15 get methods on the wrapper and 15 set methods on the bean. The code requires 45 get-and-set calls for a bulk update of only 15 fields (see Listing 1, Style 3).

Universal Wrapper Objects: Hashtables
To alleviate the maintenance problems custom classes create, we now consider using hashtables to pass update information to beans. You need not develop a separate wrapper class for each bean, and you can easily identify those data elements the client wished to update, leaving other attributes untouched. However, you must still duplicate attribute calls. The client user would have to use a put call for every attribute, then the bean developer would turn each of those into a pair of get (on the hashtable) and set (on the bean) calls. Considering the dull and systematic task of converting hashtable values with keys such as "lastName" into method calls such as setLastName(), you might begin to anticipate our solution.

EntityBeanServices: The Basics
Any Java class can be dynamically inspected using Reflection, which allows the programmer to obtain information about the fields, constructors, and methods of any class at runtime. Java implements Reflection via the java.lang.reflect package. Any object method can be called dynamically using invoke() on the java.lang.reflect.Method class. We've developed an elegant and powerful pair of methods that take advantage of Reflection and automatically translate a hashtable key into a bean method invocation.

Client code assembles a hashtable with all the bean attributes to update, using as hashkeys the actual method names on the entity bean (e.g., "setLastName"). Then they call a single method, setBeanAttributes(), on the entity bean, passing this hashtable of bean attributes as an argument (see Listing 1, Our Approach). In the setBeanAttributes() method, the code examines each of the keys provided in the hashtable, invoking the corresponding method with the key's paired object as the single argument.

The companion method, getBeanAttributes(), works in a similar way, looping through the bean's methods, finding all those whose names begin with the conventional "get" substring and putting the return object in the hashtable with a key named for the invoked get method.

Implementation Details
As shown in Figure 2, application architects can easily include these methods in their application design. We write the setBeanAttributes() method implementation in a parent class called EntityBeanServices (EBS) (see Listing 2). Each entity bean extends this class as well as implements javax.ejb.EntityBean, which the EJB specification requires. The remote interfaces can also extend an EntityBeanServicesInt interface (see Listing 3). All entity beans will then have setBeanAttributes() and getBeanAttributes() defined in the remote interface and implemented in the bean class, just as the EJB specification requires. Each entity bean now has access to setBeanAttributes() and getBeanAttributes() without any further coding on the beans (other than the two class extensions). Should you need to update attributes on more than one bean in a single transaction, you can write a session bean method that takes as arguments one hashtable per entity bean. The body of that method then looks up each bean and calls setBeanAttributes() on it, preserving all the set methods inside a single, container-managed transaction.

figure 2
Figure 2:  Universal wrapper class diagram

When you pass a hashtable to setBeanAttributes(), the key value pair consists of the set method's name and an object intended as the argument to that set method. Even if you use primitive bean attributes, setBeanAttributes() can handle the required manipulation from, for example, an Integer object to an int primitive (in fact, the java.lang.reflect package provides this service).

The methods of the EntityBeanServices class inherited by a particular enterprise bean uses the default isolation level and transactional attribute declared in the bean's deployment descriptor. For example, if the default transaction attribute is set to TX_REQUIRED, a call to setBeanAttributes() invokes all set methods together in a single transaction. Thus, if a single set method invocation fails in this bulk operation, the entire transaction will be rolled back. Given the flexible nature of the transactional attributes model for EJB, developers can implement specific transactional strategies for both the bean's own methods as well as those of the parent class.

Extensions of EBS
Most bean developers have faced the following problem posed by the ejbCreate() method. ejbCreate() asks the developer to define (via the method signature) which data fields the client code must provide to create an entity. As those requirements change, old ejbCreate() definitions must be changed or preserved as obsolete versions. With EntityBeanServices in place, we can call ejbCreate() passing only a hashtable and make a setBeanAttributes() call within that method. In this way we require only a single ejbCreate() method that we never have to change. Of course the developer must be sure to include key/value pairs for all not null attributes.

Another modification to EntityBeanServices will improve the performance of your calls to getBeanAttributes(). Rather than invoke each and every get method on the bean, even when you need only a subset of the attributes returned, you could overload getBeanAttributes(). Provide a second signature that takes as an argument a hashtable with keys specified for those get methods you hope to execute. Should you require only five attributes from the bean, this overloaded form will allow you to execute precisely five methods and no more.

We have considered numerous other features that beans might inherit from this parent class that we hope to share in future articles. We look forward to any extensions of EBS you work out as you incorporate this pattern into your applications.

Drawbacks
While we've found EntityBeanServices an excellent tool for our distributed EJB applications, we should note the price to pay for this convenience, even though we find it relatively small. The greatest inconvenience of EntityBeanServices is the absence of compile-time checking. Since attributes are put into a hashtable with method names as hashkeys, a simple misspelling can cause runtime grief. We've tried to minimize this concern by providing detailed output when throwing exceptions from EntityBeanServices. We offer several likely causes for the runtime error and use the reflection class to be sure the developer knows which bean the client was trying to update when the error occurred. To improve error handling, we encourage developers to provide the class with a set of custom exceptions.

We also experienced some problems initially when trying to get or set null object references as bean data members. Java 1.1.7 developers can avoid this by creating their own NullObject, which represents a null reference. Java 2 resolves this problem easily with the java.util.HashMap class, accepting key value pairs in which the value is a null object reference. You may also find Java 2 options such as java.util.Vector and java.util.LinkedList more efficient for setBeanAttributes(). Any of these will work as the argument type and eliminate inefficiencies caused by the initial memory allocation and code synchronization of java.util.Hashtable.

Note that changes to EntityBeanServices method signatures require the architect to rebuild all the descendent beans. Since we still experiment with new concepts in EBS, we wrote a simple shell script to rebuild our beans and run it after hours. If you use only the two methods we provide or carefully plan your implementation of EntityBeansServices ahead of time, you shouldn't encounter this problem.

Finally, those of you developing beans for commercial use will have to package EBS along with your beans. This may create minor inconveniences for you.

Conclusion
Our need for transaction-aware bulk updates led us to this bean superclass, which has served us faithfully. We've eliminated some of our developers' tedious coding responsibilities and eased the maintenance required when we augment or prune our entity beans. Although we always hope they won't, data structures may shift and change even deep into the development process, and EntityBeanServices help minimize the impact of these changes. We've controlled the risks associated with runtime errors by providing clear direction to the test developer as to the likely nature of the problem and have found numerous relevant extensions for this class. We encourage you to take this concept, work with it, and share your results and experiences with us.

Acknowlegements
We would like to thank FCG Doghouse for their support; Shaheem Sait for inspiring us to write this article; and Art Solano, Jennifer Terry, and Jonathan Leibundguth for useful comments.

Author Bios
Andrei Povodyrev is a senior software development specialist with FCG-Doghouse, Inc., experienced in the analysis, design, development, and maintenance of business information systems. He has specific expertise in Java, EJB, JSP, PowerBuilder, C++, Sybase, and Oracle systems and is a certified programmer for the Java 1.1 Platform. He can be contacted at [email protected]

Alan Askew is a senior software development specialist with FCG-Doghouse, Inc., and has worked in both management and technical consulting in a variety of industries. He has particular expertise in Java, EJB, JSP, PowerBuilder, and Oracle. He's a certified programmer for the Java 1.1 Platform and has worked through half of his Oracle DBA Certification. He can be contacted at [email protected]

	


Listing 1
 
public class EJBClient{ 
        public static void main(String [] args){ 
                try{ 
        Member ourMember; 
//Put code here to get the remote interface for a particular Member object 

//To change Member data before, you might have used one of the three styles: 

//Style 1. Calling set methods directly...Requires three network round trips 
//javax.transaction UserTransaction     
ut = //get the UserTransaction 
        //ut.begin 
                        //ourMember.setFirstName("Jane"); 
                        //ourMember.setLastName("Doe"); 
                        //ourMember.setMagazineName("Computer Weekly"); 
                        //ut.commit(); 

                        //Style 2. A long-parameter list set method is inflexible as 
     the number of arguments is //fixed 
                        // ourMember.setSubscriptionInformation("Jane", "Doe", 
     "Computer Weekly"); 

                        //Style 3. A set method with a custom wrapper class 
     (MemberWrapper)is cumbersome to maintain: changes in 
     entity bean will require corresponding changes on wrapper 
     class; each entity bean requires its own wrapper 
                        //MemberWrapper mWrapper = new MemberWrapper(); 
                        //mWrapper.setFirstName("Jane"); 
                        //mWrapper.setLastName("Doe"); 
                        //mWrapper.setMagazineName("Computer Weekly"); 

                        //ourMember.setSubscriptionInformation(mWrapper); 

                        // OUR APPROACH
                        //Using EntityBeanServices, you prepare a hashtable 
                        java.util.Hashtable ht2 = new java.util.Hashtable(); 
                        ht2.put("setFirstName","Jane"); 
                        ht2.put("setLastName","Doe"); 
                        ht2.put("setMagazineName","Computer Weekly"); 
                        //Then make a single call, uniting the set methods in a single 
                        transaction. //Universal form for every entity bean in your 
    application 
                        ourMember.setBeanAttributes(ht2); 

                } //end try 
                catch(Exception e){ 
                        e.printStackTrace(); 
                } //end catch 
        } //end main method 
} //end class 
  
  

Listing 2

public class EntityBeanServices { 

        public void setBeanAttributes(Hashtable ht) throws java.rmi.RemoteException{ 

                //invoke set methods based on keys in ht 
                String key, name, methodName = null; 
                boolean methodNotFound = true; 
                Object arglist[] = new Object[1]; 
                Enumeration keys = ht.keys(); 

                try{ 
                        name  = getClass().getName(); 

                        Method m[] = getClass().getDeclaredMethods(); 

                        while(keys.hasMoreElements()){ 
                                key =  (String)keys.nextElement(); 
                                methodNotFound = true; 
                                for(int i = 0; i < m.length; i++){ 
                                        methodName = m[i].getName(); 

// loop through bean's methods to find a match 
// some filtering based on common sense is desirable    
//match name of the method 
                  if(key.equalsIgnoreCase(methodName)) 
//get methods must be public 
                  if(m[i].getModifiers() == Modifier.PUBLIC) 
//set methods have a single argument 
                  if((m[i].getParameterTypes()).length == 1) 
//set methods must not  start with "get" 
      if(!methodName.startsWith("get")) 
      if(!methodName.startsWith("ejb")) 
																
{
    methodNotFound = false; 
    arglist[0] = ht.get(key); 
//invoke matched method 
 m[i].invoke(this, arglist); 
}// end if 
 }//end for 
   if(methodNotFound) throw new Exception("Attempt to 
     invoke a set method " + key + " on " + name + " failed. \n"  
	 + "       
	 Possible reasons: \n" 
	 + " 1) it is not a set method;\n " 
	 + " 2) method has more than 1 argument;\n" 
	 + " 3) method name does not start with 'set'\n" 
	 + " 4) no such method in the class" ); 

                        }//end while 

                }catch(Throwable e){ 
                        e.printStackTrace(); 
                  throw new java.rmi.RemoteException("Exception is rethrown");} 
        } 
  

        public Hashtable getBeanAttributes(){ 
                // identify and invoke all get methods for this entity bean 
                Hashtable ht = new Hashtable(); 
                Object arglist[] = new Object[0]; 

                try{ 
                        //get all methods of the EJB 
                        Method m[] = getClass().getDeclaredMethods(); 

                        // filter get methods and invoke them 
                        for(int i = 0; i < m.length; i++){ 
 if((m[i].getParameterTypes()).length != 0)    continue; 
        //get methods do not have parameters 
 if(m[i].getModifiers() != Modifier.PUBLIC)    continue; 
//get methods must be public  continue; 
        if(!m[i].getName().startsWith("get"))                   
//get methods must start with "get" 
        if(m[i].getName().equals("getBeanAttributes")) 
		continue;          
		//should not be itself 

                                Object ob = m[i].invoke(this, arglist); 
                                if (ob != null) 
                                                ht.put(m[i].getName(), ob); 
                        } 
                }catch(Throwable e){ 
                        e.printStackTrace(); 
                } 
                return ht; 
        } 

} 
  
  

Listing 3

/** 
* EntityBeanServicesInt is designed as a parent interface for 
   EJB remote interfaces. 
* Interface declares methods that are implemented in 
   EntitybeanServices class 
**/ 
public interface EntityBeanServicesInt { 
        public void setBeanAttributes(java.util.Hashtable ht) throws 
  java.rmi.RemoteException; 
        public java.util.Hashtable getBeanAttributes() throws 
  java.rmi.RemoteException; 
} 
  



  
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.