ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Web and Enterprise Architecture Design Patterns for J2EE, Part 1

by Ganesh Prasad, Rajat Taneja and Vikrant Todankar
09/10/2003

Editors Note: In this article, the authors propose patterns in the following five categories: Partitioning, Scope, Security, Navigation, and Data Volume Control. Because of space limitations, we have split this article into two pieces (clearly a partitioning problem with impact on navigation). This week, you will read about Partitioning and Scope. Next week, you will find descriptions of the remaining three categories.

Ever since the publication and wildfire popularity of Design Patterns by the "Gang of Four" (Gamma, Helms, Johnson, and Vlissides), software industry professionals have gained a powerful new approach to design. A design pattern, by their definition, "describes the core of a problem that occurs over and over again, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice." In general, a design pattern has four essential elements: a name, the problem it aims to solve, the elements of the solution, and the consequences (results and trade-offs) of using the pattern.

Design patterns described in GoF, such as Factory Method, Observer, Facade, etc., have become familiar motifs of the modern application design landscape. As presciently stated by the authors, design patterns perform as important a function by merely naming as by describing and analyzing. Who can estimate the number of person-hours saved in design meetings by the use of pattern names rather than their tedious descriptions?

Seven years after the book was published, we find that design patterns are as relevant as ever. However, later design patterns introduced by other authors seem to add less and less to the value of the collection initially offered by GoF. By way of proof, none has achieved the popularity or name recognition of the initial 23 patterns. To borrow a term from Economics, then, we might say that the marginal utility of newer patterns seems to be approaching zero. So is the "science" of design patterns nothing more than a static collection of one-off insights?

Related Reading

J2EE Design Patterns
By William Crawford, Jonathan Kaplan

On the contrary; from our own experience as web-era application designers, we see a repetition of history (a pattern, if you will) in the fact that there is an unfulfilled need for a set of named ways of doing things at the level of an application or subsystem, in the same manner that the original design patterns fulfilled a need for standardization of lower-level functionality.

Nothing of what we will talk about in the following sections is really new. They are simple, standard, common-sense ways of doing things that most experienced developers know about and have themselves used. But in the style of the original set of design patterns, we will classify and name these obvious banalities (in addition to describing them), in the hope that the names themselves will prove to have evocative value by vividly capturing the essence of their functionality. We call these "Web and Enterprise Architecture Design Patterns," a grandiose name for design patterns at the next level of application granularity.

Being committed J2EE practitioners, we will restrict ourselves to covering Java-related problems and solutions, though many of the concepts may be applicable to other technologies as well. (Shouldn't design patterns be language-independent? That's an attractive idea, but one could argue that GoF patterns such as Abstract Factory and Prototype, while useful in C++ and SmallTalk, are redundant in Java, because the Class class and Java's reflection mechanism solve such problems very elegantly.)

The Care and Taxonomy of WEA Design Patterns

Recall that the GoF design patterns fell into three neat categories that made them easier to understand and remember -- Creational, Structural, and Behavioral.

Following in this hallowed tradition, we propose to divide our patterns into Partitioning, Scope, Security, Navigation, and Data Volume Control categories. This is by no means an exhaustive list, but merely a set of pattern types we felt would be appropriate as a starting point.

Description of Patterns

We roughly follow the structure established by GoF to describe our patterns, but in the interests of brevity, we have collapsed the treatment into three broad headings. Under "Intent," we state the intended use of the pattern in a single line. We describe both the problem and the outline solution under a single heading "Problem and Solution." Then we briefly discuss the positive and negative consequences of the pattern under "Consequences."

We have omitted UML class diagrams, again in the interests of brevity. The patterns are generally simple enough not to require such detailed illustration. We have also omitted Java code examples. We are sure most J2EE developers can understand the patterns and "roll their own."

Partitioning Patterns

In web applications, both the client (browser) and the server are capable of executing logic -- the client through JavaScript, and the server through a server-side scripting language such as Java or PHP. But there are several factors that dictate the partitioning of application logic between these two tiers.

1. Dumb Client

Intent

To implement application logic with no support from the client (browser).

Problem and Solution

Some applications adopt a policy that avoids JavaScript at all costs. One popular reason is that the application should be capable of working even if the user turns off JavaScript support in the browser. Another common reason is that the developers want to avoid having to support different browser versions (though with improving W3C compliance by all modern browsers, such concerns are disappearing).

If any of such reasons apply, then all processing must take place on the server, even if the processing belongs on the front end, is trivial, and can easily be performed through JavaScript. Most e-commerce applications, for example, calculate totals on the server, even though all of the information required for the calculation exists on the client.

Consequences

The positive consequence of choosing a Dumb Client design pattern is an application that can work with almost any browser, including very old ones. It can support extremely security-conscious users who do not allow client-side scripting.

The negative consequences of this pattern are an increased level of network traffic and increased load on the server. This can have an adverse impact on performance, though the exact impact will need to be assessed on a case-by-case basis. Besides, several actions now become clumsier and less natural when they are not performed locally, making the application less interactive and friendly.

2. Independent Client

Intent

To implement the most interactive interface, leveraging client- and server-side processing capability.

Problem and Solution

Most modern web applications require a judicious mixture of client-side and server-side processing. JavaScript is used to perform local form validations and improve interactivity. The server only processes requests that genuinely require back-end interaction. The client-side code and server-side code are independently written, and some of the validation may be redundantly coded in both tiers.

Consequences

The positive consequence of the Independent Client pattern is an application with a more natural and intuitive feel. Most processing that can be done without server intervention is performed locally with consequent good response times. Network traffic is kept to the minimum and the load on the server is minimized.

The negative consequences of this pattern stem from the need to code some logic redundantly in both tiers. Client-side validation, for example, is friendlier than server-side validation because it provides earlier feedback. At the same time, it is not a substitute for server-side validation, because the back end must not rely on the front end for validation, but must protect itself from incorrect data by performing its own checks. Such redundancy can lead to anomalies and errors, especially when logic changes during the application's maintenance phase. There are also subtle differences between Java and JavaScript that can trap the unwary.

3. Server-Modified Client

Intent

To provide high levels of client-side interactivity through smart server-side processing.

Problem and Solution

Sometimes, the processing required on the client side requires data that is stored in a database on the back end. Rather than make a server call every time this data is to be accessed, the application can pre-load the data into a set of data structures that are accessible through JavaScript code when required. So once a page is loaded, it has all of the associated data required to enable highly-interactive, client-side processing. Dynamic or conditional population of dropdown lists on the client side, for example, can be achieved through JavaScript code acting on arrays that were generated and populated from a database by the server.

More complex applications may require the generation of JavaScript functions themselves by the back end. Client-side validation, for example, may be controlled by the server.

The Server-Modified Client pattern is intended to deal with the modification of JavaScript code and data structures by web-tier Java code. It does not refer to the generation of HTML elements, which is a standard function of any JSP page. (There are, of course, grey areas, such as the conditional generation of HTML navigational elements, which are an implicit expression of business logic.)

Consequences

The positive consequence of the Server-Modified Client pattern is the achievement of interactivity without redundancy. It is now possible to control both client- and server-side logic from a single location. Anomalous behavior of the two tiers can be reduced or eliminated.

The negative consequence of the pattern is increased complexity. Generation of JavaScript data structures is usually simple, but code that generates other code is typically an order of magnitude more complex to maintain.

Scope Patterns

Scope refers to the lifetime of certain pieces of information, and the most commonly encountered scopes, in our experience, are Request, Session, and Relationship.

A request-scoped variable must be accessible as long as the client's request has not returned from the server. A session-scoped variable must be accessible as long as the user's login session remains. Finally, a relationship-scoped variable must remain as long as the details of the user remain in the application's database. Relationship is not a scope defined by the JSP specification, but crops up repeatedly in real-world situations such as personalization. (We have not faced any significant issues dealing with two other JSP-defined scopes: Page and Application.)

4. Request Accessor

Intent

To provide a uniform mechanism to access request-scoped variables in multi-step server-side processing.

Problem and Solution

One of the cruel and apparently senseless limitations of JavaServer Pages discovered with dismay by most novice web programmers is that there is no setter equivalent to the HttpServletRequest object's getParameter() method. This can become a serious issue in applications where the processing on the web tier takes place in a series of steps, with the request object being forwarded from component to component. It may be necessary to modify request-scoped data as part of such processing, but in the absence of a setParameter() method, this becomes extremely cumbersome.

One glimmer of hope comes from the existence of the getAttribute() and setAttribute() methods of the request object, which deal with any named object (in contrast to the getParameter() method, which returns a named String). However, using getAttribute() to retrieve parameters from a request object that is freshly received from the browser will not do the trick. The getParameter() method has to be called for this.

The Request Accessor pattern is designed to manage the retrieval, storage and re-retrieval of request-scoped variables using a consistent interface. In its simplest form, the pattern is a static method (say, getAttributeOrParameter()) of a utility class that searches the HttpServletRequest object for a named object. It first calls getAttribute(), and if nothing is retrieved, calls getParameter(). Any server-side component that needs to retrieve a request-scoped variable merely calls this method. The very first access will then retrieve the parameter passed by the browser. When this variable is modified and put back into the request, it must be done using setAttribute(). Subsequent calls to the Request Accessor will then retrieve the modified attribute, not the original parameter. Since all passed parameters are strings, and all strings are objects, it is a trivial matter for the Request Accessor to do the casting to a string whenever an object is retrieved.

(In multi-part forms, an added complication has been observed in that the entire request object is thrown away and recreated when one component forwards to another. This obviously results in the loss of all request-scoped variables. In this case, it may be necessary for components to pass request-scoped variables (with care!) through the session object, for example. The Request Accessor can be modified to cater to such a wrinkle as well.)

Consequences

The positive consequence of the Request Accessor pattern is a simpler way to handle request-scoped variables in multi-step server-side processing, such as Struts.

The negative consequence of the pattern is the extra vigilance required to use attribute names in such a way as to avoid conflicts.

5. Stateless Channel

Intent

To free application state management from dependence on web-centric mechanisms.

Problem and Solution

Most web applications that maintain session state use the JSP engine's implicit "session" object to hold session-scoped variables. However, multi-channel applications may explicitly want to avoid tying themselves to a web-centric mechanism. They would prefer state to be maintained end-to-end between the client and the application server, with the intermediate web tier (JSP engine) remaining stateless. This approach makes it easier to add channels (such as IVR -- Interactive Voice Response) without duplicating the state maintenance logic. It also helps to avoid making web sessions "sticky" by decoupling them from physical JSP engines. Horizontal load balancing in the web tier can then be achieved more simply through a round-robin scheme than through clustering. (Besides, clustering in the web tier is relatively new and may not be supported by all JSP engines.)

The Stateless Channel design pattern helps to manage session-scoped variables in multi-channel applications. Here, the application server generates a session token string after successful authentication, which is passed back through the web tier to the client (e.g., browser). The web tier does not store this session token, either in the HttpSession object or elsewhere. The client (browser) tier holds this token, in the shape of a form's hidden variable or as a "get" parameter in hyperlinks. Thus, when the user submits a form or follows a hyperlink, the session token is passed back and is automatically available to the web tier, which passes it on (again, without storing it locally) to the application tier for validation, and retrieval of session-scoped (and relationship-scoped) variables. Thus, session state is maintained end-to-end, in a completely channel-neutral manner. Hence the name Stateless Channel.

Consequences

The main positive consequence of the Stateless Channel pattern is the ability to build multi-channel applications without redeveloping state management in every channel tier. It also renders the channel tier capable of horizontal scaling through a simple load-balancing scheme such as round-robin.

The main negative consequence of the pattern is an increase in complexity, as well as the extra discipline required to avoid the temptation to use the web session object. Developers who are used to purely web-based applications may have difficulty adjusting to this mindset.

Next week, in the conclusion to this two-part series on Web and Enterprise Architecture Design Patterns, the authors will describe patterns in the remaining three categories: Security, Navigation, and Data Volume Control.

Ganesh Prasad is an architect with Westpac Banking Corporation, Australia's third-largest bank.

Rajat Taneja is a contractor with Sun Microsystems, Australia.

Vikrant Todankar is a senior consultant with EDS Australia, on assignment to the Commonwealth Bank of Australia, Australia's second-largest bank.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.