J2EE Design Strategies Part IV

Enterprise Java Beans

Fine-grained vs. Coarse-grained Entities

One of the design patterns discussed by the J2EE tutorials from Sun is this topic of fine-grained vs. coarse-grained entities. The issue here is one of client access. Generally, you want to keep the amount of round-trips to the application server as small as possible. Shuttling data over a network connection in a distributed application is an expensive operation in terms of time. In general, you should avoid creating many small entity beans and accessing them directly from the client. For example, if you have a customer entity in your application, the customer likely has an address (or many addresses). You should not create an Address entity to handle addresses. Instead, you should create an Address dependent object (also known as a value object). A dependent object is a simple Java class that encapsulates information and typically consists of only accessors and a constructor.

To access a Customer’s address, the customer entity has a method that returns an Address dependent object to you. If Address was modeled as an entity bean, you would have to access it through its remote reference just as you do Customer. By passing back a dependent object, you can cut down on the number of remote method calls you must make (for example, for all the fields in the address). This is what is meant by coarse-grained objects.

Here is an example of a dependent object for address:

Listing 14: Example of an address value object

import java.io.Serializable;
public class Address implements Serializable {
    private String addr1;
    private String addr2;
    private String city;
    private String state;
    private String zip;
    public Address(String addr1, String addr2, String city, String state,
            String zip) throws ValidationException {
        if (zip.length() < 5 || zip.length() >10) {
            throw new ValidationException("Zip code format error");
        }
        this.addr1 = addr1;
        this.addr2 = addr2;
        this.city = city;
        this.state = state;
        this.zip = zip;
    }
    public String getAddr1() {
        return addr1;
    }
    public String getAddr2() {
        return addr2;
    }
    public String getCity() {
        return city;
    }
    public String getState() {
        return state;
    }
    public String getZip() {
        return zip;
    }
}

Dependent objects also provide a good place to house simple validation rules. In the example above, the constructor throws a ValidationException if the zip code is not formatted correctly. Note that all such code must appear in the constructor because there are no set methods (more about this shortly).

Another variation on this theme is to create bulk accessors and mutators. In this model, if you need to populate many of the fields of an entity, create a mutator that takes a value object as the parameter and make a single call, passing an instantiated value object. This way, you can make a single remote call instead of several.

Here is an example of a bulk accessor and mutator for Credit Card data.

Listing 15: Using bulk accesors and mutators

Credit Card Data class

public class CreditCardData implements Serializable {
    private String number;
    private String type;
    private String expirationDate;
    public CreditCardData() {
    }
    public CreditCardData(String number, String type, String expDate) {
        this.number = number;
        this.type = type;
        this.expirationDate = expDate;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getType() {
        return type;
    }u
    public void setExpirationDate(String expirationDate) {
        this.expirationDate = expirationDate;
    }
    public String getExpirationDate() {
        return expirationDate;
    }
 //-- more methods follow...
}

EJB using bulk accessors and mutators

public class OrderBean implements EntityBean {
    EntityContext entityContext;
    public String ccNumber;
    public String ccType;
    public String ccExpiration;
    public CreditCardData getData() {
        return new CreditCardData(ccNumber, ccType, ccExpiration);
    }
    public void setData(CreditCardData ccData) {
        ccNumber = ccData.getNumber();
        ccType = ccData.getType();
        ccExpiration = ccData.getExpirationDate();
    }
    //-- simple accessors
    public void setCcNumber(String ccNumber) {
        this.ccNumber = ccNumber;
    }
    
    public String getCcNumber() {
        return ccNumber;
    }

Immutable Value Objects

If you are using value objects (see the item above), you should ensure that they are immutable. Here’s why. If your value object is mutable (in other words, has mutator methods) and you pass a value object back from an entity, the client can call the setXXX() methods on the value object. However, the client is only changing their copy of the value object, not the values on the server. Because value objects are not remote objects, changes to the copy passed to the client will not be reflected on the server. So, to make sure this cannot happen, you should always make your value object immutable. If the user wants to make changes, they must create a brand new object instance and pass it back to the entity bean through one of it’s mutator methods.

Here is an example of changing the city of an address.

Listing 16: Changing the value of a dependent object

    Address a = ejbRef.getAddress();
    String newCity = "Atlanta";
    Addres newAddress = new Address(a.getAddr1(), a.getAddr2(), newCity,
            a.getState(), a.getZip());
    ejbRef.setAddress(newAddress);

Writing Proper Standard Methods for PrimaryKeys

This topic actually pertains to J2SE as well as J2EE. However, it is particularly important in the EJB context. Every primary key class must include both overridden equals() and hashCode() methods. However, creating these methods correctly is not a trivial manner.

equals()

To create a correct equals() method, you must adhere to the following conditions (taken from the J2SE specification):

  • It is reflexive: for any reference value x, x.equals(x) should return true.
  • It is symmetric: for any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the object is modified.
  • For any non-null reference value x, x.equals(null) should return false.

Let’s investigate the implications of the above requirements. First, the method must be reflexive.

Reflexive

This requirement says that the object must equal itself. If you somehow violated this rule, then when you add this object to an unordered Collection, it would allow you to add it again. You generally shouldn’t have to worry about this property.

Symmetric

This property implies that two objects must agree with each other as to whether they are equal or not. This is most common when you override class, including some overridden nontrivial behavior, and want to still include an equality check with the parent class.

Consider the following class, representing an unsigned integer.

Listing 17: Example of a class with a well intentioned but broken equals() method.

public final class UnsignedInteger {
    private Integer uInt;
    public UnsignedInteger(Integer i) {
        if (i == null) {
            throw new NullPointerException();
        }
    }
    //-- broken equals()
    public boolean equals(Object o) {
        if (o instanceof UnsignedInteger) {
             return ((UnsignedInteger)o).uInt.equals(uInt);
        }
        if (o instanceof Integer) {
            return uInt.intValue() == (Math.abs(((Integer) o).intValue()));
        }
        return false;
    }
}

The problem with this class is that it is trying to interact with the Integer class. This violates symmetry because the Integer class knows nothing about this class, and will obviously never indicate that it is equal with any instance of an UnsignedInteger. To fix this problem, we need only remove the reference to the Integer class from the equals() method. In other words, this class should only try to determine if equality exists between two instances of this class.

Transitive

The third requirement states that is object a is equal to b, and b is equal to c, then a must be equal to c. Here is an example of a violation of this principle. In the listing below, we have created a simple immutable point class.

Listing 18: A simple immutable point class.

public class Point2D {
    private int x;
    private int y;
    public Point2D(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public boolean equals(Object o) {
        if (!(o instanceof Point2D)) {
            return false;
        }
        Point2D p = (Point2D) o;
        return p.x == x && p.y == y;
    }

    public int getX() {
        return x;
    }
    public int getY() {
        return y;
    }
}

Now, we need to extend this class to account for 3D points.

Listing 19: A simple immutable 3D point class.

public class Point3D {
    private int z;
    public Point3D(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }
}

Is it possible to create a correct equals() method for this class? Consider this first pass.

Listing 20: First (incorrect) pass at an equals() method.

    //-- violates symmetry
    public boolean equals(Object o) {
        if (!(o instanceof Point3D)) {
            return false;
        }
        Point3D p3 = (Point3D) o;
        return super.equals(o) && p3.z == z;
    }

The problem is that you might get different results if you compare a Point2D with a Point3D — 2D ignores the z dimension and 3D fails because the class type is always incorrect. One attempt to fix this would have the Point3D try to ignore the third dimension when doing the comparison. Now, we violate transitivity. Consider this example:

Listing 21: Second (incorrect) pass at an equals() method.

    //-- violates transitivity
    public boolean equals(Object o) {
        if (!(o instanceof Point2D)) {
            return false;
        }
        //-- if o is a Point2D, do an ignore-z comparison
        if (!(o instanceof Point2D)) {
            return o.equals(this);
        }
        //-- o must be a 3D point
        Point3D p3 = (Point3D) o;
        return super.equals(o) && p3.z == z;
    }

This version fixes the symetry problem, but introduces a transitivity problem. So, if you have these 3 objects:

  
  Point3D p1 = new Point3D(2, 3, 4);
  Point2D p2 = new Point2D(2, 3);
  Point3D p3 = new Point3D(2, 3, 12);
 

In this case, p1.equals(p2) and p2.equals(p3) both return true. However, p1.equals(p3) returns false. This is an indicator that the equals method is not transitive.

Unfortunately, there is no simple solution to this problem. This is a general problem in all Object-oriented languages, not just Java. The easiest way to fix this is to use composition instead of inheritance. In fact, that is exactly what the JDK does in several cases. For example, the TimeStamp class is a Date class with an extra field.

Here is a version using composition that avoids the problems of symetry and transitivity of the previous examples.

Listing 22: 3D Point build with composition instead of inheritance

public class Point3DComp {
    private Point2D point;
    private int z;
    public Point3DComp(int x, int y, int z) {
        point = new Point2D(x, y);
        this.z = z;
    }
    public Point2D asPoint2D() {
        return point;
    }
    public boolean equals(Object o) {
        if (!(o instanceof Point3DComp)) {
            return false;
        }
        Point3DComp p = (Point3DComp) o;
        return p.point.equals(point) && p.z == z;
    }
}
Consistent

This requirement states that if two objects are equal, they must remain equal unless (and until) one of them is modified. This is really little more than a reminder that mutable object’s equally can change over time as the contents of the object changes. This, of course, doesn’t apply to immutable objects. Generally, you should consider making objects immutable whenever you can because it simplifies the equality requirements.

Non-null Reference value

This requirement says that any object must not be equal to null. This is the reason that all equals() methods should begin with an instanceof check to make sure that the passed object is the of the correct lineage. You do not have to explicitly check to see if the argument is null — the instanceof operator always returns false if the first argument is null.

Building the Perfect equals()

Given the above requirements, here is a heuristic for building the perfect equals() method every time.

  1. Use == to check to see if you have been passed a direct reference to yourself.
  2. Use the instanceof operator to check the lineage of the object.
  3. Cast the object to the correct type.
  4. Check the equality of each field of the class. If any test fails, return false. Note that this will turn around and call the equals() method of the non-primitive fields of you class, so this can be time consuming. This should be done last after all the other (faster) tests have been passed.
  5. Make sure that your equals is symmetric, transitive, and consistent.
hashCode()

Like the equals() method, there are also very specific rules as to how to create a correct hashCode() method. In fact, any time that you override Object.equals(), you should override Object.hashCode() as well. The rules laid out by the J2SE specification are as follows:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.

The second condition is the one of most importance because it dictates how many of the collection classes define equality. Here is an example. First, let’s look at an example of what happens if you neglect creating a hashCode() method. Consider the Point2D class from above, which doesn’t have a hashCode() method. If you tried to key a collection item from this class, it would fail.

Listing 23: Trying to key a collection item with an invalid hashCode

public class TestHash {
    public TestHash() {
         HashMap map = new HashMap();
         map.put(new Point2D(12, 12), "my point");
         System.out.println("Here is the point:");
         System.out.println(map.get(new Point2D(12, 12)));
    }
    public static void main(String[] args) {
        new TestHash();
    }
}

When you run this application, it prints "null" instead of "my point". Why? Because the default hashCode() method (from Object) doesn’t correctly calculate the hashCode. All the keyed collections in Java use the hashCode to place items in the appropriate slot. If two objects that should be identical don’t generate the same hashCode(), they cannot be retrieved from the collection.

Here is a version of Point2D that does correctly override hashCode().

Listing 24: A version of Point2D that correctly implements the hashCode() method

public class Point2DHash {
    private int x;
    private int y;
    public Point2DHash(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public boolean equals(Object o) {
        if (!(o instanceof Point2DHash)) {
            return false;
        }
        Point2DHash p = (Point2DHash) o;
        return p.x == x && p.y == y;
    }
    public int hashCode() {
        int result = 7;
        result = 3 * result + x;
        result = 3 * result + y;
        return result;
    }
    public int getX() {
        return x;
    }
    public int getY() {
        return y;
    }
}

Here is a reusable heuristic that allows you to correctly implement hashCode() for any class:

  1. Store a constant (prime) number in a variable (called result in the example above).
  2. For each field in the class that contributes to equals(), calculate a value for the field and add it to the result multiplied by a nontrivial prime number (3 in the above example)
  3. Return the result

Note that for arrays, you should iterate through the entire array to contribute to the result. You must resist the temptation of skipping fields to speed up the result because it will compromise your hashcode.

Antipatterns

Antipatterns are a response to the Design Patterns movement. If you can catalog a list of best practices and common design solutions, you can also catalog a list of worst practices and common but bad designs. Just like any architecture, J2EE won’t prevent you from doing something stupid, and in fact provides you with even more opportunities for stupid mistakes! To that end, this session presents a couple of very common worst practices that should be avoided like the plague.

"Uber Servlet" Antipattern

The first of the antipatterns is the "Uber Servlet" (also known as the "Fat Controller") pattern. This is an example of good intentions gone awry. The cause of this problem is the naive application of the Model-View-Controller design pattern discussed above. It is certainly a good thing to use the Model2 design pattern for web development (as embodied in Struts). However, if you try to use a single controller for everything, that single controller class becomes overly large, hard to debug, and starts growing all sorts of undesirable code to handle unusual circumstances. Even if you use a single controller for your MVC site, there is no rule that says that you can’t have any other servlets.

We had a client who was using this approach to manage every aspect of the application, using parameters passed to the controller to indicate what should happen. Because it was used for both normal processing and logon, the developers eventually created highly complicated logic that created a user session, immediately invalidated it, then re-created another one (there was some logic for doing this which escaped me then and now). Before they had finished, there were hundreds of lines of code (in a servlet that was already close to 5000 lines long) just to handle logging in. By changing logon to its own servlet, the entire application was simplified. The moral: don’t think that you can only have one master servlet. You can have as many as you need!

Notice that, even though Struts has a single controller, it’s controller does only one thing — map requests to actions, with its attendant form operations. If you use Struts, nothing says that you can’t create your own servlets to handle other jobs. In fact, it is probably a good thing that Struts handles this job for you and no others, because it encourages you to write other code to handle other stuff.

Finders that Return Large ResultSets

Another common misuse of the J2EE architecture has developers returning large result sets from entity bean finder methods. Especially if you just want a result set for display purposes, you should not use entity beans. When you create an entity bean, the application server must allocate resources for the beans. Thus, creating large numbers of beans generates a huge resource drain on the application server. Entity beans should only be used when you plan to update data, not display it.

If you need a large resultset to display some information, you should use a stateless session bean that connects directly to the database. These beans eat up far fewer resources than entity beans and will return the results much more quickly. In fact, if you need to update a large number of entities, you can write a stateless session bean that handles that directly with the database as well. Entities are designed for just that — small numbers of unique entities rather than large collections

Listing 25: Session bean that returns a list of Customer names

public class CustomerBean implements SessionBean {
    private SessionContext sessionContext;
    private java.util.List customerList;
    public void ejbCreate() {
    }
    public void ejbRemove() {
    }
    public void ejbActivate() {
    }
    public void ejbPassivate() {
    }
    public void setSessionContext(SessionContext sessionContext) {
        this.sessionContext = sessionContext;
    }
    public List getCustomerNameList() throws EJBException {
        Connection c = null;
        Statement s = null;
        ResultSet rs = null;
        try {
            c = getConnection();
            s = c.createStatement();
            RS = s.executeQuery("SELECT * FROM CUSTOMER");
            customerList = new Vector(20);
            while (rs.next())  {
                v.add(rs.getString("name"));
            }
        } catch (SQLException sqlx) {
            throw new EJBException(sqlx);
        }
        return customerList;
    }
    private Connection getConnection() throws SQLException {
        try {
            Context jndiContext = new InitalContext();
            DataSource ds = (DataSource) jndiContext.lookup(
                    "java:comp/env/jdbc/OrderEntryDS");
            return Ds;
        } catch (NamingException nx) {
            throw new EJBException(nx);
        }
    }
}

Summary

This session demonstrated a collection of J2EE best practices from a practical point of view. We looked at Web and EJB best practices and some J2EE Anti-patterns. If you would like to download the example from this presentation, go to http://www.thedswgroup.com/HTML/conferences.html.

For More Information


Some of the material presented here is adapted from my book Art of Java Web Development. It is guide to the topics required for state of the art web development. This book covers wide-ranging topics, including a variety of web development frameworks and best practices. Beginning with coverage of the history of the architecture of web applications, highlighting the uses of the standard web API to create applications with increasingly sophisticated architectures, developers are led through a discussion on the development of industry accepted best practices for architecture.

Described is the history and evolution towards this architecture and the reasons that it is superior to previous efforts. Also provided is an overview of the most popular web application frameworks, covering their architecture and use. Numerous frameworks exist, but trying to evaluate them is difficult because their documentation stresses their advantages but hides their deficiencies. Here, the same application is built in six different frameworks, providing a way to perform an informed comparison. Also provided is an evaluation of the pros and cons of each framework to assist in making a decision or evaluating a framework on your own. Finally, best practices are covered, including sophisticated user interface techniques, intelligent caching and resource management, performance tuning, debugging, testing, and Web services.