Practical J2EE Application Architecture - page 58


Summary

During domain modeling, we essentially discover classes from use cases. Most likely, the nouns and noun phrases provide an indication of entities that would be considered objects and attributes, and verbs and verb phrases will likely become operations and associations. The key abstractions from the problem domain must be identified at the outset, which forms the basis of the static model of the system. Business requirements are implemented on top of the domain model, therefore the domain model is a foundational artifact on which the business and presentation components are dependent. During domain modeling, we also identify the relationships between the domain objects. The type of relationship between entities and the multiplicity associated with roles on either side of the relationship will provide guidance for the creation of the database schema required to persist the corresponding entities. To arrive at an optimum design, we iterate and refine the model through the analysis phase. This optimization process can also continue through the design phase. To understand the object modeling process, we suggest that you read Use Case Driven Object Modeling with UML by Doug Rosenberg [Object Modeling], which further elaborates on this subject.



References

[Object Modeling] Use Case Driven Modeling with UML by Doug Rosenberg (Addison Wesley, 1999)



Chapter 7: Business Tier Design and Implementation

In Chapter 5, we looked at various design patterns applicable for the presentation tier along with implementation of the use case packages GreaterCause Site Administration, Manage Campaigns, and Search NPO. In this chapter, we discuss and implement several design patterns that are appropriate for the business tier. Emphasis in this chapter is on identification of appropriate design patterns in the context of our problem domain and applying these patterns for solving common problems during the design and development of the business tier. The patterns discussed in this chapter cover only those patterns that are relevant to realizing the above-mentioned use cases; for a comprehensive patterns list and related discussion, please refer to the references provided at the end of the chapter. A good understanding of this chapter will assist the readers in quickly assimilating other design patterns covered in the reference books and be able to discern their use in the context of different problem domains.

Noteá

It is assumed that the reader of this chapter has a basic understanding of EJBs and related technologies identified under J2EE framework. This chapter does not explain these technologies in great detail; instead it applies these technologies in the context of realizing the use cases identified in the preceding. We do discuss EJB usage and associated development and configuration semantics to provide the complete rationale behind our design decisions. For additional information on developing distributed systems using EJBs, please refer to tutorials available at java.sun.com.

Applying Design Patterns

In this section, we examine selected patterns that have been effectively used across most GreaterCause use cases. The Value List Handler pattern [Core] will be discussed in the section "Search NPO Use Case."

When designing distributed applications, appropriate partitioning of application logic across application tiers, coupled with efficient data transfer between tiers, is required to satisfy such concerns as scalability, performance, extensibility, and maintainability. A brief description of patterns follows; this is followed by a detailed discussion of the pattern usage in the context of the GreaterCause implementation.

  • Session Fašade This design pattern is used where there is a requirement to loosely couple the interactions between the client and the business logic residing on a server. This pattern minimizes the dependencies between the client tier and the business tier by providing a stable and simple interface to the business logic accessible by the client tier; it hides the complexities of the business processes within the methods of a session bean. This enables simpler client design and protects the client from the effects of business process changes in the business tier of the application.

  • Business Interface This design pattern is used to provide a compile-time checking of method signatures for remote/local interface implementation in the EJB bean classes.

  • Data Transfer Object Pattern This design pattern is used to transfer coarse-grained objects to and from the business tier and presentation tier, thus reducing overall network traffic and transferring more data in fewer remote call invocations.

  • EJB Home Factory Pattern This design pattern is used to encapsulate the vendor-specific details required for looking up home interfaces; it also provides caching of home references for reuse.

Implementing the Session Fašade Pattern

We first examine a scenario in which the presentation tier will try to implement the Register NPO Use Case by directly accessing the entity beans without the intervening business-tier objects, which are usually implemented using session beans. Register NPO Use Case is described in Chapter 1. The objective of this use case is to register an NPO, which includes creating the associated domain objects Admin and NPO (both are container-managed entity beans) for storing the administrator-related information and the NPO registration information, respectively. At the analysis level, the steps involved in implementing the Register NPO Use Case are depicted in the sequence diagram of Figure 7-1.

click to expand
Figure 7-1: Directly accessing domain objects from the presentation tier

Noteá

Please note that the sequence diagrams illustrated in this chapter are analysis-level diagrams. They are used to provide a high-level understanding of the underlying interaction semantics.

In this scenario, the presentation tier will make several calls over the network (assuming remote reference usage) to achieve the objective of registering the NPO in the data store. The number of calls for registering the NPO creates network chatter that does not effectively use the network bandwidth. Also, in this scenario the presentation tier has embedded logic for accessing and manipulating the domain objects, which will increase the complexity of the presentation tier logic. As discussed in Chapter 4, the presentation tier must follow the MVC semantics. This implies that it must not concern itself with manipulating the model; the logic for manipulating the model must be abstracted into a different tier, which we call the business tier.

A best practice approach to addressing these inefficiencies uses a session bean for implementing the model portion of the problem domain. The business semantics are expressed in a session bean that also encapsulates access to domain objects, thus effectively hiding the complexity of accessing and manipulating domain objects implemented as entity beans. This implementation of the session bean is referred to as a Session Fašade [Core]. When a session fašade pattern is used, the presentation tier effectively makes a single network call to a method on the session bean with relevant arguments, which could potentially be a data transfer object (this is further explained in the section "Implementing the Data Transfer Object Pattern"); the fašade method in turn deals with the complexities of business processes and data manipulation. The session fašade isolates the presentation tier from the implementation aspects of the domain tier and the related business processes, thus providing a loosely coupled interaction semantics. Should the business tier logic change, or there is a change in the domain model, the presentation tier will usually remain unaffected. Figure 7-2 depicts the interaction between the presentation tier and the session fašade.

click to expand
Figure 7-2: Accessing business logic using Session Fašade

The sequence diagram depicts that fewer calls are made by the presentation tier over the network instead of several calls as compared to Figure 7-1. In this scenario, the session fašade is implemented by the SiteAdmin session bean.

From the preceding discussion, you will observe that the presentation tier is now limited in its responsibility while delegating most of the application logic to the session fašade. By applying the session fašade pattern, we have moved the business logic from the presentation tier to the business tier and introduced the MVC semantics for isolating the presentation tier from the intricacies of the domain model and the logic that manipulates the model. The session fašade pattern is also extensively used when there is need to prevent the client from making fine-grained method calls to domain objects. Roughly speaking, the pattern could be used to wrap all the method calls required to get relevant data from entity beans in a single network call to the business tier. This pattern is usually used in conjunction with the Data Transfer Object pattern. The session bean implementing the session fašade does not have to restrict itself to accessing domain objects; a session fašade could in fact interact with other session beans for servicing the client request. This architecture is useful for solving complex business problems as well as promoting reuse and modularity.

start sidebar
Optimization Note

For the reasons of scalability, you may want to consider using stateless session beans instead of stateful session beans. A stateful session bean requires that you maintain the conversational state for a client across method invocations; therefore a stateful bean cannot be assigned to another client. A stateless session bean does not maintain the conversational state and hence any free instance of the session bean from the bean pool may service any client. This provides better opportunities for the EJB container to scale the number of available instances of session beans for servicing the clients. However, using a stateless session bean may not be practical in certain situations; for example, the implementation of Search NPO use case (discussed in section "Search NPO Use Case") employed a stateful session bean to support the paging mechanism required by the presentation tier.

For small-to medium-sized applications, it may be tempting to implement all the use cases using a single session fašade bean. This is not a recommended approach because this leads to unnecessary concentration of unrelated services into a single session bean. Instead, break up the business logic into manageable chunks based on application functionality and implement the use cases across multiple session beans as described in the following sections. This approach makes the application scalable, manageable, and modular. However, the architects must endeavor to keep the number of session fašade beans to a manageable number.

end sidebar

Implementing the Business Interface Pattern

A session bean class or an entity bean class must implement all the methods defined on the remote interface. However, according to the EJB specification, the bean class is only required to implement the javax.ejb.SessionBean interface; therefore, at compile time, there is no checking to ensure that the methods of remote interface have been implemented by the bean class. It is only during the post-compilation process that the proprietary compliance checkers provided by EJB vendors will check that the bean class methods conform to the remote interface definition. The post-compilation checkers usually are very slow and are outside of the regular development environment.

An elegant solution for this problem is to define a special interface, called Business Interface [EJB Patterns], which the remote interface extends; this business interface is implemented by the bean class. Observe that by extending the remote interface with the business interface, the remote interface does not have to specify any business methods. As illustrated in Figure 7-3, SiteAdminRemote extends the required EJBObject, and it also extends the SiteAdmin business interface. The SiteAdminBean class implements the required javax.ejb.SessionBean interface (in the case of entity beans it is the javax.ejb.EntityBean interface) and it also implements the SiteAdmin business interface. During compilation, the SiteAdminBean class must have the implementation for methods defined by the SiteAdmin business interface, otherwise the compiler will flag this as an error; this makes it possible to detect any mismatch between the method signatures on the business interface and the bean class. Using this solution, the EJB client can conveniently use the business interface instead of remote or local interface to interact with a session bean as shown by the SiteAdmin business interface in Figure 7-3.

click to expand
Figure 7-3: Using the Business Interface for accessing an EJB

The business interface differs slightly if it has to expose a remote interface or a local interface to the client. The EJB 2.0 specification specifies that all methods in a remote interface should throw a RemoteException, whereas the methods in a local interface must not throw a RemoteException. So if the remote interface extends a business interface, each method in the business interface must throw a RemoteException; as a result, this business interface cannot be used to extend the local interface.

This pattern provides a powerful mechanism for compile-time checking of method signatures defined in the remote/local interfaces that are being implemented by the EJB bean class. This pattern is sometimes called a double-interface pattern.

Implementing the Data Transfer Object Pattern

In a typical distributed application like GreaterCause, the presentation tier needs to interact with the business tier for getting information pertaining to the view being processed. For example, the Register NPO use case requires the view to show the registration information to the administrator; some of this information includes EIN, NPO Name, Address, and so on. One solution for requesting this data from the business tier is to have a session fašade expose the methods getEin(), getNpoName(), getAddress(), and so on. The presentation tier will then call the appropriate method on the session bean to get the information for display purpose. This form of access is commonly referred to as fine-grained access in that the information required by the presentation tier is obtained incrementally using several calls to the business tier. This interaction is captured in Figure 7-4.

click to expand
Figure 7-4: Fine-grained access of business functionality

It is obvious from the sequence diagram in Figure 7-4 that there is a lot of traffic between the presentation tier and business tier. Each remote method call on the session bean is going across the wire, which in turn results in marshalling and unmarshalling of the objects EIN, NPO Name, Address, and so on. This level of object granularity is expensive when communicating over the network.

An elegant solution will be to make a single call to the session bean, which returns a serialized object that aggregates the fields required by the view, as shown in Figure 7-5. In this scenario, the presentation tier requests the session bean for registration data, and the session bean in turn makes all the necessary calls to the domain objects for assembling a serializable object; this object is called a Data Transfer Object (DTO) [EJB Patterns]. Further discussion on DTO is available at http://c2.com/cgi-bin/wiki? UseDataTransferObjects. This serializable object is used for exchanging data between the presentation and the business tiers. The use of DTO minimizes the traffic between the presentation tier and the business tier (EJB tier) in a distributed environment, and it reduces the complexity of the logic in the presentation tier. The DTO can be used to assemble data from several views (as in multipage interaction) when transporting data from the presentation tier to the business tier, or the DTO can be used to assemble data to be shown across several views when transporting data from the business tier to the presentation tier. Under certain circumstance one may require exchanging more than one DTO between different tiers; in such cases one can use a collection of DTO objects.

click to expand
Figure 7-5: Coarse-grained access using a DTO

The data transfer objects are usually simple serializable JavaBean classes, as shown in the following code segment:

package com.gc.services.admin;
import java.io.Serializable;

public class NPORegistrationDTO implements Serializable{
    private String ein = null;
    private String npoName = null;
    private String adminID = null;
    private String address = null;
    private String city = null;
    private String state = null;
    private String zip = null;
    private String country = null;
    private String activationStatus = null;
    ... property accessors appear here ...
}
 

Depending on your implementation need, the DTOs can be mutable or immutable. Immutable DTOs are employed when the presentation tier should not update data values of the object, and therefore the DTO can only be used for display purposes. However, if the presentation tier requires the data to be updated, then mutable DTOs are employed, which allows the instance variables of the DTO to be changed. The changed DTO is sent back to the business layer for applying the changes to domain objects.

When the business tier receives an updated DTO, it needs a mechanism to identify the instance variables that have been changed. In the absence of this mechanism, the business layer may have to blindly update the domain objects with values from DTO, potentially updating unchanged attributes; avoiding such updates optimizes the database access performed by container-managed persistence EJB (CMP). A simple solution to recognize the changed value for an instance variable is to set a flag corresponding to the variable, as shown in the following code snippet:

package com.gc.services.admin;
import java.io.Serializable;

public class NPORegistrationDTO implements Serializable{
    private String _ein = null;
    private String _npoName = null;
    ... rest of the code ...
    /* Following members provide the index for the flags[] array */
    public static final int EIN = 0;
    public static final int NPO_NAME = 1;
    ... rest of the code ...
    private boolean[] flags = new boolean[9];
    public NPORegistrationDTO(){
        this.resetModifiers();
    }
    ... accessors are listed here ...
    public void setEin(String ein) {
        _ein = ein;
        flags[EIN] = true;
    }
    public void setNpoName(String npoName) {
        _npoName = npoName;
        flags[NPO_NAME] = true;
    }
    public boolean isFieldModified(int fieldIndex){
        /* Returns true if the corresponding setter method was called */
        return flags[fieldIndex] == true;
    }
    public void resetModifiers(){
        for (int index = 0; index <= flags.length-1; index++){
            flags[index] = false; }
        }
    }
}
 

In the business tier, the session bean simply needs to call the isFieldModified method to determine if the field has been updated by the presentation tier; this is illustrated in the following code segment. Please note that the variable npo corresponds to a domain object. Domain objects are discussed in Chapter 6.

public void updateNPORegistration(NPORegistrationDTO details){
    if (details.isFieldModified(NPORegistrationDTO.NPO_NAME) ) {
        npo.setNpoName(details.getNpoName());
    }
    ... check other attributes for modification ...
}
 

Proliferation of Data Transfer Objects

For a small-sized application like Greater Cause, only a handful of data transfer objects were needed to satisfy the requirements of the presentation tier. For large applications, the requirements of the presentation tier may require a large number of data transfer objects. To keep the number of DTOs manageable, one solution would be to create DTOs that have several common data elements; the flip side of this approach is that the data transfer object will contain more data than required for satisfying a presentation tier request; populating additional data also implies making unnecessary calls to the domain layer. Optionally, a data transfer object based on HashMapmay be appropriate in situations where an arbitrary amount of data needs to be transferred across tiers in a generic manner.

When Not to Use Data Transfer Objects

From the outset, the DTO pattern may be used between different tiers of the application—between the presentation and business tiers, and between the business and domain tiers. However, it is considered a bad practice to apply a data transfer object pattern for interactions with the domain tier.

Back in the days of EJB 1.x–based implementations, the DTO pattern surfaced due to the mandatory requirement that the calls to entity beans be remote even when the session or entity beans accessing them were co-located. To reduce the network overhead of these remote calls, the data transfer object pattern was applied in a fashion similar to the discussion in this section. Under this circumstance, if the same DTOs were used by both the presentation tier and the domain tier, then if the domain tier changed, the associated DTO changed and therefore the presentation tier was required to change, and vice versa. This tight coupling between the view and the domain objects will result in unnecessary dependency between tiers, which also undermines MVC semantics. Note that with the introduction of local interfaces in EJB 2.0, the method calls to entity beans are no longer required to be remote.

Implementing EJB Home Factory Pattern

The EJB specification standardizes the access mechanisms for locating the EJBs. A client locates a session or entity bean's home interface using JNDI. For example, the home interface for the SiteAdmin session bean (from the Register NPO Use Case) can be located using the following segment of code:

//Vendor specific code
Hashtable props = new Hashtable();
props.put(InitialContext.INITIAL_CONTEXT_FACTORY,
    "weblogic.jndi.WLInitialContextFactory");
props.put(InitialContext.PROVIDER_URL,
    "t3://localhost:7001");
InitialContext ctx = new InitialContext(props);
SiteAdminHome siteAdminHome = (SiteAdminHome)
    javax.rmi.PortalRemoteObject.narrow(
    ctx.lookup("ejb/com.gc.services.admin.SiteAdminHome"),SiteAdmin.class);
 

This code first packages the necessary vendor-specific values into a Hashtable and calls PortalRemoteObject.narrow with the corresponding JNDI name that was declared in the deployment descriptors. The preceding snippet has the following disadvantages:

  • Each client accessing the EJB is providing vendor-specific code, and therefore the code is repeated in multiple places where access to EJBs is required.

  • Getting the initial context and subsequently the home interface is a resource-intensive process, which will impact performance.

To overcome these limitations, the EJB Home Factory pattern [EJB Patterns] should be introduced as follows:

  • Develop a helper class that hides all the vendor-specific details. In the sample application, this helper class is called EJBHomeFactory.

  • Using a combination of Factory and Singleton [Gof] patterns, create a single instance of EJBHomeFactory that creates the InitialContext only once, and provides a suitable caching mechanism for home references.

The following code fragment shows the implementation for EJBHomeFactory:

public class EJBHomeFactory {
    private HashMap _ejbHomes;
    /* Singleton pattern */
    private static EJBHomeFactory _factory = new EJBHomeFactory();
    private static InitialContext _ctx = null;
    private EJBHomeFactory(){
        _ejbHomes = new HashMap();
    }
    public static EJBHomeFactory getFactory(){
        return _factory;
    }
    public static InitialContext getContext() throws NamingException{
        /* Check if initial context already exists */
        if (_ctx == null){
            /* Vendor specific parameters */
            Hashtable props = new Hashtable();
            props.put(
                InitialContext.INITIAL_CONTEXT_FACTORY,
                "weblogic.jndi.WLInitialContextFactory");
            props.put(InitialContext.PROVIDER_URL, "t3://127.0.0.1:7001");
            _ctx = new InitialContext(props);
        }
        return _ctx;
    }
    public EJBHome lookUpHome(Class homeClass) throws NamingException{
        EJBHome home = null;
        /* Check whether the reference for the EJB already exists in the
         * cache _ejbHomes */
        if ((home = (EJBHome)_ejbHomes.get(homeClass)) == null){
            home = (EJBHome)PortableRemoteObject.narrow(
                getContext().lookup("ejb/"+homeClass.getName()),homeClass);
            // Cache the reference for future use
            _ejbHomes.put(homeClass,home);
        }
        return home;
    }
}
 

The EJBHomeFactory has the getFactory method, which returns an instance of EJBHomeFactory. The convenience method lookUpHome creates the InitialContext only once using vendor-specific details in the getContext method. The InitialContext is used in the PortableRemoteObject.narrow method for looking up the home reference for the given JNDI name.

The following snippet illustrates usage of the EJB Home Factory class for accessing the home reference of the desired EJB. Note that the developers do not have to concern themselves with vendor-specific details. The home factory pattern provides a mechanism for caching home references while hiding vendor-specific details.

SiteAdminHome adminHome = (SiteAdminHome)EJBHomeFactory.getFactory().
            lookUpHome(SiteAdminHome.class);
 

The JNDI name for the home interface is described in the weblogic-ejb-jar.xml file, as shown here:

<weblogic-enterprise-bean>
   <ejb-name>SiteAdminEJB</ejb-name>
   <jndi-name>ejb/com.gc.services.admin.SiteAdminHome</jndi-name>
</weblogic-enterprise-bean>