Core J2EE Patterns
Best Practices and Design Strategies

Deepak Alur,   John Crupi,   Dan Malks

Bad practices

Refactorings

J2EE patterns


Refactorings - more

ProblemSolution
Introduce a controller Control logic is scattered throughout the application, typically duplicated in multiple JSP views. Extract control logic into one or more controller classes that server as the initial contact point for handling a client request.
Introduce synchronizer token Clients make duplicate resource requests that should be monitored and controlled, or clients access certain views out of order by returning to previously bookmarked pages. Use a shared token to monitor and control the request flow and client access to certain resources.
Localize disparate logic Business logic and presentation formatting are intermingled within a JSP view. Extract business logic into one or more helper classes that can be used by the JSP or by a controller.
Hide presentation tier-specific details from the business tier Request handling and/or protocol-related data structures are exposed from the presentation tier to the business tier. Remove all references to request handling and protocol-related presentation tier data structures from the business tier (e.g. HttpServletRequest). Pass values between tiers using more generic data structures.
Remove conversions from view Portions of the model are converted for display within a view component. Extract all conversion code from view and encapsulate it in one or more helper classes.
Hide resource from client Certain resources, such as JSP views, should not be directly accessible to a client browser. Hide certain resources via container configuration or by using a control component.
Wrap Entities with Session EntityBeans from the business tier are exposed to clients in another tier. Use a SessionBean facade to encapsulate the EntityBeans.
Introduce business delegate SessionBeans in the business tier are exposed to clients in other tiers. Use a business delegate to decouple the tiers and hide the implementaion details.
Merge SessionBeans Create a one-to-one mapping between SessionBean and EntityBean. Map course-grained business services to SessionBeans. Eliminate or combine SessionBeans that act solely as EntityBean proxies into SessionBeans that represent coarse-grained business services.
Eliminate inter-EntityBean communication Inter-EntityBean relationships introduce overhead in the model. Reduce or eliminate the inter-EntityBean relationships by using coarse-grained EntityBean (Composite Entity) with dependent objects.
Move business logic to Session Inter-EntityBean relationships introduce overhead in the model. Encapsulate the workflow related to inter-EntityBean relationships in a SessionBean (Session FAcade).
Separate data access code Data access code is embedded directly within a class that has other unrelated responsibilities. Extract the data access code into a new class and move the new class logically and/or physically closer to the data source.
Refactor architecture by tiers Increasing architectural sophistication requires changing the localization of data access logic and processing logic. Move data access code logically and/or physically closer to the actual data source. Move processing logic out of the client and presentation tiers into the business tier.
Use a connection pool Database connections are not shared. Instead, clients manage their own connections for making database invocations. Use a connection pool to pre-initialize multiple connections, improving scalability and performance.
 

J2EE patterns - more

ProblemSolution
Intercepting Filter When a request enters a Web application, it often must pass several entrance tests prior to the main processing stage. For example:
  • Has the client been authenticated?
  • Does the client have a valid session?
  • Is the client's IP address from a trusted network?
  • Does the request path violate any constraints?
  • What encoding does the client use to send the data?
  • Do we support the browser type of the client?
  • Create pluggable filters to process common services in a standard manner without requiring changes to core request processing code. The filters intercept incoming requests and outgoing responses, allowing pre-processing and post-processing. We are able to add and remove these filters unobtrusively, without requiring changes to our existing code.

    Use the GoF Decorator pattern, or, the servlet spec v2.3 filter mechanism.

    Front Controller The system requires a centralized access point for presentation tier request handling to support the integration of system services, content retrieval, view management, and navigation. When the user accesses individual views directly without going through a centralized mechanism, two problems may occur:
  • Each view is required to provide its own system services, often resulting in duplicate code.
  • View navigation is left to the views. This may result in commingled view content and view navigation.
  • Use a controller as the initial point of contact for handling a request. The controller manages the handling of the request, including invoking security services such as authentication and authorization, delegating business processing, managing the choice of an appropriate view, handling errors, and managing the selection of content creation strategies.
    View Helper Intermingling the business and systems logic with the view processing reduces modularity, makes the system less resilient to change, and provides a poor separation of roles among Web production and software development teams. The view component (i.e. JSP) only contains formatting code, delegating its processing responsibilities to its helper classes, implemented as JavaBeans or custom tags. Helpers also store the view's intermediate data model and serve as business data adapters.
    Composite View Sophisticated Web pages present content from numerous data sources, using multiple subviews in a single page. These pages are built by embedding formatting code directly into views, and this code is oftentimes replicated many times over with only slight variations. Create a template for the replicated code, instantiate subviews from the template, build up composite views from multiple atomic subviews. CompositeView can delegate to a ViewManager for more sophisticated control of page structure, or to provide conditional inclusion.
    Service to Worker No centralized component for managing access control, content retrieval, or view management, and duplicate control code scattered throughout various views. Additionally, business logic and presentation formatting logic are intermingled within the views, making the system less flexible, less reusable, less resilient to change, and blurring the separation of development roles. [A Controller delegates content retrieval to Helpers (which manage the population of the intermediate model for each View), then it delegates to the Dispatcher. A Dispatcher is responsible for View management and navigation and can be encapsulated within a Controller or a separate component.] Combine a Controller and Dispatcher with Views and Helpers to handle client requests and prepare a dynamic presentation as the response. The Dispatcher is intended to play a major role. It may invoke a business service to determine the appropriate View to display, or, it may rely on Helpers to decide.
    Dispatcher View The same problem as "Service to Worker". The Controller does not delegate content retrieval to Helpers, because these activities are deferred to the time of View processing. The Controller is the initial contact point, it manages authentication and authorization, and then it delegates to the Dispatcher. The Dispatcher plays a much more limited role. No outside resources are needed to choose the next View. The information encapsulated in the request is sufficient to determine the View to dispatch the request. In fact, the Dispatcher may find most (or all) of its functionality moved into the View(s). Service to Worker describes architectures with more behavior "up front" in the Controller and Dispatcher, while Dispatcher View describes architectures with more behavior moved back to the time of view processing.
    Business Delegate Presentation tier components interact directly with business services through RMI. This produces undue coupling, client complexity (networking issues), and poor performance (too many remote calls). Create a Business Delegate to hide underlying implementation details (such as lookup and access of EJBs). The Business Delegate is a client-side abstraction for the server-side services. It hides all distribution details, intercepts remote exceptions, performs any retry or recovery operations, throws application level exceptions as needed, and may cache results locally.
    Value Object Application clients need to exchange data with EJBs. Using multiple calls to get methods that return single attribute values is inefficient and sucks up network band width. Create a Value Object (a serializable class with public attributes) that can be used to house all the attribute values of an EJB. The client makes a single remote method invocation. The EJB initializes an instance of the Value Object and passes it by value to the client.
    Session Facade Clients are coupled directly to session and entity EJBs. Tight coupling leads to "hardening of the software arteries". Fine-grained method invocations saturate the network. Complexity of interactions unduly complicate the client code. Use a session bean as a facade to encapsulate the complexity of interactions amongst the server-side business objects participating in a workflow. The Session Facade: provides a simpler interface, creates a higher level "business service" abstraction, eliminates the lower level "chattiness" between the client and the server, and clearly centralizes security, transaction control, and relationship management.
    Composite Entity When entity beans are fine-grained objects, each bean usually represents a single row in a database. This is not a proper application of the entity bean intent. Fine (or row level) granularity results in tight coupling to the database schema and high network overhead. Create coarse-grained entity beans that represent an entire graph of objects. Each provides complex behavior beyond simply getting and setting field values. Each typically has dependent objects (objects that have no real domain meaning when not associated with its coarse-grained parent). To optimize the performance of these larger aggregate abstractions, consider using lazy loading and dirty marker storing.
    Value Object Assembler The application model is an abstraction of the business data and business logic implemented on the server. In a J2EE apllication, the model is a distributed collection of objects such as session beans, entity beans, DAOs, etc. For a client to obtain the data for the model, it must individually access all these remote objects, and, it must have business logic sufficient to reconstruct the model. These issues lead to: coupling, complexity, and poor performance. Create a Value Object Assembler responsible for constructing a composite value object that represents the complex data from many different business components. The composite value object carries the data for the model to the client in a single method call.
    Value List Handler The client requires a list of items from the server for presentation. The number of items in the list is unknown and can be quite large. EJB finder methods are not suitable for browsing large result sets because they: return remote proxies, do not support read-only references, and can possibly return thousands of references to entity beans. Create a Value List Handler to perform 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. The Handler provides an Iterator interface that the client can use to retrieve results one "page" at a time.
    Service Locator Locating a JNDI-administered service object requires significant resources, and is common to all clients that need to access the object. This can result in unnecessary duplication of code, and undue overhead. Use a Service Locator object to abstract all JNDI usage and to hide the complexities of initial context creation, EJB home object lookup, and EJB object re-creation. Multiple clients can reuse the Service Locator object to reduce code complexity, provide a single point of control, and improve performance by providing a caching facility.
    Data Access Object Data sources can include object-oriented databases, RDBMS's, flat files, LDAP repositories, legacy systems, and B2B bureau services. Within a "standardized" API such as SQL, the syntax of queries may vary dramatically from vendor to vendor. Clients of data sources can include: stand-alone applications, JavaBeans, servlets, SessionBeans, and BMP EntityBeans. Putting the connectivity and data access code directly within these components introduces tight coupling. Such dependencies make it difficult and tedious to migrate the application from one type of data source to another. Use a Data Access Object to abstract and encapsulate all access to the data source. The DAO manages the connection with the data source to obtain and store data.
    Service Activator An application needs to access EJBs asynchronously to support publish/subscribe or point-to-point messaging. [This is supported in EJB 2.0 with Message-Driven Beans, but MDBs are like stateless session beans; so stateful session beans and entity beans still cannot be accessed asynchronously.] Create a Service Activator to receive asynchronous client requests. The Service Activator locates the appropriate EJB(s) and delegates the requests. It may optionally send an acknowledgement to the client after successfully completing the request processing. The Service Activator is a JMS listener and delegation service.