Ask a question
Tuesday, 15 September 2009 11:48

J2EE Design Pattern - Business Tier Interfaces

Written by Vicky
Rate this item
(0 votes)
Business Tier Interfaces
Business tier interfaces connect the domain model with the client (in thick-client applications) or the server-side presentation layer (in web applications). They also implement some of the business logic for an application, controlling and limiting the actions that the presentation tier can perform on the domain model.
This chapter focuses on patterns for packaging and presenting the business logic of an application. The Business Delegate pattern and the Session Façade pattern address the organization of process logic and the division between the domain logic and the process logic. As an added bonus, both of these patterns provide performance enhancements in addition to their organizational benefits. We'll also discuss the Business Delegate Factory pattern, which allows us to build more effective non-EJB applications by creating our own versions of some of the services provided by a full application server.
We also cover two patterns for connecting to remote business services. The Service Adapter pattern, which is a variation on the original Gang of Four Adapter pattern, can be used to provide a useful Java-based interface to non-EJB business tier resources. The Service Locator pattern provides centralized management of connections to Enterprise JavaBeans and other remote resources.

9.1 Abstracting Business Logic

The last few chapters explored ways to keep the presentation tier and the business tier separate. We accomplished this by wrapping the domain logic for the system in both regular and Enterprise JavaBeans and introducing various controller patterns, including the MVC and Service to Worker patterns, in order to mediate the interaction between the tiers. This decoupling makes the overall system easier to understand and maintain. But without a good way to communicate between the tiers, the advantages can quickly be lost. The Business Delegate and Business Delegate Factory patterns provide client-side objects that flexibly connect the presentation and business tiers.

9.1.1 Business Delegate

In an MVC design, the controller still has to interact directly with the business tier (the model). The view will do so as well, although in a read-only fashion and mediated through the controller. This exposes the underlying implementation details of the business tier to the presentation tier. The application needs to know enough about the business tier to perform the operations requested by the client, and enough about the networking infrastructure between the presentation tier and the business tier to locate the business objects in the first place. The result is a tightly coupled application.

The Business Delegate pattern defines a client-side object that handles communications with the business objects. It fulfills the model role in an MVC framework. The business delegate doesn't necessarily contain any business logic itself, but encapsulates knowledge about how to locate, connect to, and interact with the business objects that make up the application. These objects might be in the same JVM, or they might be remote Enterprise JavaBeans, CORBA components, web services, or other resources. The business delegate is also responsible for returning enough data to the controller to allow the controller to instantiate an appropriate view.

The business delegate wraps access to a series of business objects and business services into a single, easy-to-implement application call. It can also incorporate all of the networking code required to access those resources, including any retries, caching, or exception handling that might be required. Networking or database access exceptions can be converted into application exceptions for consistent and user-friendly handling.
 
The UML diagram in Figure 9-1 shows a basic set of object relationships for an application using business delegates. The client interacts with the business delegate object, which in turn interacts with a set of business services. Some of these services are instantiated directly, and others are retrieved via a service locator class. The service locator provides a directory interface to locate the various business services used by the business delegate. In EJB environments, this is often a JNDI server. We cover service locators later in this chapter.
 
Figure 9-1. Business delegate

The major difference between our definition of Business Delegate and others is that we don't consider them to be pure EJB front ends. Rather, we consider them the client-side equivalent of an EJB session bean: an object that contains a front end to the process logic underlying an application. It doesn't matter whether the business delegate object implements that logic itself, parcels it out to other components, or does a little of both. The presentation tier delegates business process responsibilities to the business delegate, and that's more or less the end of that.

Our version varies from the version presented in Sun's J2EE Patterns collection.[1] For one thing, we make the Service Locator component optional. While Service Locators are often a good idea, we don't consider them vital to a business delegate implementation.
9.1.1.1 Organizing business logic into business delegates

If none of the external resources available to the business delegate are capable of incorporating all of the business logic, then the business delegate's own implementation should provide the missing elements. For example, consider a use case that involves retrieving stock information from a web service, computing a shipping charge, and submitting stock updates and shipping information to two additional web services. Because business delegates can be shared, we don't consider it bad practice to include the code for computing shipping charges in the business delegate implementation. Of course, it does make sense to consider ways to move that functionality into other areas that can be reused more easily, but that's almost always true. Incorporating the code directly into the presentation tier (having a servlet compute the charges before submitting the order), on the other hand, would easily prove problematic.

The quantity of business logic included in the business delegate depends on the application's persistence model. If the business tier consists exclusively of EJB entity beans that represent the data model, the business delegate will, of necessity, have to incorporate a substantial amount of domain knowledge. It must know which entity beans to create, remove, or update to perform a particular activity. A delegate for a legacy interface, on the other hand, may simply relay calls to an existing API that handles object management. In both cases, we consider the business delegate to be part of the business logic tier, even if the object itself is instantiated at the client tier.

We'll talk more about the division of business logic between business delegates and EJBs later in this chapter, when we discuss the Session Façade pattern.
9.1.1.2 Business delegate implementation

In an iterative or distributed environment, the business delegate can be stubbed out first, allowing development to proceed on the presentation tier while the ultimate business logic implementation is worked out. For applications that include complex relationships with external systems, this approach substantially accelerates development.

Example 9-1
shows a very simple business delegate. It performs a data validity check and then uses a DAO object to update the domain objects. In a real application, the business delegate doesn't need to be much more complex. It should be easy to see how we would convert this class to an EJB session bean.
Example 9-1. A business delegate
public class PatientManagementDelegate {
 
 public static PatientDTO createNewPatient(PatientDTO patient) 
    throws InvalidPatientException{
 
    if(patient == null|| patient.getFirstName( ) == null 
        || patient.getLastName( ) == null) 
          throw new InvalidPatientException(
            "Patient Records Require Full Name");
    
    PatientDAO pDAO = PatientDAOFactory.getPatientDAO( );
    PatientDTO newPatientRecord = pDAO.createPatient(patient);
 
    return newPatientRecord;
 }
  
}
The implementation of the business delegate can include other patterns as well, further hiding the business logic's complexity from the client. For example, a business delegate can use the Gang of Four Strategy pattern to retrieve data from different sources depending on runtime requirements or changing infrastructure (we touched on the Strategy pattern in Chapter 2).

In an EJB environment, business delegates usually work as a front end to a session bean (specifically, to a session façade, which we'll discuss later in this chapter). Implementing the presentation tier-to-EJB tier connection this way insulates the presentation tier programmers from the EJB environment, and simplifies error handling, since the various EJB exceptions can be caught, handled, and either resolved within the delegate or reported to the presentation layer in a consistent, simplified manner. A business delegate can throw exceptions related to the business process rather than exceptions related to the EJB environment.
9.1.1.3 Nesting business delegates

Since a business delegate is an access point to business logic and the domain model, there's no reason it can't use the resources of other business delegates. Business delegates can be nested arbitrarily deep, particularly when you're not in an EJB environment. This ability allows you to create separate business delegates for each use case and combine them as needed to implement the master use cases for your application.

For example, Figure 9-2 shows how a servlet, accessing a business delegate object to submit an order to a purchasing system, might actually invoke three different delegates, even though the servlet only sees one of them.
Figure 9-2. Nested business delegates
 
Nesting business delegates means that we have to keep track of the resources each delegate uses. For example, if each call to a business delegate retrieves a database connection and doesn't return it until all processing is completed (whether by instantiating DAO objects or retrieving the connection directly), a call stack containing five business delegate instances might hold onto five separate database connections. This means that a pool of 25 database connections can only support 5 concurrent users.

Using nested business delegates in an EJB application could result in inappropriate connections between different EJBs. If a session bean uses a business delegate to access another session bean, the business delegate may have to make a remote connection to the second bean, even if it's located in the same EJB server. If the session bean connected directly, it could easily use local interfaces instead. We'll address this issue later in this chapter with the Session Façade pattern.

The other major issue raised by nested business delegates is transaction control. A presentation tier programmer will, absent indications to the contrary, expect that each call to a business delegate method takes part in one transaction. If each business delegate creates its own transaction, the nested delegates will handle their transactions separately from the parent delegate. At best, this process results in incomplete rollbacks when the parent delegate fails; at worst, it will result in database resource locks and the application slowing or grinding to a halt. Since this isn't a simple issue, we'll look at transaction issues in enterprise applications in much more detail in the next chapter, including a pattern, Transactional Context, which addresses the problem specifically.
9.1.1.4 Stateful business delegates

Business delegates can be stateful or stateless. To use a stateful business delegate, generate an instance of the business delegate class. To create a stateless business delegate, declare the business delegate methods as static, and call them accordingly. You can also make the business delegate class a singleton. Either way, all classes running in the same Java VM and class loader will share a stateless business delegate. For web applications, this means that all the code will share the delegate.

The tradeoffs between stateful and stateless business delegates are almost identical to the tradeoffs between stateful and stateless session beans in EJB. Stateful delegates require more resources and can be marginally slower, due to the overhead of creating the additional objects. On the other hand, they allow you to configure a particular instance to your needs, making the delegates themselves easier to reuse. Stateless delegates require less memory and present a simpler interface to the programmer.

Here's a rule of thumb: if a business delegate requires a resource that is both expensive to create and that must be created specifically for the current user (such as a JAAS security principal), and if that resource will be used multiple times within the same delegate by the same thread, use a stateful delegate. You should also use a stateful delegate if the delegate needs complex configuration specifically for the current invocation, rather than trying to pass in all configuration information with each method call. Otherwise, look for opportunities to use a stateless delegate.

9.1.2 Business Delegate Factory Pattern

Since the simplest business delegates are just objects with static methods, we don't need to worry about creating or configuring them. But once we start using stateful business delegates, things get more complicated—even with stateless delegates we can eventually accumulate so many of them that it becomes hard to keep track. Architectures using business delegates face the following problems:
 
·         No standard for instantiating business delegates, making it difficult to exchange one delegate for another
·         Instantiation code, which may include configuring the delegate to access runtime-specific resources, is spread across the application
·         No centralized directory of available business logic

We can address these points with the Business Delegate Factory pattern. The Business Delegate Factory pattern is specialization of the Gang of Four Factory pattern. A business delegate factory is a single object responsible for creating instances or one or more kinds of business delegates.
 
Depending on your application, the factory can produce a particular kind of business delegate (resulting in one factory per delegate class), or a set of delegates related by their configuration requirements or general functionality. Having a broad-spectrum factory makes it easier to find the various business delegates within an application, but also introduces additional compilation dependencies.
 
Like business delegates themselves, business delegate factories can be stateful or stateless. The same rules apply: if the factory needs to be configured for the current thread/user/block of code, or if creating a new delegate instance requires an expensive resource that cannot be shared between all users of the application, consider a stateful factory. Otherwise, consider a stateless factory.
 
Figure 9-3 shows the sequence diagram for a stateful factory creating a stateful business delegate. It's a simple process: the client application creates a new instance of the factory by calling a static newInstance( ) method. It then calls a setAttribute(...) method to configure the factory, and finally requests an instance of a particular business delegate type. The factory creates the delegate instance and returns it to the client. At the end of their useful lives, both the factory and delegate objects go out of scope and are garbage-collected.
 
Figure 9-3. Stateful business delegate factory
A stateless business delegate factory would look similar but could include all of the code executed in the newInstance( ) and setAttribute( ) methods in static initialization blocks or in the getBusinessDelegate( ) method, which would now be a static method on the delegate factory class. If we implement the delegate as a singleton, the initialization can still take place at startup.
9.1.2.1 Modifying business delegate behavior

The Business Delegate Factory pattern lets you add functionality to your business delegates at runtime. As a simple example, consider logging. An application might need to support logging calls to business delegate methods but require different levels of detail, or different outputs, depending on the current status of the application. The simplest way of implementing this functionality (and the road most often taken) is to implement the logging functions in each business delegate object, perhaps tagging them with a "Loggable" interface, and toggling logging on or off via a centralized control class or at creation-time within the factory.

Other solutions take better advantage of the object-oriented nature of Java and create wrapper classes for each business delegate. The factory can wrap each delegate in the appropriate logging wrapper when logging is required, and skip the step when it isn't. The problem is that we need to maintain a separate logging wrapper for each business delegate class, and update them at the same time we update the delegate. It's a lot of work, and a lot of class profusion.

Obviously, we're leading up to something! The Java reflection API (which we've already seen in Chapter 8) includes a class called Proxy, which allows you to dynamically create an object that implements a given set of interfaces. An InvocationHandler object handles calls to methods on those interfaces, and the entire assemblage is referred to as a proxy object. Most uses of this class involve taking an existing object, extracting its interfaces, and creating a new object that implements those same interfaces while providing some additional functionality. The new object can delegate method calls to the original object or handle method invocations itself. Figure 9-4 shows how this can work in practice.
The custom InvocationHandler includes a reference to the wrapped object. When the application calls methods on the proxy object, the invocation handler forwards the method on to the original object and returns the results to the application.
 
Figure 9-4. Wrapping an object with an InvocationHandler

Example 9-2
shows how we can apply this technique to the logging problem. The DelegateLoggingWrapper class includes a single method, decorate( ), and an inner class defining a LoggingWrapperHandler, which implements the InvocationHandler interface. The invoke( ) method prints out the name of the method and the arguments passed into it, invokes the method on the original object, prints another message confirming success, and returns the result of the method call. In the next chapter, we'll use this approach to handle transaction management within and across business delegates.
Example 9-2. DelegateLoggingWrapper.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
final class DelegateLoggingWrapper {
    /**
     * Decorates a business delegate object with a Logging Wrapper. 
     * The object returned by this method will implement all of the 
     * interfaces originally implemented by the target
     * and loaded by the same class loader as that of the target.
     * @param delegate The Business Delegate to wrap
     * @return The business delegate wrapped in this wrapper
     */
    static Object decorate(Object delegate) {
        return Proxy.newProxyInstance(
            delegate.getClass().getClassLoader( ),
            delegate.getClass().getInterfaces( ),
            new LoggingWrapperHandler(delegate));
    }
 
    static final class LoggingWrapperHandler 
                       implements InvocationHandler {
        private final Object delegate;
 
        LoggingWrapperHandler(Object delegate) {
            this.delegate = delegate;
        }
 
        /** Invoke the target method, but display logging 
            information first. */
        public Object invoke(Object proxy, Method method, 
                             Object[] args)
            throws Throwable {
            System.out.println("Invoked Business Delegate Method: " +
                method.getName( ));
 
            if (args != null) {
                for (int i = 0; i < args.length; i++) {
                    System.out.print(args[i]); 
                    System.out.print( 
                      (i < (args.length - 1)) ? "," : "\n");
                }
            }
 
            Object result = method.invoke(delegate, args);
            System.out.println("Completed Without Exception");
 
            return result;
        }
    }
}
Example 9-3 shows how to use invocation proxies in the business delegate factory itself. We've created a static factory object that produces runtime instances of each delegate object (to save space, we've included only one delegate type). The user can call static methods on the class in order to start or stop logging. If logging is enabled, the DelegateLoggingWrapper is used to add logging capability. Note that we've shifted the logging/no-logging decision all the way down to the wrap( ) method, rather than placing it in the delegate creation methods. While this adjustment is marginally slower, it reduces the amount of code we need to write in each delegate creation method, and makes it easier to add support for additional wrappers later.
Example 9-3. BusinessDelegateFactory with logging
public class BusinessDelegateFactory {
 
 private static boolean useLogging = false;
 
 public static PatientManagementDelegate 
                 getPatientManagementDelegate( ) {
    PatientManagementDelegate delegate = 
        new PatientManagementDelegateImpl( );
    return (PatientManagementDelegate)wrap(delegate);   
  }
 
 private static Object wrap(Object o) {
    if(useLogging)
      return DelegateLoggingWrapper.decorate(o);
    return o;
 }
  
  public static void startLogging( ) {
    useLogging = true;
 }
 
 public static void stopLogging( ) {
    useLogging = false;
 }
 
 public static boolean getLogStatus( ) {
    return useLogging;
 }
}
You probably noticed that the proxy object requires interfaces, rather than concrete classes. Rather than maintain a single class for each business delegate we want to wrap, we now need to maintain an implementation class and an interface. This is simple enough to do (and still less work than maintaining separate wrapper classes for each delegate, since it's easy to update the interface at the same time a delegate changes: no additional code needs to be written). We assume you already know how to create an interface, but for completeness, Example 9-4 shows the interface for the patient management delegate.
Example 9-4. PatientManagementDelegate.java (interface)
public interface PatientManagementDelegate 
{
 public PatientDTO createNewPatient(PatientDTO patient) 
    throws InvalidPatientException;
}
Example 9-4 expects a PatientManagementDelegateImpl object that implements this interface. The code for this class is exactly the same as in Example 9-1, but renamed and with an implements clause.
This approach to modifying behavior on the fly does have performance implications. Each request for a business delegate now carries the overhead of creating the proxy object, which increases the time required to create a new business delegate instance. Each method call also incurs additional overhead, both for the reflection activity itself and whatever activities the wrapper performs. In absolute terms, the difference due to reflection is measured in microseconds, and we don't consider it significant, particularly given that the business delegates and the wrapper objects are likely to be participating in much more resource intensive activities.
Since the Factory pattern itself is fundamental to good object-oriented programming (out of everything in the original Gang of Four book, it may well be the pattern most frequently encountered),[2] it's worth keeping the proxy approach in mind when implementing other areas of your system.

9.2 Accessing Remote Services

Business delegates are great for providing local code with access to business resources, whether the resources themselves are local or not. For local resources the data lives on the same server—maybe even in the same JVM—as the business delegate. In these situations, connecting to the data is not a huge concern. J2EE, however, is designed to handle much more complicated scenarios. An enterprise J2EE deployment typically involves resources that are distributed between multiple servers. To fulfill requests, the servers hosting the presentation tier may need to communicate with application servers, messaging middleware servers, database servers, and legacy IS systems. Finding all these servers and maintaining the various connections can quickly complicate otherwise simple business delegates.

Since the work of connecting is not central to the business delegate, it is often beneficial to build a separate object that the delegates can use to access remote services. The Service Adapter and Service Locator patterns describe ways to do this, the former for non-J2EE business services and the latter primarily for EJBs. Figure 9-5 shows how the service locator and service adapter act as intermediaries between the business delegates and business services.
 
Figure 9-5. Using service adapters and locators

9.2.1 The Service Adapter Pattern

A business delegate might make use of several services to fulfill a particular use case. In order to keep the business logic code understandable and maintainable, we want to use the simplest interfaces possible. A service adapter encapsulates interactions with a remote service, hiding details of the implementation when they aren't central to fulfilling the business need.

The Service Adapter pattern is a variation on the Gang of Four Adapter pattern. The Adapter pattern wraps a class in a superclass that implements a standard interface. The Service Adapter pattern goes one step further by wrapping a remote service in a standard Java object that can be used without knowledge of the underlying access mechanism. The Java wrapper is responsible for:
 
·         Locating the Remote Service.
·         Translating Java method calls into a format that the remote service understands.
·         Accessing remote functionality.
·         Translating results into a pure Java representation.

The Service Adapter pattern is overkill for EJBs, which already map business methods to Java methods. The translation activities are not required, and the directory services abstracted by a service adapter can be better addressed in the EJB context with the Service Locator pattern, discussed later in this chapter.

However, if the native interface to the external service is not Java, a service adapter can bridge the gap. A good example is an application that needs to access a SOAP-based web service. The service might be delivered by SOAP over HTTP, SOAP over SMTP, or via specialized, routed protocols like ebXML. The service adapter hides the complexities of creating a SOAP or ebXML message, allowing the developer to substitute different transport mechanisms as the needs of the application evolve. A SOAP-based service adapter in a prototype system can be replaced with a JMS-based adapter before going to production. Figure 9-6 shows a service adapter for a SOAP web service.
 
Figure 9-6. Service adapter for a SOAP web service

In J2EE 1.4 or earlier versions with the appropriate add-on modules, the JAX-RPC API and support tools can be used to build service adapters for web services. This largely eliminates the need to hand-code service adapters for web services.
The most recent Java IDEs support building service adapters directly from WSDL definition files. Some tools do this in a more platform-independent way than others, so it's worth watching for portability issues when using such tools.

However, most of the generated code is either already reusable or can be easily modified to be so. Let's look at how this might work with a simple web service. Sticking with our health care theme, imagine a web service that returns a patient's medical records. Since complex data is more useful than simple data (and because we don't want to have to execute a few dozen web service requests for each use case), we've written the service to return a number of pieces of data with each request, in the form of a complex XML type. (If you're new to web services programming, now would be a good time to go read Java Web Services, by Dave Chappell and Tyler Jewell, also published by O'Reilly.)

A call to a web service can return a simple data type (numbers, strings, etc.) or a complex type. Complex types are defined within the WSDL file that specifies the web service, in the form of embedded XML Schema documents. In this case, the schema for the patient record type looks like this:
<schema
         targetNamespace="http://chapter9/IMedicalRecordService.xsd"
         xmlns="http://www.w3.org/2001/XMLSchema"
         xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
         <complexType name="chapter9_PatientRecord">
            <all>
               <element name="FirstName" type="string"/>
               <element name="LastName" type="string"/>
               <element name="MiddleName" type="string"/>
               <element name="Address1" type="string"/>
               <element name="Address2" type="string"/>
               <element name="City" type="string"/>
               <element name="State" type="string"/>
               <element name="Zip" type="string"/>
               <element name="Country" type="string"/>
               <element name="PatientID" type="long"/>
            </all>
         </complexType>
      </schema>
Within our application, we've defined a JavaBean, PatientRecord, which corresponds with this type. The JavaBean includes the same String and long properties, in traditional get/set form, and is otherwise indistinguishable from any other JavaBean.

Example 9-5
shows a class that uses the Apache SOAP toolset to identify a web service, calls its getMedicalRecord( ) method with a patient identifier as a parameter, and translates the return value from the XML defined in the WSDL file (shown above) into the JavaBean defined within our application. It then returns the JavaBean to the code that called it.

The code is pretty simple, and it gets even simpler once you realize that it's largely auto-generated: our IDE put most of the adapter code together based on the WSDL file and the JavaBean. All we did was clean it up and replace a vendor-provided SOAPHTTPConnection implementation with Apache's freely available version.
Example 9-5. MedicalRecordServiceAdapter.java
import org.apache.soap.transport.http.SOAPHTTPConnection;
import org.apache.soap.encoding.soapenc.BeanSerializer;
import org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.util.xml.QName;
import org.apache.soap.*;
import org.apache.soap.rpc.*;
 
import java.net.URL;
import java.util.*;
 
public class MedicalRecordServiceAdapter {
 
 public MedicalRecordServiceAdapter( ) {
    m_httpConnection = new SOAPHTTPConnection( );
    m_smr = new SOAPMappingRegistry( );
 
    BeanSerializer beanSer = new BeanSerializer( );
   m_smr.mapTypes(Constants.NS_URI_SOAP_ENC, 
      new QName("http://chapter9/IMedicalRecordService.xsd", 
        "chapter9_PatientRecord"), chapter9.PatientRecord.class, 
        beanSer, beanSer);
 }
 
 public String endpoint = 
    "http://service.hospital.org/records/MedicalRecordService";
 private SOAPHTTPConnection m_httpConnection = null;
 private SOAPMappingRegistry m_smr = null;
 
 public PatientRecord getMedicalRecord(Long patientID) 
    throws Exception {
    PatientRecord returnVal = null;
 
    URL endpointURL = new URL(endpoint);
    Call call = new Call( );
    call.setSOAPTransport(m_httpConnection);
    call.setTargetObjectURI("chapter9.MedicalRecordService");
    call.setMethodName("getMedicalRecord");
    call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
 
    Vector params = new Vector( );
    params.addElement(new Parameter("patientID", 
      java.lang.Long.class, patientID, null));
    call.setParams(params);
 
    call.setSOAPMappingRegistry(m_smr);
 
    Response response = call.invoke(endpointURL, "");
 
    if (!response.generatedFault( )) {
      Parameter result = response.getReturnValue( );
      returnVal = (PatientRecord)result.getValue( );
    }
    else {
      Fault fault = response.getFault( );
      throw new SOAPException(fault.getFaultCode(), fault.getFaultString( ));
    }
 
    return returnVal;
 }
 
}
Using this service adapter in a regular application is easy. To print out a patient's first name, all you would need to do is:
try {
      MedicalRecordServiceAdapter adapter = 
         new MedicalRecordServiceAdapter( );
      System.out.println(adapter.getMedicalRecord(new Long(4)).getFirstName( ));
    }
    catch(Exception ex) {
      ex.printStackTrace( );
    }
9.2.2 Session Façade

Enterprise JavaBeans take much of the complexity of building a distributed system out of the hands of the developer. They do not, however, eliminate that complexity: they hide it under an easy-to-use implementation (or at least easier). Just because you, the programmer, don't have to write the code for transporting data across a TCP/IP connection, doesn't mean that the data doesn't have to get transported.
Performance can be a challenge in EJB architectures, particularly when you don't have a lot of computing muscle to put behind your application. If a client application needs to access multiple entity beans in order to perform a transaction, it must make a network call each time it retrieves a new entity bean or calls an EJB method. The performance consequences are potentially huge, since a single network invocation can take several hundred milliseconds or more, depending on the amount of work that has to be done on the other side and the state of the intervening network.

The Composite Entity Bean pattern reduces the number of entity beans in a system and makes them easier to manipulate but does not address the broader performance problem. To make matters worse, even the best entity bean designs usually involve more than one bean per use case. A client wishing to modify more than one entity bean in the course of a transaction must handle transaction control itself, via the JTA API or another mechanism. Alternately, the business logic for the specific use case can be built into one entity bean, but this approach results in a massively overcomplicated entity bean design, in which domain objects have far too much knowledge of the context in which they are used.
EJB entity beans also don't provide much help when it comes to structuring business logic. The Business Delegate pattern helps organize an application's business logic in a convenient, easy-to-use manner, but doesn't control the number of network calls or simplify transaction management (although the burden can be shifted to the Business Delegate from the client application itself, which can be of some advantage to the programmers involved).

The Session Façade pattern addresses these issues by grouping sets of activities into a session bean that can be invoked by a client to complete an entire use case at once. The façade is a business-tier object that hides the session beans, entity beans, and other business services from the presentation tier.

Using the session façade simplifies transaction management. Since transaction control is implemented at the first point of entry into a method on the EJB, a business method attached to a session façade can manipulate all of the entity and session beans required to fulfill the current use case within a single transaction.

Figure 9-8
shows a potential class diagram for an EJB session façade that provides two business methods and makes use of generic business objects, EJB session beans, and EJB entity beans (in one case directly, in another indirectly via the session bean). The session façade can also use service adapters to access non-EJB resources.
 
Figure 9-8. A session façade class structure

Session façades provide a performance boost because interactions between EJBs within a server are much more efficient than interactions between EJBs and remote clients: with EJB 2.0, local beans can use local interfaces to communicate with one another, cutting out the networking layer.
Not all session beans are session façades! Business rules usually should not be implemented within the façade itself. A mathematical formula assessing a customer's credit rating, for example, belongs either in the entity bean representing the customer or in a separate, "utility" session bean, depending on how tightly coupled it is to the entity and whether the rule needs to be reused in other contexts. Conversely, remote applications shouldn't access any EJBs except for session façades. That same credit rating assessment should be included in (or at least accessible via) a session façade when the use case reports a credit assessment back to the presentation tier.

Use cases can be divided between a set of façades, although splitting use cases between session façades can be as much an art as a science. Creating a separate session façade for each use case can rapidly lead to a profusion of session façades. At the same time, avoid creating a single session façade for the entire application, unless the application is extremely small. All-encompassing session façades are sometimes known as "God Façades" or "God Objects." A God façade inevitably becomes difficult to maintain as the number of included use cases increases.

When organizing logic into a session façade, try to locate groups of related use cases. An e-commerce system, for example, might use a single session façade for all activities related to processing sales: checking inventory, setting shipping, arranging handling, and billing customers. Another façade might support catalog activities, and a third could handle account management for individual customers.
It's helpful to organize your use cases in an outline format, using the highest-level items as your top-level session façades. This technique usually leads to a nice balance between class complexity and class profusion.
9.2.2.1 Implementing a session façade

The EJB 2.0 specification introduced local EJBs, which can boost the performance of session beans by avoiding excessive round trips to the same server (see the Round-Tripping antipattern in Chapter 12). By restricting local EJBs to the same JVM, much of the overhead associated with translating data to and from network formats can be avoided. Example 9-6 shows a simple session façade, which uses the local interfaces of a number of EJBs to process a sale.
Example 9-6. A session façade EJB
import javax.ejb.*;
import java.util.*;
import javax.naming.*;
 
public class SaleFacadeBean implements SessionBean {
    private SessionContext context;
    private LocalCustomerHome customerHome;
        private LocalItemHome itemHome;
        private LocalSalesRecordHome recordHome;
    
    public void setSessionContext(SessionContext aContext) {
        context=aContext;
    }
    
    public void ejbActivate( ) {}
    public void ejbPassivate( ) {}
    
    public void ejbRemove( ) {
        customerHome = null;
        itemHome = null;
        recordHome = null;
    }
 
    public void ejbCreate( ) {
        try {
            InitialContext ic = new InitialContext( );
            customerHome = 
               (LocalCustomerHome)
               ic.lookup("java:comp/env/ejb/local/Customer");
            itemHome = 
               (LocalItemHome)
               ic.lookup("java:comp/env/ejb/local/Item");
            recordHome = 
               (LocalSalesRecordHome)
             ic.lookup("java:comp/env/ejb/local/Record");
        } catch(Exception ex) {
            throw new EJBException(
           "Error looking up home object: " + ex, ex);
        }
    }
 
    public ReceiptDTO doSale(int itemNumbers[], int customerId) {    
        try {
           LocalCustomer cust =     
          customerHome.findByPrimaryKey(customerId);
      
           LocalItem items[] = new LocalItem[itemNumbers.length];
                for (int i = 0; i < itemNumbers.length; i++) {
               items[i] = itemHome.findByPrimaryKey(itemNumbers[i]);
           }

	  
	  	  
	  
Last modified on Tuesday, 15 September 2009 13:09
Vicky

Vicky

E-mail: This e-mail address is being protected from spambots. You need JavaScript enabled to view it