Using J2EE Design Patterns

Design Patterns in the VSM

The VSM sample application demonstrates ways to implement the following design patterns.

MVC (Model–View–Controller)

The MVC design pattern differs from the others discussed in this article, which describe ways to solve specific programming problems or perform specific tasks, because the MVC design pattern describes an approach to an application as a whole. The approach is multi-tiered and modular; the MVC design pattern identifies three entities, each operating in a different logical layer within the application space:

  • Model: A back-end representation of enterprise data and business logic, the model also maintains data about the state of the application. In many cases, the model is a logical representation of a real-world process.
  • View: The front-end presentation layer that renders the model for end-users. Views also provide user interfaces for accessing the enterprise data represented by the model.
  • Controller: The middle tier of the MVC pattern. The controller defines application behavior, selecting views for presentation and capturing a user's interactions with views and routing them as commands to the model.

The MVC design pattern defines a clear separation of application logic, presentation details, business rules and data. As a result, multiple clients, displays, and devices can use the same application and business rules to work with the same data. The following figure shows how model, view, and controller interact.

Model - View - Controller

  • The model is implemented by EJBs and other Java classes, many of which represent real-world objects. such as shops, shopping carts, and orders. For implementation examples, see Shops.java, ShopsBean.java, and ShopsHome.java.
  • The views are provided by JavaServer Pages (JSPs) rendered in a browser. Examples include shoppingMall.jsp, mallAdmin.jsp, and cart.jsp.
  • The central controller is implemented in RouterServlet.java. When an end-user interacts with a view (for example, by submitting a form), this HTTP servlet dispatches the action to a controller object, such as LoginController orCartController, as appropriate, which in turn invoke methods on objects in the model.

The process of dispatching and executing commands is itself captured in a design pattern, Command Façade, discussed in the next section.

Command Façade

The Command Façade design pattern describes a way to work with method calls as objects. Without the Command Façade design pattern, a client must find an object, hold a reference to it, then use that reference to call a method. Using the Command Façade design pattern, a client issues commands by name, and a command object dispatches each command to a method call on an underlying EJB. This encapsulation of commands presents a consistent interface to clients while hiding implementation details--developers are free to change the back-end implementation (model or controller) without fear of breaking the presentation layer (view). Also, commands are cached in a history list so that they can be reused, improving performance.

The VSM implements this design pattern in several files, including RouterServlet.java, Command.java, and CommandCache.java. When the main VSM controller (RouterServlet) receives a request from a view, its service method executes and invokes the Java class RequestMap to load the values from requestmap.xml into a Hashtable, thereby mapping the URI to a Controller class. The RequestMap class maintains these values in a Hashtable so that subsequent calls can be made from memory instead of reading the properties file each time.

The following figure shows two event sequences. First, it shows what happens when the RouterServlet is initialized: it initializes the RequestMap class, which in turn reads mappings from requestmap.xml. The second sequence shows what happens later in the application life cycle when an end-user sends a command (in this case, to remove an item from the shopping cart).

Command Facade event flow.

Here is a portion of requestmap.xml, which maps URIs to methods. This example maps the /remove URI to the removeItem method of the CartController class, and specifies cart.jsp as the page to display for the resulting view.

<?xml version="1.0"?>
<mappings> ... <uri-mapping>
<uri>/remove</uri>
<jsp-template>jsps/mallUser/cart.jsp</jsp-template>
<controller class="oracle.otnsamples.vsm.controllers.CartController"
method="removeItem"/>
<security>
<authenticate>false</authenticate>
<invalidate-session>false</invalidate-session>
</security>
</uri-mapping> ... </mappings>

The following code comes from Command.execute. Given a URI submitted with a client request, this code searches the request map for the corresponding controller and method, then invokes the method on that controller. Then it returns the response to the RouterServlet which parses it to find out which JSP to invoke for the next client view.

public class Command {
...
public UserResponse execute( String uri, 
Hashtable request,
UserSession session,
String controller )
throws InvalidURIException, BusinessException {
// Initialize Response object
UserResponse response = null;
try {
// Provides access to the required method on the Controller Class
Method methodObject = null;
// Create the controller object only if not created previously
if( baseController == null ) {
baseController = (BaseController)Class.forName( controller ).newInstance();
}
// Check the cache for method object
if( uriMethodMap.containsKey( uri ) ) {
methodObject = (Method)uriMethodMap.get( uri );
} else {
// An array of Class objects that identify the method's formal
// parameter types in declared order
Class[] paramTypes = new Class[] {
Hashtable.class, UserSession.class
};
// Get the method name from the RequestMap and instantiate the method
String methodName = RequestMap.getInstance().getMethod( uri );
methodObject = baseController.getClass().getMethod(methodName, paramTypes);
// Cache this method object
uriMethodMap.put( uri, methodObject );
}
// Create an object array of the parameters to be passed to the method
Object[] params = {
request, session
};
// Execute the method on the specified controller object using reflection
response = (UserResponse)methodObject.invoke( baseController, params );
} catch ... }
// Return the response from the Controller
return response;
} ... }

 

Session Façade

The Session Façade design pattern is useful in situations where client objects need to interact with a set of EJBs to perform tasks in a workflow. For example, in the VSM environment, customers browse shops and order products, shop owners track orders and maintain inventory, and adminstrators approve and reject requests for new shops and manage category lists. In an implementation where client objects interact directly with the underlying EJBs, the following problems can arise:

  • When an EJB's interface changes, client objects must also be updated. This situation is analogous to the brittle class problem common in object-oriented programming.
  • To carry out a workflow, client objects must make numerous remote calls to access the EJBs, leading to increased network traffic and reduced performance.

A session façade solves such problems by presenting client objects with a unified interface to the underlying EJBs. Client objects interact only with the façade, which resides on the server and invokes the appropriate EJB methods. As a result, dependencies and communication between clients and EJBs is reduced. A session façade can also simplify transaction management: for example, when a database transaction involves multiple method calls, all could be wrapped in one method of the façade and the transaction could be monitored at that level.

In the VSM, the CartManagerBean implements a session façade that provides an interface to the EJBs that manage the items in a customer's shopping cart. The CartManagerBean exposes the checkOutCart method to clients, encapsulating inventory management and order creation tasks performed by underlying objects InventoryManager and OrdersBean (accessed via OrdersHome).

  public StringBuffer checkOutCart( ShoppingCart cart )
throws CartException {
try {
// Get the order home
OrdersHome home = (OrdersHome)ServiceLocator.getLocator().
getService( "Orders" );
... // For each item in the cart
for( int i = cart.getItems().length - 1; i >= 0; i-- ) {
currentItem = (CartItem)cart.getItems()[i];
// check the inventory
if( !InventoryManager.inventoryCheck( currentItem.getID(),
currentItem.getQuantity() ) ) {
response.append( "Failure," );
response.append( currentItem.getID() );
response.append( "," );
response.append( InventoryManager.getInventory(currentItem.getID()));
continue;
} ... // Create the order and add it to the table
order = home.create( new Integer( orderID ), details );
shops.put( shopID, order );
}
... // Add it to the order for the shop
order.setOrderItemID( item );
cart.removeItem( i );
}
if( response.length() < 1 ) {
response.append( "Checked out your cart successfully" );
}
return response; ... } ... }

The following figures show this client-EJB interaction with and without the session façade, and how the session façade reduces network traffic.

Without Session Façade With Session Façade
(CartManagerBean)
Lots of netwrok traffic without the Session Facade.
Session Facade reduces network traffic.

Value Object

The Value Object design pattern (also known as Data Transfer Object) describes a container for a set of related data. It is often (but not necessarily) used with the Session Façade pattern to reduce network traffic and the number of method calls required to get an entity's attribute values. For example, when a customer uses the VSM to buy a product, the application generates an order comprising several attributes including order ID, order date, customer name, and shipping address. To retrieve the details of an order, an application that does not implement the Value Object pattern would have to make a remote get method call for each attribute (example: Orders.getOrderID), adding to network traffic and increasing EJB container resource usage.

In contrast, the VSM implements the Value Object design pattern in several places, creating a container object and sending it across the network to the client, which can then access the data via local method calls. For example, the following code from CartManagerBean.checkOutCart uses an OrderDetails object to store data for a given order.

  public StringBuffer checkOutCart( ShoppingCart cart )
throws CartException {
... // Create order details
details = new OrderDetails( orderID,
new Date(),
cart.getUserName(),
shopID.intValue(),
shippingAddress.getAddress(),
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy