J2EE Design Patterns Applicable to Session Beans

Let's now look at two J2EE design patterns applicable to session beans. The Session Façade pattern is a proven staple for applications using entity beans, while the EJB Command pattern offers an interesting alternative that is sometimes appropriate.

Both these patterns are most relevant to distributed applications. Both are typically implemented using stateless session beans.

The Session Façade Pattern in Distributed J2EE Applications

In distributed J2EE applications, remote clients should communicate with the EJB tier exclusively via session beans, regardless of whether entity beans are used. In a distributed application, session beans will implement the application's use cases, handle transaction management, and mediate access to lower-level components such as entity beans, other data access components, and helper objects. This approach is known as the Session Façade design pattern and can significantly improve performance, compared to having clients use entity beans directly.

The performance gain in using a session façade comes in the reduction of the number of expensive network round trips needed to implement a given use case. Such network round trips result from remote method calls and client-side JNDI lookups. Thus, even when clients really want to work with underlying components such as entity beans, adding the extra layer of a session façade improves performance. The session beans can communicate efficiently within the EJB container to lower-level components.

Consider a use case implementation that calls on three entity beans. If the client were to implement this use case, it would potentially require three JNDI lookups for remote objects and would need to make a minimum of three remote calls to the entities required. The client would also need to perform transaction management, to ensure that all three calls occurred in the same transaction (normally the intended behavior). Using a session façade, on the other hand, a client would need only one remote JNDI lookup and one remote call. This will lead to a large performance gain.

Remote method calls are often hundreds of times slower than local calls. By definition, a client will have to make at least one remote call to access any EJB method (often, two will be required, as it will be necessary to call the EJB home to obtain an EJB reference before invoking a business method). The more we can accomplish with each remote call, the better our distributed application will perform, even if this means that the amount of data exchanged in any one call is greater. By invoking a session façade to implement our use case, we can usually avoid even this tradeoff, as most of the data required for the business logic will never leave the server. For example, objects created during the work of the method may never be exposed to the client and never pass down the wire.

Finally, if remote clients are able to invoke low-level components such as entity beans directly, they are tightly coupled to the architecture of the EJB tier. This makes refactoring within the EJB tier unnecessarily difficult. For example, it's impossible to replace entity beans with another persistence strategy if performance requirements dictate.

In collocated applications, these performance considerations don't apply. Interface granularity is no longer a decisive factor in designing EJB interfaces, but there is still an argument for hiding the implementation of the EJB tier - for example, whether it uses entity beans - from code outside the EJB container.

The EJB Command Design Pattern

An alternative to the Session Façade pattern is the EJB Command pattern - a special case of the GoF Command design pattern. This pattern was used heavily in IBM's San Francisco business framework, which predated EJB. It's particularly suited for use in distributed applications, but can also be useful in collocated applications.

Implementing the EJB Command Design Pattern

This pattern is an object-oriented form of a callback method (we discussed callback methods in Chapter 4, and used them in the JDBC abstraction framework discussed in Chapter 9).

Application functionality is encapsulated in serializable command objects. In distributed applications, commands are created by remote clients and passed over the wire to the EJB container. Typically commands are JavaBeans, with input and output properties. All commands implement a common interface that includes an execute() method. For example:

    public interface Command extends Java.io.Serializable {      void execute() throws CommandException;    } 

A command is constructed on the client, which sets its input properties (for example, HTTP request parameters might be bound to a command's bean properties). The command is then passed to the EJB server, where its execute() method is invoked by a generic stateless session bean. Thus the code in the execute() method can access the EJB's run-time environment, via the JNDI environment (for example, to access EJBs or resource managers). The execute ( ) method either throws an exception, which will be returned to the client, or sets the output properties of the command object. The command object is then returned to the client, which uses the value of the output properties.

Note that as the signature of the Command execute() method is fixed, command implementations may only throw a checked CommandException, constraining their method signatures. Typically they will throw a generic command exception that nests another exception within it, or a custom subclass of CommandException.

Here we're focusing on the use of an SLSB as command executor, but the command pattern isn't tied to use of EJB. There must be a CommandExecutor interface, which can be implemented by an SLSB. A typical CommandExecutor interface might have a single method:

    public interface CommandExecutor {      Command executeCommand (Command command)        throws RemoteException, CommandException;    } 

An SFSB remote interface might extend this (the executeCommand() method has to throw java.rmi.RemoteException to allow this).

The following is a complete implementation of a simple command executor SLSB:

    import Java. rmi.RemoteException;    import Javax. ejb. EJBException;    import Javax. ejb. SessionBean;    import Javax. ejb. SessionContext;    public class CommandExecutorEJB implements SessionBean, CommandExecutor {      private SessionContext sessionContext;      public void setSessionContext (SessionContext sessionContext)        throws EJBException, RemoteException {      this. sessionContext = sessionContext;      }      public void ejbRemove() throws EJBException, RemoteException {      }      public void ejbActivate ( ) throws EJBException, RemoteException {      }      public void ejbPassivate ( ) throws EJBException, RemoteException {      }      public void ejbCreate() {      }    } 

The implementation of the executeCommand() method is completely generic, as the command itself contains all the application-specific code. Note that if an exception is encountered executing the command, the CommandExecutorEJB ensures that the current transaction is rolled back. The command executor session bean uses CMT, so the executeCommand ( ) method runs in its own transaction:

    public Command executeCommand (Command command) throws CommandException {      try {        command. execute();      } catch (CommandException ex) {        sessionContext. setRollbackOnly();        throw ex;      }      return command;    } 

A typical command object might look like this:

    public class CustomerDataCommand implements Command {      private int custid;      private String name;      private int invoices;      public void setCustomerId (int id) {        this. custid = id;      } 

     public void execute() throws CommandException {        try {          Context ctx = new InitialContext ();          SalesHome home = (SalesHome) ctx. lookup ("java:comp/env/ejb/sales");          Sales sales = home. create();          Customer cust = sales. findCustomer (this. custid);          this. name = cust. getForename();          this. invoices = <code to count invoices omitted>        } catch (Exception ex) {          throw new CommandException ("Failed to execute command in EJB container",                                        ex);        }      } 

     public String getName() {        return name;      }      public int getInvoices() {        return invoices;      }    } 

This command has one input property (customerId) and two output properties: name and invoices. The highlighted execute() method runs within the EJB container, where it is able to connect to another EJB to look up this data. Thus a complete use case - find customer data - executes in a single network round trip, and in a single transaction.

Note 

Note that a real command would probably used the Service Locator pattern - discussed in the next chapter - to reduce the amount of code needed to look up the EJBs it references.

The following code fragment shows how, with a reference to a CommandExecutor EJB, we could execute this command from the clientside. Note that the tc variable, which holds the command sent to the EJB tier, is set to the command returned from the EJB on successful execution of the command. This ensures that client code can access any output properties of the command, such as the name and invoices properties:

    TestCommand tc = new TestCommand();    tc. setCustomerId(customerId);    try {       tc = (TestCommand) commandExecutor. executeCommand(tc);       System. out. printIn ("Customer name is" + tc.getName());       System. out. printIn ("Customer has" + tc. getInvoices() + "invoices");    } catch (CommandException ex) {    } catch (RemoteException ex) {    } 

This code could be held in a client-side command executor that would hide the details of EJB access from other client-side code.

Advantages and Disadvantages of the EJB Command Design Pattern

The EJB Command design pattern has the following advantages:

  • It delivers the usual benefits of the GoF Command design pattern, allowing queuing and logging of commands.

  • It promotes the execution of an entire use case in a single round trip, minimizing network overhead.

  • It makes the EJB tier extensible without modifying EJBs.

  • It allows multiple return values from the EJB tier.

  • It's relatively robust against distributed failures, as it only requires only one remote call for each operation.

  • It's not tied to an EJB implementation. Clients work with command objects, rather than with EJBs. In the above example, the CommandExecutor interface could be implemented without using EJB.

The disadvantages are not all so obvious:

  • We need to include all command classes in the EJB container, so we need to redeploy the command executor EJB whenever we add a new command. It's possible for the EJB container to "export" classes to clients, but it's impossible to "import" additional classes into the EJB container, for security and other reasons.

  • It makes the client dependent on libraries used in the EJB tier. For example, if some commands use JDO, the client needs access to the JDO libraries.

  • Sometimes efficiency may dictate that what goes up to the EJB server shouldn't come back down. Using a typical implementation of the command design pattern like that shown above, the input data is returned redundantly to the client with the response.

  • Clumsy error handling. All exceptions must be wrapped in a generic CommandException. Client code must extract further details as necessary.

  • While a session bean can normally cache resources and data, a command can't, as new commands are constantly being created and all command data has to be serialized and deserialized twice. For example, we will need to do a JNDI lookup with each execute() call to obtain EJB references that could have been cached if the operation were implemented in a normal SLSB. However, JNDI lookups should be fast within the EJB container.

  • A command can only rolback the current transaction by throwing a CommandException, as it doesn't have access to the command executor EJB's SessionContext. Although it would be possible to pass the SessionContext as a parameter to the execute() method, this increases the client's dependency on an EJB implementation.

  • The EJB Command design pattern introduces a real risk of code duplication. As using the EJB Command pattern tends to alter the way in which teams work, it is easier for developers to implement the same functionality without refactoring common code. Close teamwork can help to minimize this risk, but there is also a serious danger of the Command pattern making it hard to share common code.

Important 

Due to these disadvantages, I don't tend to use the EJB Command design pattern. However, it's a viable option, and worth discussing as it demonstrates one approach to ensuring that an entire use case executes in one remote call in distributed applications.

Using Commands without Adopting the Command Design Pattern

The idea of encapsulating a request to the EJB tier - or any business interface - as a single object, rather than individual parameters, can be used without shipping executable code from client to server.

We can simply provide a method on our business interfaces, which may or may not be implemented using EJB, to handle each command. In this approach, the command does not contain the necessary business logic, just one request to the application. In this approach, we don't always need to return the command (we can return a distinct output object), and such business methods can throw any exceptions we choose.

This approach, which we'll use in the sample application, promotes good practice in web applications by formalizing the role of web tier code to convert user gestures into commands and command responses to views to display to the user. It also facilitates testing. Many web applications use this approach, and the design of most MVC web application frameworks encourages it.

The following example from the sample application illustrates this approach. Some of the methods on the com.wrox.expertj2ee.ticket.boxoffice.BoxOffice interface take object (command) arguments rather than multiple primitive or object parameters. For example, the allocateSeats() method, which initiates the booking process, has the following signature:

    public interface BoxOffice {      // Other methods omitted 

     Reservation allocateSeats (ReservationRequest request)          throws NotEnoughSeatsException, NoSuchPerformanceException,                 InvalidSeatingRequestException;    } 

The ReservationRequest parameter is a command. Its properties are automatically populated from HttpRequest parameter values by infrastructure code invoked by com.wrox.expertj2ee.ticket.web.TicketController, the application's web-tier controller class. This use of a single object, instead of multiple parameters, means that the BoxOffice interface does not need to change if more information needs to be carried with each reservation request, and that it's easy to queue and log commands or publish events (using the Observer design pattern) when a command is issued or processed.

The implementation of the BoxOffice interface, not the ReservationRequest command object, contains the business logic necessary to process this command.



Expert One-on-One J2EE Design and Development
Microsoft Office PowerPoint 2007 On Demand
ISBN: B0085SG5O4
EAN: 2147483647
Year: 2005
Pages: 183

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net