Passing Objects by Value

Passing objects by value is tricky with Enterprise JavaBeans. Two simple rules will keep you out of most problem areas: objects that are passed by value should be fine-grained dependent objects or wrappers used in bulk accessors, and dependent objects should be immutable.

EJB 1.1: Dependent Objects

The concept of dependent objects was addressed in Chapter 6, which describes the use of dependent objects in EJB 2.0. But for EJB 1.1, dependent objects are a new concept. EJB 2.0 and EJB 1.1 use dependent objects differently, because EJB 2.0 can accommodate much more fine-grained entity beans than EJB 1.1.

Dependent objects are objects that have meaning only within the context of another business object. They typically represent fairly fine-grained business concepts, such as an address, phone number, or order item. For example, an address has little meaning when it is not associated with a business object such as Person or Organization. It depends on the context of the business object to give it meaning. Such an object can be thought of as a wrapper for related data. The fields that make up an address (street, city, state, and zip) should be packaged together in a single object called AddressDO. In turn, the AddressDO object is usually an attribute or property of another business object; in EJB, we would typically see a dependent object such as AddressDO as a property of an entity bean.

Here’s a typical implementation of an AddressDO object:

public class AddressDO implements java.io.Serializable {

    private String street;
    private String city;
    private String state;
    private String zip;

    public Address(String str, String cty, String st, String zp) {
        street = str;
        city = cty;
        state = st;
        zip = zp;
    }
    public String getStreet() {return street;}
    public String getCity() {return city;}
    public String getState() {return state;}
    public String getZip() {return zip;}
}

We want to make sure that clients do not change an AddressDO object’s fields. The reason is quite simple: the AddressDO object is a copy, not a remote reference. Changes to an AddressDO object are not reflected in the entity from which it originated. If a client changes the AddressDO object, those changes will not be reflected in the database. Making the AddressDO object immutable helps to ensure that clients do not mistake this fine-grained object for a remote reference, thinking that a change to an address property will be reflected on the server.

To change an address, the client is required to remove the AddressDO object and add a new one with the changes. Again, this is because the dependent object is not a remote object and changes to its state are not reflected on the server. Here is the remote interface to a hypothetical Employee bean that aggregates address information:

public interface Employee extends javax.ejb.EJBObject {
    public AddressDO [] getAddresses() throws RemoteException;
    public void removeAddress(AddressDO adrs) throws RemoteException;
    public void addAddress(AddressDO adrs) throws RemoteException;
    // ... Other business methods follow.
}

In this interface, the Employee can have many addresses, which are obtained as a collection of pass-by-value AddressDO objects. To remove an address, the target AddressDO object is passed back to the bean in the removeAddress() method. The bean class then removes the matching AddressDO object from its persistence fields. To add an address, an AddressDO object is passed to the bean by value.

Dependent objects may be persistence fields or they may be properties that are created as needed. The following code demonstrates both strategies using the AddressDO object. In the first listing, the AddressDO object is a persistence field, while in the second the AddressDO object is a property that does not correspond to any single field; we create the AddressDO object as needed but do not save it as part of the bean. Instead, the AddressDO object corresponds to four persistence fields: street, city, state, and zip.

// Address as a persistence field
public class Person extends javax.ejb.EntityBean {
    public AddressDO address;
    public AddressDO getAddress() {
        return address;
    }
    public void setAddress(AddressDO addr) {
        address = addr;
    }
    ...
}

// Address as a property
public class Person extends javax.ejb.EntityBean {
    public String street;
    public String city;
    public String state;
    public String zip;

    public AddressDO getAddress() {
        return new AddressDO(street, city, state, zip);
    }
    public void setAddress(AddressDO addr) {
        street = addr.street;
        city = addr.city;
        state = addr.state;
        zip = addr.zip;
    }
    ...
}

When a dependent object is used as a property, it can be synchronized with the persistence fields in the accessor methods themselves or in the ejbLoad() and ejbStore() methods. Both strategies are acceptable.

This discussion of dependent objects has been full of generalizations and thus may not be applicable to all situations. That said, it is recommended that only very fine-grained, dependent, immutable objects should be passed by value. All other business concepts should be represented as entity or session beans. A very fine-grained object is one that has very little behavior, consisting mostly of get and set methods. A dependent object is one that has little meaning outside the context of its aggregator. An immutable object is one that provides only get methods and thus cannot be modified once created.

Validation Rules in Dependent Objects

Dependent objects make excellent homes for format-validation rules. Format validation ensures that a simple data construct adheres to a predetermined structure or form. As an example, a Zip Code always has a certain format. It must be composed of digits; it must be five or nine digits in length; and if it has nine digits, it must use a hyphen as a separator between the fifth and sixth digits. Checking to see that a Zip Code follows these rules is format validation.

One problem that all developers face is deciding where to put validation code. Should data be validated at the user interface (UI), or should it be done by the bean that uses the data? Validating the data at the UI has the advantage of conserving network resources and improving performance. However, validating data in the bean, on the middle tier, ensures that the logic is reusable across user interfaces. Dependent objects provide a logical compromise that allows data to be validated on the client but remain independent of the UI. Since the validation logic is located in the constructor of the dependent object, the object automatically validates the data when it is created. When data is entered at the UI (e.g., GUI, servlet, or JSP), the UI can validate it using its corresponding dependent object. If the data is valid, the dependent object is created; if the data is invalid, the constructor throws an exception.

The following code shows a dependent object that represents a Zip Code. It adheres to the rules for a dependent object as I have defined them and also includes format-validation rules in the constructor:

public class ZipCodeDO implements java.io.Serializable {

    private String code;  
    private String boxNumber;  

    public ZipCode(String zipcode) throws ValidationException {
        if (zipcode == null)  
            throw new ValidationException("Zip Code cannot be null");  
        else if (zipcode.length()==5 && ! isDigits(zipcode))  
            throw new ValidationException("Zip Code must be all digits");
        else if (zipcode.length()==10 ) {  
            if (zipcode.charAt(5) == '-' ) {  
                code = zipcode.substring(0,5);  
                if (isDigits( code )){  
                    boxNumber = zipcode.substring(6);
                    if (isDigits( boxNumber ))  
                        return;  
                }  
            }  
            throw new ValidationException("Zip Code must be of form #####-####");  
        }
    }  
    private boolean isDigits(String str) {  
        for (int i = 0; i < str.length(); i++) {  
            char chr = str.charAt(i);  
            if ( ! Character.isDigit(chr)) {  
                return false;  
            }  
        }
        return true;  
    }  
    public String getCode() { return code; }  

    public String getBoxNumber() { return boxNumber; }  

    public String toString() {  
        return code+'-'+boxNumber;  
    }  
}

This simple example illustrates that format validation can be performed by dependent objects when the object is constructed at the user interface or client. Any format-validation errors are reported immediately, without any interaction with the middle tier of the application. In addition, any business object that uses ZipCodeDO automatically gains the benefit of the validation code, making the validation rules reusable (and consistent) across beans. Placing format validation in the dependent object is also a good coding practice because it makes the dependent object responsible for its own validation; responsibility is a key concept in object-oriented programming. Of course, dependent objects are useful for validation only if the Enterprise JavaBeans implementation supports pass-by-value.

As an alternative to using dependent objects, format validation can be performed by the accessors of enterprise beans. If, for example, a customer bean has accessors for setting and obtaining the Zip Code, those accessors could incorporate the validation code. While this strategy is more efficient from a network perspective—passing a String value is more efficient than passing a dependent object by value—it is less reusable than housing format-validation rules in dependent objects.

Bulk Accessors for Remote Clients

Most entity beans have several persistence fields that are manipulated through accessor methods. Unfortunately, the one-to-one nature of the accessor idiom can result in many invocations when accessing an entity, which translates into a lot of network traffic when using remote references. Every field you want to modify requires a method invocation, which in turn requires you to go out to the network. One way to reduce network traffic when accessing entities from remote clients is to use bulk accessors, which package access to several persistence fields into a single accessor method. Bulk accessors provide get and set methods that work with structures or simple pass-by-value objects. The following code shows how a bulk accessor could be implemented for the Cabin bean:

// CabinData DataObject
public class CabinData {
    public String name;
    public int deckLevel;
    public int bedCount;
    public CabinData() {
    }
    public CabinData(String name, int deckLevel, int bedCount) { 
        this.name = name; 
        this.deckLevel = deckLevel; 
        this.bedCount = bedCount; 
    } 
} 

// CabinBean using bulk accessors
public class CabinBean implements javax.ejb.EntityBean { 
    public int id; 
    public String name; 
    public int deckLevel; 
    public int ship; 
    public int bedCount; 
    // bulk accessors 
    public CabinData getData() { 
        return new CabinData(name,deckLevel,bedCount); 
    } 
    public void setData(CabinData data) { 
        name = data.name; 
        deckLevel = data.deckLevel; 
        bedCount = data.bedCount; 
    } 
    // simple accessors and entity methods 
    public String getName() { 
        return name; 
    } 
    public void setName(String str) { 
        name = str; 
    } 
    // more methods follow 
}

The getData() and setData() methods allow several fields to be packaged into a simple object and passed between the client and bean in one method call. This is much more efficient than requiring three separate calls to set the name, deck level, and bed count.

Rules-of-thumb for bulk accessors

Here are some guidelines for creating bulk accessors:

Data objects are not dependent objects

Data objects and dependent objects serve clearly different purposes, but they may appear at first to be the same. Where dependent objects represent business concepts, data objects do not; they are simply an efficient way of packaging an entity’s fields for access by clients. Data objects may package dependent objects along with more primitive attributes, but they are not dependent objects themselves.

Data objects are simple structures

Keep the data objects as simple as possible; ideally, they should be similar to a simple struct in C. In other words, the data object should not have any business logic at all; it should only have fields. All the business logic should remain in the entity bean, where it is centralized and can be maintained easily.

In order to keep the semantics of a C struct, data objects should not have accessor (get and set) methods for reading and writing their fields. The CabinData class does not have accessor methods; it only has fields and a couple of constructors. The lack of accessors reinforces the idea that the data object exists only to bundle fields together, not to “behave” in a particular manner. As a design concept, we want the data object to be a simple structure devoid of behavior; this is a matter of form following function. The exception is the multiargument constructor, which is left as a convenience for the developer.

Bulk accessors bundle related fields

The bulk accessors can pass a subset of the entity’s data. Some fields may have different security or transaction needs, which require that they be accessed separately. In the CabinBean, only a subset of the fields (name, deckLevel, bedCount) is passed in the data object. The id field is not included for several reasons: it does not describe the business concept, it is already found in the primary key, and the client should not edit it. The ship field is not passed because it should be updated only by certain individuals; the identities authorized to change this field are different from the identities allowed to change the other fields. Similarly, access to the ship may fall under a different transaction isolation level than the other fields (e.g., Serializable versus Read Committed).

In addition, it is more efficient to design bulk accessors that pass logically related fields. In entity beans with many fields, it is possible to group together certain fields that are normally edited at the same time. An Employee bean, for example, might have several fields that are demographic in nature (address, phone, email) and can be logically separated from fields that are specific to benefits (compensation, 401K, health, vacation). A group of logically related fields can have its own bulk accessor. You might even want several bulk accessors in the same bean:

public interface Employee extends javax.ejb.EJBObject { 

    public EmployeeBenefitsData getBenefitsData() 
       throws RemoteException; 

    public void setBenefitsData(EmployeeBenefitsData data)
        throws RemoteException; 

    public EmployeeDemographicData getDemographicData() 
        throws RemoteException;

    public void setDemographicData(EmployeeDemographicData data) 
        throws RemoteException; 

    // more simple accessors and other business methods follow 

}
Retain simple accessors

Simple accessors (get and set methods for single fields) should not be abandoned when using bulk accessors. It is still important to allow editing of single fields. It’s just as wasteful to use a bulk accessor to change one field as it is to change several fields using simple accessors.

Local references in EJB 2.0 container-managed persistence are very efficient, so the performance benefits of bulk accessors are minimal. If you’re using EJB 2.0, use bulk accessors with remote interfaces whenever it makes sense according to the guidelines given here, but use them sparingly with local interfaces.

Entity Objects

The earlier section on passing objects by value gave you some good ground rules for when and how to use pass-by-value in EJB. Business concepts that do not meet the dependent object criteria should be modeled as either session or entity beans. It is easy to mistakenly adopt a strategy of passing business objects that would normally qualify as entity beans (e.g., Customer, Ship, and City) by value to the clients. Overzealous use of bulk accessors that pass data objects loaded with business behavior is bad design. The belief is that passing the entity objects to the client avoids unnecessary network traffic by keeping the set and get methods local. The problem with this approach is object equivalence. Entities are supposed to represent the actual data on the database, which means that they are shared and always reflect the current state of the data. Once an object is resident on the client, it is no longer representative of the data. It is easy for a client to end up with many dirty copies of the same entity, resulting in inconsistent processing and representation of data.

While it is true that the set and get methods of entity objects can introduce a lot of network traffic, implementing pass-by-value objects instead of using entity beans is not the answer. The network problem can be avoided if you stick to the design strategy elaborated throughout this book: remote clients interact primarily with session beans, not entity beans. You can also reduce network traffic significantly by using bulk accessors, provided that these accessors transfer only structures with no business logic. Finally, try to keep the entity beans on the server encapsulated in workflow defined by session beans. This eliminates the network traffic associated with entities, while ensuring that they always represent the correct data.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset