Core J2EE Patterns - Value List Handler

http://java.sun.com/blueprints/corej2eepatterns/Patterns/ValueListHandler.html
Core J2EE Pattern Catalog
Core J2EE Patterns - Value List Handler
 

Context

The client requires a list of items from the service for presentation. The number of items in the list is unknown and can be quite large in many instances.

Problem

Most Java 2 Platform, Enterprise Edition (J2EE) applications have a search and query requirement to search and list certain data. In some cases, such a search and query operation could yield results that can be quite large. It is impractical to return the full result set when the client's requirements are to traverse the results, rather than process the complete set. Typically, a client uses the results of a query for read-only purposes, such as displaying the result list. Often, the client views only the first few matching records, and then may discard the remaining records and attempt a new query. The search activity often does not involve an immediate transaction on the matching objects. The practice of getting a list of values represented in entity beans by calling an ejbFind() method, which returns a collection of remote objects, and then calling each entity bean to get the value, is very network expensive and is considered a bad practice.

There are consequences associated with using Enterprise JavaBeans (EJB) finder methods that result in large results sets. Every container implementation has a certain amount of finder method overhead for creating a collection of EJBObject references. Finder method behavior performance varies, depending on a vendor's container implementation. According to the EJB specification, a container may invoke ejbActivate() methods on entities found by a finder method. At a minimum, a finder method returns the primary keys of the matching entities, which the container returns to the client as a collection of EJBObject references. This behavior applies for all container implementations. Some container implementations may introduce additional finder method overhead by associating the entity bean instances to these EJBObject instances to give the client access to those entity beans. However, this is a poor use of resources if the client is not interested in accessing the bean or invoking its methods. This overhead can significantly impede application performance if the application includes queries that produce many matching results.

Forces

  • The application client needs an efficient query facility to avoid having to call the entity bean's ejbFind() method and invoking each remote object returned.

  • A server-tier caching mechanism is needed to serve clients that cannot receive and process the entire results set.

  • A query that is repeatedly executed on reasonably static data can be optimized to provide faster results. This depends on the application and on the implementation of this pattern.

  • EJB finder methods are not suitable for browsing entire tables in the database or for searching large result sets from a table.

  • Finder methods may have considerable overhead when used to find large numbers of result objects. The container may create a large number of infrastructure objects to facilitate the finders.

  • EJB finder methods are not suitable for caching results. The client may not be able to handle the entire result set in a single call. If so, the client may need server-side caching and navigation functions to traverse the result set.

  • EJB finder methods have predetermined query constructs and offer minimum flexibility. The EJB specification 2.0 allows a query language, EJB QL, for container-managed entity beans. EJB QL makes it easier to write portable finders and offers greater flexibility for querying.

  • Client wants to scroll forward and backward within a result set.

Solution

Use a Value List Handler to control the search, cache the results, and provide the results to the client in a result set whose size and traversal meets the client's requirements.

This pattern creates a ValueListHandler to control query execution functionality and results caching. The ValueListHandler directly accesses a DAO that can execute the required query. The ValueListHandler stores the results obtained from the DAO as a collection of Transfer Objects. The client requests the ValueListHandler to provide the query results as needed. The ValueListHandler implements an Iterator pattern [GoF] to provide the solution.

Structure

The class diagram in Figure 8.29 illustrates the Value List Handler pattern.


Figure 8.29 Value List Handler Class Diagram

Participants and Collaborations

The sequence diagram in Figure 8.30 shows the interactions for the Value List Handler.


Figure 8.30 Value List Handler Sequence Diagram

ValueListIterator

This interface may provide iteration facility with the following example methods:

  • getSize() obtains the size of the result set.

  • getCurrentElement()obtains the current Transfer Object from the list.

  • getPreviousElements(int howMany) obtains a collection of Transfer Objects that are in the list prior to the current element.

  • getNextElements(int howMany) obtains a collection of Transfer Objects that are in the list after the current element.

  • resetIndex() resets the index to the start of the list.

Depending on the need, other convenience methods can be included to be part of the ValueListIterator interface.

ValueListHandler

This is a list handler object that implements the ValueListIterator interface. The ValueListHandler executes the required query when requested by the client. The ValueListHandler obtains the query results, which it manages in a privately held collection represented by the ValueList object. The ValueListHandler creates and manipulates the ValueList collection. When the client requests the results, the ValueListHandler obtains the Transfer Objects from the cached ValueList, creates a new collection of Transfer Objects, serializes the collection, and sends it back to the client. The ValueListHandler also tracks the current index and size of the list.

DataAccessObject

The ValueListHandler can make use of a DataAccessObject to keep separate the implementation of the database access. The DataAccessObject provides a simple API to access the database (or any other persistent store), execute the query, and retrieve the results.

ValueList

The ValueList is a collection (a list) that holds the results of the query. The results are stored as Transfer Objects. If the query fails to return any matching results, then this list is empty. The ValueListHandler session bean caches ValueList to avoid repeated, unnecessary execution of the query.

TransferObject

The TransferObject represents an object view of the individual record from the query's results. It is an immutable serializable object that provides a placeholder for the data attributes of each record.

Strategies

Java Object Strategy

The ValueListHandler can be implemented as an arbitrary Java object. In this case, the ValueListHandler can be used by any client that needs the listing functionality. For applications that do not use enterprise beans, this strategy is useful. For example, simpler applications may be built using servlets, JavaServer Pages (JSP) pages, Business Delegates, and DAOs. In this scenario, the Business Delegates can use a ValueListHandler implemented as a Java object to obtain list of values.

Stateful Session Bean Strategy

When an application uses enterprise beans in the business tier, it may be preferable to implement a session bean that uses the ValueListHandler. In this case, the session bean simply fronts an instance of a ValueListHandler. Thus, the session bean may be implemented as a stateful session bean to hold on to the list handler as its state, and thus may simply act as a facade (see "Session Facade" on page 291) or as a proxy.

Consequences

  • Provides Alternative to EJB Finders for Large Queries
    Typically, an EJB finder method is a resource-intensive and an expensive way of obtaining a list of items, since it involves a number of EJBObject references. The Value List Handler implements a session bean that uses a DAO to perform the query and to create a collection of Transfer Objects that match the query criteria. Because Transfer Objects have relatively low overhead compared to EJBObject references and their associated infrastructure, this pattern provides benefits when application clients require queries resulting in large result sets.

  • Caches Query Results on Server Side
    The result set obtained from a query execution needs to be cached when a client must display the results in small subsets rather than in one large list. However, not all browser-based clients can perform such caching. When they cannot, the server must provide this functionality. The Value List Handler pattern provides a caching facility in the Value List Handler session bean to hold the result set obtained from a query execution. The result set is a collection of Transfer Objects that can be serialized if required.

    When the client requests a collection, or a subset of a collection, the handler bean returns the requested results as a serialized collection of Transfer Objects. The client receives the collection and now has a local copy of the requested information, which the client can display or process. When the client needs an additional subset of the results, it requests the handler to return another serialized collection containing the required results. The client can process the query results in smaller, manageable chunks. The handler bean also provides the client with navigation facilities (previous and next) so that the results may be traversed forward and backward as necessary.

  • Provides Better Querying Flexibility
    Adding a new query may require creating a new finder method or modifying an existing method, especially when using bean-managed entity beans. (With bean-managed entity beans, the developer implements the finder methods in the bean implementation.) With a container-managed entity bean, the deployer specifies the entity bean finder methods in the bean's deployment descriptor. Changes to a query for a container-managed bean require changes to the finder method specification in the deployment descriptor. Therefore, finder methods are ill-suited to handle query requirements that change dynamically. You can implement a Value List Handler to be more flexible than EJB finder methods by providing ad hoc query facilities, constructing runtime query arguments using template methods, and so forth. In other words, a Value List Handler developer can implement intelligent searching and caching algorithms without being limited by the finder methods.

  • Improves Network Performance
    Network performance may improve because only requested data, rather than all data, is shipped (serialized) to the client on an as-needed basis. If the client displays the first few results and then abandons the query, the network bandwidth is not wasted, since the data is cached on the server side and never sent to the client. However, if the client processes the entire result set, it makes multiple remote calls to the server for the result set. When the client knows in advance that it needs the entire result set, the handler bean can provide a method that sends the client the entire result set in one method call, and the pattern's caching feature is not used.

  • Allows Deferring Entity Bean Transactions
    Caching results on the server side and minimizing finder overhead may improve transaction management. When the client is ready to further process an entity bean, it accesses the bean within a transaction context defined by the use case. For example, a query to display a list of books uses a Value List Handler to obtain the list. When the user wants to view a book in detail, it involves the book's entity bean in a transaction.

Sample Code

Implementing the Value List Handler as a Java Object

Consider an example where a list of Project business objects are to be retrieved and displayed. The Value List Handler pattern can be applied in this case. The sample code for this implementation is listed in Example 8.29 as ProjectListHandler, which is responsible to provide the list of Projects. This class extends the ValueListHandler base class, which provides the generic iteration functionality for all Value List Handler implementations in this application. The ValueListHandler sample code is listed in Example 8.30. The ValueListHandler implements the generic iterator interface ValueListIterator, which is shown in Example 8.32. The relevant code sample from the data access object ProjectDAO, used by ValueListHandler to execute the query and obtain matching results, is shown in Example 8.31.

Example 8.29 Implementing Value List Handler Pattern

package corepatterns.apps.psa.handlers;

import java.util.*;
import corepatterns.apps.psa.dao.*;
import corepatterns.apps.psa.util.*;
import corepatterns.apps.psa.core.*;

public class ProjectListHandler 
extends ValueListHandler {

  private ProjectDAO dao = null;
  // use ProjectTO as a template to determine
  // search criteria
  private ProjectTO projectCriteria = null;

  // Client creates a ProjectTO instance, sets the 
  // values to use for search criteria and passes 
  // the ProjectTO instance as projectCriteria
  // to the constructor and to setCriteria() method
  public ProjectListHandler(ProjectTO projectCriteria) 
  throws ProjectException, ListHandlerException {
    try {
      this.projectCriteria = projectCriteria;
      this.dao = PSADAOFactory.getProjectDAO();
      executeSearch();
    } catch (Exception e) {
      // Handle exception, throw ListHandlerException
    }
  }

  public void setCriteria(ProjectTO projectCriteria) {
    this.projectCriteria = projectCriteria;
  }

  // executes search. Client can invoke this
  // provided that the search criteria has been
  // properly set. Used to perform search to refresh
  // the list with the latest data.
  public void executeSearch()   
  throws ListHandlerException {
    try {
      if (projectCriteria == null) {
        throw new ListHandlerException(
          "Project Criteria required...");
      }
      List resultsList = 
        dao.executeSelect(projectCriteria);        
      setList(resultsList);
    } catch (Exception e) {
      // Handle exception, throw ListHandlerException
    }
  }
}

The Value List Handler is a generic iterator class that provides the iteration functionality.

Example 8.30 Implementing Generic ValueListHandler Class

package corepatterns.apps.psa.util;

import java.util.*;

public class ValueListHandler
implements ValueListIterator {

  protected List list;
  protected ListIterator listIterator;

  public ValueListHandler() {
  }

  protected void setList(List list) 
  throws IteratorException {
    this.list = list;
    if(list != null)
      listIterator =  list.listIterator();
    else
      throw new IteratorException("List empty");
  }

  public Collection getList(){
    return list;
  }
    
  public int getSize() throws IteratorException{
    int size = 0;
        
    if (list != null)
      size = list.size();
    else
      throw new IteratorException(...); //No Data

    return size;
  }
    
  public Object getCurrentElement() 
  throws IteratorException {

    Object obj = null;
    // Will not advance iterator
    if (list != null)
    {
      int currIndex = listIterator.nextIndex();
      obj = list.get(currIndex);
    }
    else
      throw new IteratorException(...);
    return obj;

  }
    
  public List getPreviousElements(int count) 
  throws IteratorException {
    int i = 0;
    Object object = null;
    LinkedList list = new LinkedList();
    if (listIterator != null) {
      while (listIterator.hasPrevious() && (i < count)){
        object = listIterator.previous();
        list.add(object);
        i++;
      }
    }// end if
    else
      throw new IteratorException(...); // No data

    return list;
  }
    
  public List getNextElements(int count) 
  throws IteratorException {
    int i = 0;
    Object object = null;
    LinkedList list = new LinkedList();
    if(listIterator != null){
      while(  listIterator.hasNext() && (i < count) ){
        object = listIterator.next();
        list.add(object);
        i++;
      }
    } /  / end if
    else
      throw new IteratorException(...); // No data

    return list;
  }
    
  public void resetIndex() throws IteratorException{
    if(listIterator != null){
      listIterator = list.ListIterator();
    }
    else
      throw new IteratorException(...); // No data
  }
  ...
}


Example 8.31 ProjectDAO Class

package corepatterns.apps.psa.dao;

public class ProjectDAO {
  final private String tableName = "PROJECT";

  // select statement uses fields
  final private String fields = "project_id, name," +
      "project_manager_id, start_date, end_date, " + 
      " started, completed, accepted, acceptedDate," + 
      " customer_id, description, status";

  // the methods relevant to the ValueListHandler
  // are shown here.
  // See Data Access Object pattern for other details.
  ...
  private List executeSelect(ProjectTO projCriteria) 
  throws SQLException {

    Statement stmt= null;
    List list = null;
    Connection con = getConnection();
    StringBuffer selectStatement =   new StringBuffer();
    selectStatement.append("SELECT "+ fields + 
          " FROM " + tableName + "where 1=1");

    // append additional conditions to where clause
    // depending on the values specified in 
    // projCriteria
    if (projCriteria.projectId != null) {
      selectStatement.append (" AND PROJECT_ID = '" + 
        projCriteria.projectId + "'");
    }
    // check and add other fields to where clause
    ...
    
    try {
      stmt = con.prepareStatement(selectStatement);
      stmt.setString(1, resourceID);
      ResultSet rs = stmt.executeQuery();
      list = prepareResult(rs);
      stmt.close();
    }
    finally {
      con.close();
    }
    return list;
  }

  private List prepareResult(ResultSet rs) 
  throws SQLException {
    ArrayList list = new ArrayList();
    while(rs.next()) {
      int i = 1;
      ProjectTO proj = new 
        ProjectTO(rs.getString(i++));
      proj.projectName = rs.getString(i++);
      proj.managerId = rs.getString(i++);
      proj.startDate = rs.getDate(i++);
      proj.endDate = rs.getDate(i++);
      proj.started = rs.getBoolean(i++);
      proj.completed = rs.getBoolean(i++);
      proj.accepted = rs.getBoolean(i++);
      proj.acceptedDate = rs.getDate(i++);
      proj.customerId = rs.getString(i++);
      proj.projectDescription = rs.getString(i++);
      proj.projectStatus = rs.getString(i++);
      list.add(proj);

    }
    return list;
  }
  ...
}


Example 8.32 ValueListIterator Class

package corepatterns.apps.psa.util;

import java.util.List;

public interface ValueListIterator {
    
  public int getSize() 
    throws IteratorException;
    
  public Object getCurrentElement() 
    throws IteratorException;
    
  public List getPreviousElements(int count) 
    throws IteratorException;
    
  public List getNextElements(int count) 
    throws IteratorException;
    
  public void resetIndex()
    throws IteratorException;

  // other common methods as required
  ...
}

Related Patterns

  • Iterator [GoF]
    This Value List Handler pattern is based on Iterator pattern, described in the GoF book, Design Patterns: Elements of Reusable Object-Oriented Software.

  • Session Facade
    Since the Value List Handler is a session bean, it may appear as a specialized Session Facade. However, in isolation, it is a specialized session bean rather than a specialized Session Facade. A Session Facade has other motivations and characteristics (explained in the Session Facade pattern), and it is much coarser grained.

 

你可能感兴趣的:(Core J2EE Patterns - Value List Handler)