![]() |
|
|
![]()
|
Transfer ObjectContextApplication clients need to exchange data with enterprise beans. ProblemJ2EE applications implement server-side business components as session beans and entity beans. Some methods exposed by the business components return data to the client. Often, the client invokes a business object's get methods multiple times until it obtains all the attribute values. Session beans represent the business services and are not shared between users. A session bean provides coarse-grained service methods when implemented per the Session Facade pattern. Entity beans, on the other hand, are multiuser, transactional objects representing persistent data. An entity bean exposes the values of attributes by providing an accessor method (also referred to as a getter or get method) for each attribute it wishes to expose. Every method call made to the business service object, be it an entity bean or a session bean, is potentially remote. Thus, in an EJB application such remote invocations use the network layer regardless of the proximity of the client to the bean, creating a network overhead. Enterprise bean method calls may permeate the network layers of the system even if the client and the EJB container holding the entity bean are both running in the same JVM, OS, or physical machine. Some vendors may implement mechanisms to reduce this overhead by using a more direct access approach and bypassing the network. As the usage of these remote methods increases, application performance can significantly degrade. Therefore, using multiple calls to get methods that return single attribute values is inefficient for obtaining data values from an enterprise bean. Forces
SolutionUse a Transfer Object to encapsulate the business data. A single method call is used to send and retrieve the Transfer Object. When the client requests the enterprise bean for the business data, the enterprise bean can construct the Transfer Object, populate it with its attribute values, and pass it by value to the client. Clients usually require more than one value from an enterprise bean. To reduce the number of remote calls and to avoid the associated overhead, it is best to use Transfer Objects to transport the data from the enterprise bean to its client. When an enterprise bean uses a Transfer Object, the client makes a single remote method invocation to the enterprise bean to request the Transfer Object instead of numerous remote method calls to get individual attribute values. The enterprise bean then constructs a new Transfer Object instance, copies values into the object and returns it to the client. The client receives the Transfer Object and can then invoke accessor (or getter) methods on the Transfer Object to get the individual attribute values from the Transfer Object. Or, the implementation of the Transfer Object may be such that it makes all attributes public. Because the Transfer Object is passed by value to the client, all calls to the Transfer Object instance are local calls instead of remote method invocations. StructureFigure 8.5 shows the class diagram that represents the Transfer Object pattern in its simplest form.
As shown in this class diagram, the Transfer Object is constructed on demand by the enterprise bean and returned to the remote client. However, the Transfer Object pattern can adopt various strategies, depending on requirements. The "Strategies" section explains these approaches. Participants and ResponsibilitiesFigure 8.6 contains the sequence diagram that shows the interactions for the Transfer Object pattern.
ClientThis represents the client of the enterprise bean. The client can be an end-user application, as in the case of a rich client application that has been designed to directly access the enterprise beans. The client can be Business Delegates (see "Business Delegate" on page 248) or a different BusinessObject. BusinessObjectThe BusinessObject represents a role in this pattern that can be fulfilled by a session bean, an entity bean, or a Data Access Object (DAO). The BusinessObject is responsible for creating the Transfer Object and returning it to the client upon request. The BusinessObject may also receive data from the client in the form of a Transfer Object and use that data to perform an update. TransferObjectThe TransferObject is an arbitrary serializable Java object referred to as a Transfer Object. A Transfer Object class may provide a constructor that accepts all the required attributes to create the Transfer Object. The constructor may accept all entity bean attribute values that the Transfer Object is designed to hold. Typically, the members in the Transfer Object are defined as public, thus eliminating the need for get and set methods. If some protection is necessary, then the members could be defined as protected or private, and methods are provided to get the values. By offering no methods to set the values, a Transfer Object is protected from modification after its creation. If only a few members are allowed to be modified to facilitate updates, then methods to set the values can be provided. Thus, the Transfer Object creation varies depending on an application's requirements. It is a design choice as to whether the Transfer Object's attributes are private and accessed via getters and setters, or all the attributes are made public. StrategiesThe first two strategies discussed are applicable when the enterprise bean is implemented as a session bean or as an entity bean. These strategies are called Updatable Transfer Objects Strategy and Multiple Transfer Objects Strategy. The following strategies are applicable only when the BusinessObject is implemented as an entity bean: Entity Inherits Transfer Object Strategy and Transfer Object Factory Strategy. Updatable Transfer Objects StrategyIn this strategy, the Transfer Object not only carries the values from the BusinessObject to the client, but also can carry the changes required by the client back to the business object. Figure 8.7 is a class diagram showing the relationship between the BusinessObject and the Transfer Object.
The BusinessObject creates the Transfer Object. Recall that a client may need to access the BusinessObject values not only to read them but to modify these values. For the client to be able to modify the BusinessObject attribute values, the BusinessObject must provide mutator methods. Mutator methods are also referred to as setters or set methods. Instead of providing fine-grained set methods for each attribute, which
results in network overhead, the BusinessObject can expose a coarse-grained
The There is an impact on the design using the updatable Transfer Objects in terms of update propagation, synchronization, and version control. Figure 8.8 shows the sequence diagram for the entire update interaction.
Multiple Transfer Objects StrategySome application business objects can be very complex. In such cases, it is possible that a single business object produces different Transfer Objects, depending on the client request. There exists a one-to-many relationship between the business object and the many Transfer Objects it can produce. In these circumstances, this strategy may be considered. For instance, when the business object is implemented as a session bean, typically applying the Session Facade pattern, the bean may interact with numerous other business components to provide the service. The session bean produces its Transfer Object from different sources. Similarly, when the BusinessObject is implemented as a coarse-grained entity bean, typically applying the Composite Entity pattern, the entity bean will have complex relationships with a number of dependent objects. In both these cases, it is good practice to provide mechanisms to produce Transfer Objects that actually represent parts of the underlying coarse-grained components. For example, in a trading application, a Composite Entity that represents a customer portfolio can be a very coarse-grained complex component that can produce Transfer Objects that provide data for parts of the portfolio, like customer information, lists of stocks held, and so on. A similar example is a customer manager session bean that provides services by interacting with a number of other BusinessObjects and components to provide its service. The customer manager bean can produce discrete small Transfer Objects, like customer address, contact list, and so on, to represent parts of its model. For both these scenarios, it is possible to adopt and apply the Multiple Transfer Objects Strategy so that the business component, whether a session bean or an entity bean, can create multiple types of Transfer Objects. In this strategy, the business entity provides various methods to get different Transfer Objects. Each such method creates and returns a different type of Transfer Object. The class diagram for this strategy is shown Figure 8.9.
When a client needs a Transfer Object of type TransferObjectA, it invokes the entity's getDataA() method requesting TransferObjectA. When it needs a Transfer Object of type TransferObjectB, it invokes the entity's getDataB() method requesting TransferObjectB, and so on. This is shown in the sequence diagram in Figure 8.10.
Entity Inherits Transfer Object StrategyWhen the BusinessObject is implemented as an entity bean and the clients typically need to access all the data from the entity bean, then the entity bean and the Transfer Object both have the same attributes. In this case, since there exists a one-to-one relationship between the entity bean and its Transfer Object, the entity bean may be able to use inheritance to avoid code duplication. In this strategy, the entity bean extends (or inherits from) the Transfer Object class. The only assumption is that the entity bean and the Transfer Object share the same attribute definitions. The class diagram for this strategy is shown in Figure 8.11.
The TransferObject implements one or more Thus, this strategy eliminates code duplication between the entity and the Transfer Object. It also helps manage changes to the Transfer Object requirements by isolating the change to the Transfer Object class and preventing the changes from affecting the entity bean. This strategy has a trade-off related to inheritance. If the Transfer Object is shared through inheritance, then changes to this Transfer Object class will affect all its subclasses, potentially mandating other changes to the hierarchy. The sequence diagram in Figure 8.12 demonstrates this strategy.
The sample implementation for the Entity Inherits Transfer Object Strategy is shown in Example 8.10 (ContactTO - Transfer Object Class) and Example 8.11 (ContactEntity - Entity Bean Class). Transfer Object Factory StrategyThe Entity Inherits Transfer Object Strategy can be further extended to support multiple Transfer Objects for an entity bean by employing a Transfer Object factory to create Transfer Objects on demand using reflection. This results in an even more dynamic strategy for Transfer Object creation. To achieve this, define a different interface for each type of Transfer Object that must be returned. The entity bean implementation of Transfer Object superclass must implement all these interfaces. Furthermore, you must create a separate implementation class for each defined interface, as shown in the class diagram for this strategy in Figure 8.13. Once all interfaces have been defined and implemented, create a method in the TransferObjectFactory that is passed two arguments:
The TransferObjectFactory can then instantiate an object of the correct class, set its values, and return the newly created Transfer Object instance.
The sequence diagram for this strategy is shown in Figure 8.14.
The client requests the Transfer Object from the BusinessEntity. The BusinessEntity passes the required Transfer Object's class to the TransferObjectFactory, which creates a new Transfer Object of that given class. The TransferObjectFactory uses reflection to dynamically obtain the class information for the Transfer Object class and construct a new Transfer Object instance. Getting values from and setting values into the BusinessEntity by the TransferObjectFactory is accomplished by using dynamic invocation. An example implementation for this strategy is shown in the "Sample Code" section for "Implementing Transfer Object Factory Strategy" on page 284. The benefits of applying the Transfer Object Factory Strategy are as follows: There is less code to write in order to create Transfer Objects. The same Transfer Object factory class can be reused by different enterprise beans. When a Transfer Object class definition changes, the Transfer Object factory automatically handles this change without any additional coding effort. This increases maintainability and is less error prone to changes in Transfer Object definitions. The Transfer Object Factory Strategy has the following consequences: It is based on the fact that the enterprise bean implementation extends (inherits) from the complete Transfer Object. The complete Transfer Object needs to implement all the interfaces defined for different Transfer Objects that the entity bean needs to supply. Naming conventions must be adhered to in order to make this strategy work. Since reflection is used to dynamically inspect and construct Transfer Objects, there is a slight performance loss in construction. However, when the overall communication time is considered, such loss may be negligible in comparison. There is a trade-off associated with this strategy. Its power and flexibility must be weighed against the performance overhead associated with runtime reflection. Consequences
Sample CodeImplementing the Transfer Object PatternConsider an example where a business object called Project is modeled
and implemented as an entity bean. The Project entity bean needs to send
data to its clients in a Transfer Object when the client invokes its Example 8.3 Implementing the Transfer Object Pattern - Transfer Object Class // Transfer Object to hold the details for Project public class ProjectTO implements java.io.Serializable { public String projectId; public String projectName; public String managerId; public String customerId; public Date startDate; public Date endDate; public boolean started; public boolean completed; public boolean accepted; public Date acceptedDate; public String projectDescription; public String projectStatus; // Transfer Object constructors... } The sample code for the entity bean that uses this Transfer Object is shown in Example 8.4. Example 8.4 Implementing the Transfer Object Pattern - Entity Bean Class ... public class ProjectEntity implements EntityBean { private EntityContext context; public String projectId; public String projectName; public String managerId; public String customerId; public Date startDate; public Date endDate; public boolean started; public boolean completed; public boolean accepted; public Date acceptedDate; public String projectDescription; public String projectStatus; private boolean closed; // other attributes... private ArrayList commitments; ... // Method to get Transfer Object for Project data public ProjectTO getProjectData() { return createProjectTO(); } // method to create a new Transfer Object and // copy data from entity bean into the value // object private ProjectTO createProjectTO() { ProjectTO proj = new ProjectTO(); proj.projectId = projectId; proj.projectName = projectName; proj.managerId = managerId; proj.startDate = startDate; proj.endDate = endDate; proj.customerId = customerId; proj.projectDescription = projectDescription; proj.projectStatus = projectStatus; proj.started = started; proj.completed = completed; proj.accepted = accepted; proj.closed = closed; return proj; } ... } Implementing the Updatable Transfer Objects StrategyExample 8.4 can be extended to implement Updatable Transfer Objects Strategy.
In this case, the entity bean would provide a Example 8.5 Implementing Updatable Transfer Objects Strategy ... public class ProjectEntity implements EntityBean { private EntityContext context; ... // attributes and other methods as in Example 8.4 ... // method to set entity values with a Transfer Object public void setProjectData(ProjectTO updatedProj) { mergeProjectData(updatedProj); } // method to merge values from the Transfer Object into // the entity bean attributes private void mergeProjectData(ProjectTO updatedProj) { // version control check may be necessary here // before merging changes in order to // prevent losing updates by other clients projectId = updatedProj.projectId; projectName = updatedProj.projectName; managerId = updatedProj.managerId; startDate = updatedProj.startDate; endDate = updatedProj.endDate; customerId = updatedProj.customerId; projectDescription = updatedProj.projectDescription; projectStatus = updatedProj.projectStatus; started = updatedProj.started; completed = updatedProj.completed; accepted = updatedProj.accepted; closed = updatedProj.closed; } ... } Implementing the Multiple Transfer Objects StrategyConsider an example where a Resource entity bean is accessed by clients to request different Transfer Objects. The first type of Transfer Object, ResourceTO, is used to transfer data for a small set of attributes. The second type of Transfer Object, ResourceDetailsTO, is used to transfer data for a larger set of attributes. The client can use the former Transfer Object if it needs only the most basic data represented by that Transfer Object, and can use the latter if it needs more detailed information. Note that this strategy can be applied in producing two or more Transfer Objects that contain different data, and not just subset-superset as shown here. The sample code for the two Transfer Objects for this example are shown in Example 8.6 and Example 8.7. The sample code for the entity bean that produces these Transfer Objects is shown in Example 8.8, and finally the entity bean client is shown in Example 8.9. Example 8.6 Multiple Transfer Objects Strategy - ResourceTO // ResourceTO: This class holds basic information // about the resource public class ResourceTO implements java.io.Serializable { public String resourceId; public String lastName; public String firstName; public String department; public String grade; ... } // ResourceDetailsTO This class holds detailed // information about resource public class ResourceDetailsTO { public String resourceId; public String lastName; public String firstName; public String department; public String grade; // other data... public Collection commitments; public Collection blockoutTimes; public Collection skillSets; } // imports ... public class ResourceEntity implements EntityBean { // entity bean attributes ... // entity bean business methods ... // Multiple Transfer Object method : Get ResourceTO public ResourceTO getResourceData() { // create new ResourceTO instance and copy // attribute values from entity bean into TO ... return createResourceTO(); } // Multiple Transfer Object method : Get // ResourceDetailsTO public ResourceDetailsTO getResourceDetailsData() { // create new ResourceDetailsTO instance and copy // attribute values from entity bean into TO ... return createResourceDetailsTO(); } // other entity bean methods ... } ... private ResourceEntity resourceEntity; private static final Class homeClazz = corepatterns.apps.psa.ejb.ResourceEntityHome.class; ... try { ResourceEntityHome home = (ResourceEntityHome) ServiceLocator.getInstance().getHome( "Resource", homeClazz); resourceEntity = home.findByPrimaryKey( resourceId); } catch(ServiceLocatorException ex) { // Translate Service Locator exception into // application exception throw new ResourceException(...); } catch(FinderException ex) { // Translate the entity bean finder exception into // application exception throw new ResourceException(...); } catch(RemoteException ex) { // Translate the Remote exception into // application exception throw new ResourceException(...); } ... // retrieve basic Resource data ResourceTO vo = resourceEntity.getResourceData(); ... // retrieve detailed Resource data ResourceDetailsTO = resourceEntity.getResourceDetailsData(); ... Implementing the Entity Inherits Transfer Object StrategyConsider an example where an entity bean ContactEntity inherits all its properties from a Transfer Object ContactTO. Example 8.10 shows the code sample for an example Transfer Object ContactTO that illustrates this strategy. Example 8.10 Entity Inherits Transfer Object Strategy - Transfer Object Class // This is the Transfer Object class inherited by // the entity bean public class ContactTO implements java.io.Serializable { // public members public String firstName; public String lastName; public String address; // default constructor public ContactTO() {} // constructor accepting all values public ContactTO(String firstName, String lastName, String address){ init(firstName, lastName, address); } // constructor to create a new TO based // using an existing TO instance public ContactTO(ContactTO contact) { init (contact.firstName, contact.lastName, contact.address); } // method to set all the values public void init(String firstName, String lastName, String address) { this.firstName = firstName; this.lastName = lastName; this.address = address; } // create a new Transfer Object public ContactTO getData() { return new ContactTO(this); } } The entity bean sample code relevant to this pattern strategy is shown in Example 8.11. Example 8.11 Entity Inherits Transfer Object Strategy - Entity Bean Class public class ContactEntity extends ContactTO implements javax.ejb.EntityBean { ... // the client calls the getData method // on the ContactEntity bean instance. // getData() is inherited from the Transfer Object // and returns the ContactTO Transfer Object ... } Implementing Transfer Object Factory StrategyExample 8.12 demonstrates the Transfer Object Factory strategy. The entity bean extends a complete Transfer Object called CustomerContactTO. The CustomerContactTO Transfer Object implements two interfaces, Customer and Contact. The CustomerTO Transfer Object implements Customer, and the ContactTO Transfer Object implements Contact. Example 8.12 Transfer Object Factory Strategy - Transfer Objects and Interfaces public interface Contact extends java.io.Serializable { public String getFirstName(); public String getLastName(); public String getContactAddress(); public void setFirstName(String firstName); public void setLastName(String lastName); public void setContactAddress(String address); } public class ContactTO implements Contact { // member attributes public String firstName; public String lastName; public String contactAddress; // implement get and set methods per the // Contact interface here. ... } public interface Customer extends java.io.Serializable { public String getCustomerName(); public String getCustomerAddress(); public void setCustomerName(String customerName); public void setCustomerAddress(String customerAddress); } public class CustomerTO implements Customer { public String customerName; public String customerAddress; // implement get and set methods per the // Customer interface here. ... } public class CustomerContactTO implements Customer, Contact { public String firstName; public String lastName; public String contactAddress; public String customerName; public String customerAddress; // implement get and set methods per the // Customer and Contact interfaces here. ... } The entity bean code sample to obtain these three different Transfer Objects is shown Example 8.13. Example 8.13 Transfer Object Factory Strategy - Entity Bean Class public class CustomerContactEntity extends CustomerContactTO implements javax.ejb.EntityBean { // implement other entity bean methods...not shown // define constant to hold class name // complete Transfer Object. This is required by // the TransferObjectFactory.createTransferObject(...) public static final String COMPLETE_TO_CLASSNAME = "CustomerContactTO"; // method to return CustomerContactTO Transfer Object public CustomerContactTO getCustomerContact() { return (CustomerContactTO) TransferObjectFactory.createTransferObject( this, "CustomerContactTO", COMPLETE_TO_CLASSNAME); } // method to return CustomerTO Transfer Object public CustomerTO getCustomer() { return (CustomerTO) TransferObjectFactory.createTransferObject( this, "CustomerTO", COMPLETE_TO_CLASSNAME); } // method to return ContactTO Transfer Object public ContactTO getContact() { return (ContactTO) TransferObjectFactory.createTransferObject( this, "ContactTO", COMPLETE_TO_CLASSNAME); } // other entity bean business methods ... } The TransferObjectFactory class is shown in Example 8.14. Example 8.14 Transfer Object Factory Strategy - Factory Class import java.util.HashMap; import java.lang.*; /** * The factory class that creates a Transfer Object for a * given EJB. */ public class TransferObjectFactory { /** * Use a HashMap to cache class information for * Transfer Object classes */ private static HashMap classDataInfo = new HashMap(); /** * Create a Transfer Object for the given object. The * given object must be an EJB Implementation and have * a superclass that acts as the class for the entity's * Transfer Object. Only the fields defined in this * superclass are copied in to the Transfer Object. */ public static java.io.Serializable createTransferObject(Object ejb, String whichTOType, String completeTOType) { try { // Get the class data for the complete // Transfer Object type ClassData cData = getClassData (completeTOType); // Get class data for the requested TO type ClassData voCData = getClassData (whichTOType); // Create the Transfer Object of the requested // Transfer Object type... java.lang.Object whichTO = Class.forName(whichTOType).newInstance(); // get the TO fields for the requested TO // from the ClassData for the requested TO java.lang.reflect.Field[] voFields = voCData.arrFields; // get all fields for the complete TO // from the ClassData for complete TO java.lang.reflect.Field[] beanFields = cData.arrFields; // copy the common fields from the complete TO // to the fields of the requested TO for (int i = 0; i < voFields.length; i++) { try { String voFieldName = voFields[i].getName(); for (int j=0; j < beanFields.length; j++) { // if the field names are same, copy value if ( voFieldName.equals( beanFields[j].getName())) { // Copy value from matching field // from the bean instance into the new // Transfer Object created earlier voFields[i].set(whichTO, beanFields[j].get(ejb)); break; } } } catch (Exception e) { // handle exceptions that may be thrown // by the reflection methods... } } // return the requested Transfer Object return (java.io.Serializable)whichTO; } catch (Exception ex) { // Handle all exceptions here... } return null; } /** * Return a ClassData object that contains the * information needed to create * a Transfer Object for the given class. This information * is only obtained from the * class using reflection once, after that it will be * obtained from the classDataInfo HashMap. */ private static ClassData getClassData(String className){ ClassData cData = (ClassData)classDataInfo.get(className); try { if (cData == null) { // Get the class of the given object and the // Transfer Object to be created java.lang.reflect.Field[] arrFields ; java.lang.Class ejbTOClass = Class.forName(className); // Determine the fields that must be copied arrFields = ejbTOClass.getDeclaredFields(); cData = new ClassData(ejbTOClass, arrFields); classDataInfo.put(className, cData); } } catch (Exception e) { // handle exceptions here... } return cData; } } /** * Inner Class that contains class data for the * Transfer Object classes */ class ClassData { // Transfer Object Class public Class clsTransferObject; // Transfer Object fields public java.lang.reflect.Field[] arrFields; // Constructor public ClassData(Class cls, java.lang.reflect.Field[] fields) { clsTransferObject = cls; arrFields = fields; } } Related Patterns
|
|||||||||||||||||||||||
© 2001, Core J2EE Patterns, All Rights Reserved. |