BUY THIS BOOK
Add to Cart

Print Book $44.99


Add to Cart

Print+PDF $58.49

Add to Cart

PDF $35.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £28.50

What is this?

Looking to Reprint or License this content?


J2EE Design Patterns
J2EE Design Patterns

By William Crawford, Jonathan Kaplan
Book Price: $44.99 USD
£28.50 GBP
PDF Price: $35.99

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Java Enterprise Design
Before we dive into specific patterns for enterprise architecture, we need to set the stage. In this chapter, we'll take a closer look at design patterns as a context, and then take an extremely quick tour through J2EE. Next, we'll explore the various tiers of an enterprise application, from the client up through the business logic and enterprise level services. We'll look at component-based design, and the four major themes that govern much of enterprise development: Extensibility, Scalability, Reliability, and Timeliness.
In the next chapter, we'll break off and concentrate on UML, the Unified Modeling Language, which provides a standard vocabulary to describe both design patterns and systems as a whole.
The rest of the book builds on the themes of good enterprise architecture practices and the design patterns that support them.
A design pattern is a recurring solution to a recurring problem. From a programming perspective, a pattern provides a set of specific interactions that can be applied to generic objects to solve a known problem. Good patterns strike a balance between the size of the problem they solve and the specificity with which they address the problem.
The simplest patterns may be summed up in no more than a sentence or two. Using a database to store information for a web site is a pattern, albeit a fairly high-level and obvious one. More complex patterns require more explanation, perhaps including the use of modeling languages or a variety of other forms of longer description.
Design patterns originated outside the computer industry, originally showing up in conventional (as opposed to computer systems) architecture. Architects of buildings and architects of software have more in common than one might initially think. Both professions require attention to detail, and each practitioner will see their work collapse around them if they make too many mistakes.
The book to read, if you're interested in the architectural origins of design patterns, is A Pattern Language: Towns, Buildings, Construction
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Design Patterns
A design pattern is a recurring solution to a recurring problem. From a programming perspective, a pattern provides a set of specific interactions that can be applied to generic objects to solve a known problem. Good patterns strike a balance between the size of the problem they solve and the specificity with which they address the problem.
The simplest patterns may be summed up in no more than a sentence or two. Using a database to store information for a web site is a pattern, albeit a fairly high-level and obvious one. More complex patterns require more explanation, perhaps including the use of modeling languages or a variety of other forms of longer description.
Design patterns originated outside the computer industry, originally showing up in conventional (as opposed to computer systems) architecture. Architects of buildings and architects of software have more in common than one might initially think. Both professions require attention to detail, and each practitioner will see their work collapse around them if they make too many mistakes.
The book to read, if you're interested in the architectural origins of design patterns, is A Pattern Language: Towns, Buildings, Construction by Christopher Alexander (Oxford University Press). Widespread acceptance of design patterns in software began with the publication of Design Patterns: Elements of Reusable Object Oriented Software (Addison-Wesley), by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, also known as the "Gang of Four."
Design patterns are discovered as much as created. Some of the most effective patterns emerged from two decades of object-oriented (OO) design theory and the fewer but highly intensive years of enterprise Java. In many cases, the challenge has been to move from best practices and gut feelings to clear, defined sets of activities that can form the basis for communication between developers. It's amazing how much easier it is to suggest that someone use an Actor-Observer pattern (which allows one piece of code to register interest in the activities of another and receive notifications of key events) than to explain how to implement an event model.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
J2EE
This book expects that you know the fundamental Java language and have some basic familiarity with the J2EE APIs. A J2EE environment differs from a Java 2, Standard Edition (J2SE) environment in that it offers a wider range of services than a standalone application could expect to find. J2SE is geared towards providing core language features (I/O, text manipulation, variables, object semantics), standard utility classes that apply in a variety of settings (collections, mathematics), and features required for building client applications (GUIs, and some basic enterprise integration, including access to databases and naming services).
The J2EE application model is built on a division of labor into various tiers. The client presentation in a web browser, applet, or Java application is separated from server side logic in a JavaServer Page or Java Servlet and the business logic in a database or Enterprise JavaBeans. The J2EE APIs are focused on implementing the interactions between tiers. The interfaces to each tier are standardized, allowing programmers with an understanding of the core J2EE concepts to easily apply their skills to any J2EE-based project.
The core of a J2EE application deployment is a J2EE-compliant application server. The application server supports hosting business logic components and web components, providing access to the full range of J2EE capabilities. Note that the J2EE API doesn't say very much beyond the essentials about how these servers should be designed, or how deployment, maintenance, and general administration should be conducted. The focus of the J2EE API, instead, is on programmatic interfaces and runtime behavior. This specialization can make it difficult to transfer administration skills from, say, IBM WebSphere to BEA WebLogic. Code, however, should transfer transparently. Figure 1-1 shows the canonical J2EE application model.
Figure 1-1: Canonical J2EE application model
Each J2EE API itself is simply a wrapper for a service provided by the J2EE container or by an external component within the enterprise. A full-scale J2EE environment could include one or more J2EE application servers hosting servlets and Enterprise JavaBeans, as well as an external transaction manager, an Oracle or DB2 relational database, and a messaging middleware system. Full J2EE platforms implement the following APIs:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Application Tiers
We've gotten all this way without defining enterprise applications. This is because they often defy simple definition. Enterprise applications range from mainframe-based transaction processing applications with aging green-screen terminals to phone systems, from traditional client/server to intranet web sites, and even to Amazon.com.
All enterprise applications are divided into tiers. These tiers are sometimes referred to as components, although this term is a little misleading; more than one component is frequently present on any given tier. A tier can be thought of as a collection of software with a particular scope of operation and a defined set of interfaces to the outside world.
Different people divide enterprise applications in different ways. The official J2EE application model, as discussed above, divides an application into a Client Presentation tier, a Server Side Presentation tier, and a Server Side Business Logic tier. Enterprise information systems, such as databases, constitute a fourth tier.
We call this the "official" model because it's what Sun's J2EE documentation proposes, but in reality, there's a lot of scope for individual choice. For example, when thinking about application tiers for this book, we came up with five layers:
Client Presentation tier
Provides a user interface to the end user. The client can be "thin" (typically a web browser), "fat" (a full scale application), or something in between.
Server Side Presentation tier
Provides the Client Presentation tier with the materials, such as HTML, required to produce the user interface.
Server Side Business Logic tier
Includes the business methods sitting behind an application, performing actions like registering customers, taking orders, and managing shipping. These can often be thought of as the "verbs" of an application.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Core Development Concepts
When evaluating the end product of any enterprise development project, we can score it on four factors: Extensibility, Scalability, Reliability, and Timeliness. Different projects emphasize these factors to different degrees: NASA programmers will emphasize reliability above all else, giving appropriately short shrift to timeliness concerns. A startup may emphasize scalability and timeliness, with concerns over extensibility put off for the next release.
Obviously, each of the four issues affects the others at some level. A highly extensible system might be made more scalable by plugging in higher performance components, and time spent up front building support for scalability will pay off in timely deployment of later versions. The important thing to know is that design patterns can improve performance in all four areas. In this book, we focus on extensibility and scalability in particular.
The one constant in software development is that requirements always change. With each version of a product, there are bugs to fix and ideas to develop into new features. These days, particularly in business programming, requirements often change between the time a product is designed and the time it is released. When requirements do change, software divides into two categories: the kind that can be easily extended and the kind that can't. Unfortunately, determining in advance which category your program fits into is difficult. If you were trying to extend a toaster, it might be easier to add the ability to toast bagels than bake cakes.
In general, the extensibility of software determines how easily changes can be accommodated. It is easy to say whether the program was extensible or not in hindsight, but for the program to be really useful, we must have a sense beforehand. In a first version, it is sufficient to show that a program avoids the most common extensibility pitfalls. Once a few versions have been released, empirical evidence provides a much clearer picture.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Looking Ahead
This chapter provides the groundwork for understanding some of the primary issues in J2EE development and in enterprise architecture as a whole. In the chapters that follow, we'll look at the individual tiers within the J2EE model, and discuss patterns and approaches for designing and implementing them.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: The Unified Modeling Language
Before we dive into the design patterns themselves, it's worth stepping back and thinking about the framework they fit into. Design patterns are just one part of the much broader discipline of software engineering. Building any application involves requirements gathering, design, implementation, testing, and deployment. While design patterns provide developers with a library of proven techniques and allow teammates to apply labels to concepts, the patterns don't begin to address every aspect of enterprise development. Most importantly, perhaps, design patterns can't tell you where to apply a design pattern. That's why we have design.
Design patterns describe the implementation strategies for particular areas of the application, and even the overall design. Enterprise applications in particular need effective engineering practices, given their complexity and business importance. As object-oriented languages take hold in business environments, a need has emerged for a common language to discuss and describe object-oriented software, as well as the problems and business processes the software serves.
Software and process-modeling languages were created to meet this need for common vocabulary to describe software structure. The various leading modeling languages have blended together over the last few years into the Unified Modeling Language, generally abbreviated as the UML.
UML supports all aspects of the software development life cycle, from requirements gathering to design to deployment. In particular, the UML can be extremely helpful in documenting the object structure underlying an application, known as the domain model ; but UML models can represent use cases, class structures, program interactions, process flows, physical deployments, packages, and more. Often, simply creating UML diagrams uncovers subtle flaws in system design, saving huge quantities of effort later on in a project. For our immediate purposes in this book, the UML also provides an excellent way to describe design patterns, both in terms of class structures and program processes.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Origins of UML
Modeling languages for object-oriented software have been around since the 1970s and began to proliferate in the late 1980s. The profusion of modeling options caused problems, and no single approach managed to reach critical mass. Thus, while many approaches were helpful in the design process itself, no common vocabulary emerged. The debates between the practitioners of different modeling systems are sometimes referred to as the "method wars," which brings to mind some fairly amusing images of the goings-on at academic conferences in the late 1980s and early 90s.
The UML specification developed out of the method wars. Grady Booch, James Rumbaugh, and Ivar Jacobson emerged as the leading lights in the modeling movement. Booch's approach was design-oriented, while Rumbaugh's Object Modeling Technique was geared toward data analysis. Jacobson created the Object Oriented Software Engineering (OOSE) method, which focused on developing "use cases" to feed system design. Jacobsen is known as the Father of Use Cases.
In 1994, Rumbaugh joined Booch at Rational Software, and introduced Version .8 of UML in October. A year later Jacobson arrived at Rational, and the three focused on merging their different, yet largely complementary approaches into a single model. Booch, Jacobson, and Rumbaugh became known as the Three Amigos. The Software Development Life Cycle (SDLC) they developed at Rational is known as the Rational Unified Process, but the modeling language associated with it can be applied in any number of frameworks.
In 1996, in response to a request for standards proposals from the Object Management Group, Rational formed the UML Partners Consortium to gather support for the standard. UML 1.0 was submitted to the OMG in January 1997 as a suggested specification.
The consortium followed up with the UML 1.1 proposal, and in November 1997, the UML specification was accepted as a standard. Subsequent iterations have brought the UML to Version 1.4, and a substantially improved version, 2.0, is currently in the advanced preparatory stages. The rest of this chapter focuses on key elements of UML 1.4.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Magnificent Seven
Remember that UML is not the be-all and end-all of software development methodology. It's just an extremely useful tool for communications between and within user groups, development teams, and deployment staff. It's possible to go overboard on modeling, particularly in areas that don't map directly to code, such as when building use cases during the requirements gathering phase.
UML is complex. The specification itself is dense, and as a result much of the available literature must address the complexities in detail. (Since this isn't a book on UML, we're saved from that particular fate.) The complexity can work for your project, but it can also result in massive expenditures of time and effort, particularly in "high ceremony" development environments that produce vast quantities of paper. Most teams find a comfortable middle ground in which modeling, via UML or other methods, serves the team's underlying goals rather than becoming an end in itself.
The UML isn't the only tool that can be misused, of course. Design patterns can also be misapplied, making simple problems more complex than necessary, or encouraging code-first design-later development cycles as developers assume that the presence of patterns (with their promises of easy extensibility and maintenance) allow requirements to be dealt with as they come up.
To be useful, design patterns need to be expressed. While there are a variety of ways to do this, a UML diagram is part of most of them. The diagram can make broad concepts and implementation details clear in a language-independent way that doesn't require working through large chunks of sample code. There are relatively few programming patterns that don't lend themselves to some sort of UML representation.
Coupling modeling with design patterns has a number of advantages. The model provides context for choosing the right design patterns, although the presence of effective design patterns will also, perhaps somewhat recursively, influence the development of the model. In most circumstances, the result is simpler, smaller, more manageable software.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
UML and Software Development Lifecycles
The typical software development process consists of several phases: requirements-gathering, high-level design, low-level design, coding and unit testing, integration testing, and deployment. Different methodologies divide these areas into different categories and subdivisions.
UML provides several diagram types that fit into each section of the Software Development Lifecycle (SDLC). Here's an overview of the diagram types we address in this chapter, in roughly the order they enter the development process:
Use case diagrams
Used through the high-level design phase to identify common sets of activities that users of the system indulge in. Use case diagrams also describe the participants in each use case. Use cases are helpful when developing test plans.
Class diagrams
Used as early as the high-level design process to define the domain model for the application: specifically, the relationship of data objects within the system, the relationships between them, and the operations that they can perform or that can be performed on them.
Interaction diagrams
Sometimes used during the requirements gathering process but particularly used in high- and low-level design to show the interactions between objects in the system. Interaction diagrams are also very helpful in the testing state when creating testing processes and procedures.
Activity diagrams
Used during requirements gathering and high-level design to further identify process flows within the system. Unlike program flow charts, activity diagrams include users and activities beyond the code itself, and allow clear delineation of the roles played by various participants.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Use Case Diagrams
A use case diagram is the highest level of abstraction available in UML. Use cases are collections of related activities that work toward a particular goal. They're part of the requirements gathering process rather than the design process itself, and can be as detailed or as generic as is necessary to communicate system requirements.
The challenge here is often to make the requirements accessible and useful to both the design team and the domain experts involved in the project. Depending on need, the size of the team and the preferences of those leading the development a project might have two or three use cases, or dozens, or even more, although there is generally a point at which the sheer volume of use cases grows unmanageable.
A "Buy Groceries" use case, for example, could consist of selecting food, checking out, processing a credit card, and bagging groceries. The use case can also incorporate internal variations, such as a declined credit card.
Use cases incorporate multiple actors , which may be humans or systems. The actors in the Buy Groceries use case could be the shopper, the checkout person, the grocery bagger, the inventory system, and the credit processor.
Like any UML diagram, use cases exist to convey information. A use case diagram can be a valuable communication tool, and the process of creating the use cases themselves almost invariably leads to better software and greater accountability. In addition to ensuring user needs are met, use cases help determine where a system is likely to need extension in the immediate future—information that can play a valuable part in the design process.
The ultimate consumer of the use case diagram is a human being; as a result, the diagram's goal is clarity, rather than precision. Some developers don't like using use case diagrams at all, preferring text, and some mix-and-match according to the needs of the current project. Use case diagrams at a high level are often used to quickly communicate the scope of a more detailed and nuanced textual discussion. In fact, UML use case diagrams alone are almost, but not quite, useless without the full textual use case sitting behind them.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Class Diagrams
Representing the domain model for an application is arguably the most important area of object-oriented modeling. Class diagrams , as a result, are the most widely used feature of the UML.
Class diagrams show the relationships between classes in a system. Since object-oriented design blurs the line between data objects and executable application logic, a UML class diagram documents both. UML actually supports both class diagrams and object diagrams. If you're reading this book, you're almost certainly familiar with the distinction between a class and an object: a class is a framework containing methods and data that can be instantiated into one or more objects, which contain actual data items. UML class diagrams show the relationship between the frameworks, and object diagrams show the state of a system with actual objects, containing actual values.
Although the names are the same, a class in a UML class diagram does not necessarily map to a Java class—although in more detailed, design-focused UML diagrams, they will. Class diagrams can be used to build conceptual pictures of an application's domain model, which can then be used to develop more specific system designs that eventually map into code.
It's nice to be as complete as possible when developing a class diagram, but it is by no means necessary. Generally, a modeler picks a level of completeness that corresponds with the current stage in the software design process. The highest level class diagrams ignore all private methods and internal data structures, focusing instead on the logical content of the objects.
The diagram in Figure 2-6 illustrates a single class, in fairly high detail. The first compartment of the box holds the class name. The second compartment identifies the fields within the class, and the third compartment includes the methods in the class. In UML terminology, these are referred to as the attributes and operations of the class, respectively. Each of the fields and methods has a visibility modifier: +, -, or # for public, private, and protected. A colon and a variable type to indicate the storage or return types can follow both fields and methods.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Interaction Diagrams
Class diagrams are not well suited to describing program flow. While modelers frequently use class diagrams to model aspects of systems that are oriented towards process instead of data, seeing the internal class relations does not always translate into a clear view of the underlying logic. All modeling systems have some mechanism for indicating the flow of control between multiple objects in the system. In the UML, these diagrams are referred to as interaction diagrams . There are two kinds of interaction diagrams: sequence diagrams and collaboration diagrams.
Sequence diagrams , which show the sequential interactions of a set of objects, are the most common kind of interaction diagram. They can be used for understanding the flow of control within an application (or more often, given the scale of the average enterprise system, within a component of the system).
Sequence diagrams consist of a row of boxes representing objects, generally placed from left to right in order of their appearance within the flow of control. These are the same boxes used for object diagrams with the labels underlined to indicate that they are objects rather than classes. A lifeline stretches down from each object. The lifeline begins when the object is created and ends when the object is removed. If the object is never removed, the lifeline continues to the bottom of the page.
Each lifeline contains one or more activation boxes. Activation boxes show that the object is actively processing, or blocking, while waiting for a response from further down the line. When modeling classes directly, activation boxes correspond to the duration of a method invocation on the object.
Figure 2-15 shows how an object representing the signup process for our hypothetical web site would go about creating a user account, associating it with a partner object, and requesting account approval from a manager. The sequence begins by calling a method on the web site signup object, which creates a user account. The newly created user account determines the level of access it is authorized to have, checking a partnership list and creating a new partner object if none is found. Creating a new partner kicks off an asynchronous request for a manager to review the new account.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Activity Diagrams
Activity diagrams look like regular flow charts, and it's tempting to assume that's what they are. That would be a mistake: activity diagrams are both more and less than a regular flow chart. They're more flexible, since they provide clear support for parallel applications and for dividing different areas of the chart into different areas of responsibility. On the other hand, this flexibility means that it's harder to translate an activity diagram directly into program flow. Instead, an activity diagram is usually used to describe a use case in detail.
Activity diagrams, like most things, begin with a starting point, in this case drawn as a black circle. Diagrams move down the page, and an arrow connects each component. Activities are shown as lozenges. Decision points are shown as diamonds, and the conditions leading to each potential decision are shown as curly braces.
Activity diagrams support parallel operations via thick horizontal bars. Once an activity enters a bar, multiple activities can leave the bar. Another bar can be used to merge the activity once all the branches have been completed. It is not absolutely necessary to merge all of the parallel operations, but if you don't, each activity should be brought to a separate, explicit conclusion.
Finally, the end of the activity is shown as a black circle surrounded by a white circle. Figure 2-17 shows a complete activity diagram.
Figure 2-17: Activity diagram
The diagram in Figure 2-17 is divided into three parts, although this step is not required. Each of the parts is called a swimlane . A swimlane allows an activity diagram to be portioned according to the actor responsible for each section of the activity. In this case, the partner is responsible for requesting an account and logging in; the web site is responsible for creating accounts, forwarding approval requests, and emailing login information; and a manager is responsible for evaluating the request and deciding whether to enable partner access (if the manager decides not to, the web site emails the user with information about a generic account instead).
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Deployment Diagrams
The final UML diagram type (for our purposes) is the deployment diagram . Deployment diagrams are used to show how individual components are distributed across multiple systems, and how those components interact. Servers are represented as three-dimensional boxes, and individual components are represented as rectangles with two smaller rectangles attached to the left side.
There are a few key types of associations possible within deployment diagrams. For example, lines between servers specify how components installed on each server communicate with each other.
Components can be connected via solid arrows to indicate persistent relationships. In Figure 2-18, the Swing Application and the Client Façade are joined at compile time. Dashed arrows indicate runtime connections such as those between the Client Façade and an EJB Session Bean, and between the session bean and a set of entity beans.
Figure 2-18: Deployment diagram
The entity bean in the diagram is shown in the stacked style to indicate that the system contains more than one instance of it.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: Presentation Tier Architecture
All applications eventually evolve into operating systems.
We begin our discussion of enterprise applications with the presentation tier and a single question: how can we manage the evolution of our software? There's no simple answer. To successfully maintain software over the long term, we must create an architecture that allows the programmer to extend and rearrange the underlying components. The architecture needs to balance flexibility, extensibility, and performance.
In J2EE, the server-side presentation tier is responsible for most of an application's complexity. Since the presentation tier controls the user experience (and, by extension, the bulk of an application's feature set), it's often a magnet for change requests. The servlets, HTML, JSP pages, and other resources that make up the presentation tier tend to evolve like most pieces of complex software. User requests spawn new features, and new features create new code.
Since we know that software evolves, we can make our lives easier by planning for it up front. The extensibility of software determines how easily changes can be accommodated. Does a word processor need to be rewritten to add an email client? What about adding a spellchecker? An extensible program changes gracefully: adding and updating features has minimal impact on the rest of the program.
We sometimes know what features we'll be adding tomorrow, next week, next revision, and next year, and so we can create a software architecture with those directions in mind. But much of the time this foreknowledge is unavailable—or it turns out to be wrong. If we're going to assume that any application we build will be in use more than six months in the future, we need to bake extensibility right into the underlying architecture.
In this chapter, we look at patterns that affect the overall design of the presentation tier with an eye toward keeping it extensible. Here are the patterns we will discuss:
Model-View-Controller pattern
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Server-Side Presentation Tier
Most developers think of a web browser as the presentation layer of J2EE. But the web browser isn't the whole story. J2EE supports many ways for users to connect: web browser, WAP-enabled phone, or web service, to name a few. These mechanisms have two things in common:
  • They have a client-server model.
    The clients, such as web browsers and cell phones, share the work of generating a presentation with a server. The server holds the server-side presentation tier, which typically generates marked-up data like HTML or XML. The client interprets the marked-up data and presents it to the user.
  • They are request-based.
    When the user wants to do anything, the client initiates a request to the server, and then waits for a response. The server processes the request and generates the appropriate response.
Enterprise developers usually don't have control over the client. Concentrate on what we can control: the server-side presentation tier. Figure 3-1 shows a typical setup for a server-side presentation tier in a web-based environment. The server is like a hotel's front desk, waiting for clients to call with questions (in the form of HTTP requests). The web server acts like a highly caffeinated receptionist, answering basic questions itself and directing complex requests to other interfaces—like the concierge or the plumber.
Figure 3-1: Components of the server-side presentation tier
Web servers support a number of interfaces for generating responses. For requests that always yield the same result, the server can create a response directly from a static HTML file. This option is obviously the most efficient, but it is the least flexible: any dynamic data must be generated by a plug-in to the web server itself. For more complicated requests, the server may use the CGI interface to execute an external program and use its results as the response. This method is more flexible, since programs can be added, removed, or changed without stopping the server. Unfortunately, it is inefficient, since it requires a new process for each request. The final option—the one implemented in J2EE—is to keep another process around that is always running. The web server can forward requests to this process at any time, without having to worry about startup costs. In J2EE, this process is the servlet container.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Application Structure
It's never easy to start with a clean slate. Whether your application is big or small, the decisions you make at the beginning of the design process affect the application's entire lifetime. Wouldn't it be nice if there was a design pattern to define the overall shape of the presentation tier? The Model-View-Controller (MVC) pattern is just such a pattern.
As its name suggests, MVC breaks the problem of user interfaces into three distinct pieces: model, view, and controller. The model stores the application's state. A view interprets data in the model and presents it to the user. Finally, the controller processes user input, and either updates the model or displays a new view. By carefully dividing labor and controlling communication between these three pieces, we can achieve a robust, extensible architecture for the user interface and the application as a whole.
Figure 3-2 gives an overview of the communications within the MVC architecture. While MVC was originally designed for graphical environments—in which the user acts directly via a mouse or keyboard—over time, it has been adapted for use in other areas of programming.
Figure 3-2: Overview of the MVC pattern
The MVC paradigm extends quite naturally to enterprise software, where the "user" may be a web browser or web server. Since the presentation tier is request-driven, the "user" can be any request originator. A controller (for example, a web server) handles the request. The model, then, is the business data, and the view is the response that is finally generated. An MVC application is made up of a set of models, views, and controllers that handle related requests.
A controller is the first point of contact for a request. Its job is to coordinate request handling, turning user input into model updates and views. The controller acts as a supervisor, planning what changes need to be made and what view needs to be shown, then calling the chosen model and view to execute the actual plan. An application may have multiple controllers, each responsible for a certain area of the application. By coordinating the response to the user's requests, controllers manage the overall flow of the application.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Building a Central Controller
As an architecture, MVC is a good start and suitable for many applications. But sometimes more advanced processing is called for, so it's time to start filling in the holes in the MVC architecture, starting with the controller. Not only is the controller the first point of contact for requests, it is also the place where we have the most programming and design freedom. Unlike the model, which is shaped by the underlying data, the controller is designed from start to finish as a part of the presentation tier. And unlike the view, which is stateless and generally focused on presentation, we are free to perform as much complicated logic as we like.
The MVC pattern does not specify how many controllers there should be. In our previous example, we built a controller that handled the input from a single screen. In order to extend our application along those same lines, we would have to add new screens and an equal number of new controllers. Alternatively, we could build a single, omniscient controller. This controller would know about every possible request and how to generate a response to each.
Since we are interested in building extensible software, it's obvious that neither of these solutions is exactly right. Building an entire new controller for each screen does not just mean lots of classes, it makes the application harder to extend. If we wanted to build a logging mechanism, for example, we would have to add logging code to each controller separately. In fact, there are lots of things we might need to do for every request: implement navigation, maintain session information, and gather statistics, to name a few. Adding each of these functions to a mix of static HTML, JSP, and servlets is a time-consuming and error-prone process.
We encounter similar problems when using a single controller. While it would be easy to add common functions, the controller must also contain the specific functions for each page. This situation is not very extensible. To add new functionality, we probably need to recompile and redeploy the entire controller (and retest, and redocument . . . ).
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 4: Advanced Presentation Tier Design
The patterns discussed in the previous chapter give a broad framework for the presentation tier. Now it's time to dive into the details. This chapter looks at more advanced design patterns for the presentation tier, with a specific question in mind: how can we build reusable components?
Reusable components are something of a holy grail for programmers. Many developers and companies believe that programs, like cars or computers, ideally should be assembled from off-the-shelf pieces. There are many advantages to a component-based development model. Shorter development time is one. Lower price is another: reusing a component spreads the development costs out over time, and also reduces the risk of bugs, since the component has already proved itself in previous uses. When the risk of bugs is lowered, testing can focus on new code, further reducing costs and time.
With all these advantages, why aren't more developers building applications from off-the-shelf components? One explanation is economic: so far, no one has been able to build a successful business model around software components. But more often than not, the reason is that designing reusable components takes time and effort. Reusable components require up-front design, not after-the-fact retrofitting. The interfaces and dependencies for each component must be clearly defined. Interfaces must be kept simple and dependencies to the bare minimum. A component with clear, simple interfaces is easy to test, replace, repurpose, and reuse.
This chapter focuses on patterns for breaking the presentation tier down into small, reusable components. We will look at these patterns:
Service to Worker
Describes how to divide a controller into reusable navigation and actions.
View Helper
Shows how to encapsulate related functions in a view into a single, reusable object.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Reuse in Web Applications
Reusability is a driving factor in object-oriented languages, including Java. Java is full of built-in frameworks meant to promote reuse. JavaBeans provides an explicit component model, while Swing and AWT both provide reusable widget libraries. Throughout the software industry, the adoption of object-oriented programming languages in the 90s led to across-the-board development of a number of frameworks for reuse.
Sadly, there is no standard component framework in the relatively young field of J2EE web applications. While the JavaBeans component model is often used to communicate model data, there is no universal mechanism for reusing parts of views or controllers. Until quite recently, web application developers have had to build their own framework or settle for applications that were not intrinsically reusable. This problem has been recently recognized, and frameworks such as Struts have gained popularity as a solution (see the sidebar "Frameworks and Patterns"). These frameworks allow controllers and views to be developed using standard interfaces and connected in standard ways.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Extending the Controller
In the last chapter, we discussed how to best add features to a front controller. The main problem was that adding new features directly to the controller specializes it. A specialized controller quickly becomes unwieldy, with lots of code for each specific page. Every time we wanted new functionality, we had to rebuild, retest, and redeploy the entire controller. To solve this problem, we looked at the Decorator pattern, which allowed us to dynamically add functionality to the front controller.
With decorators in place, common functions such as security are separated from the front controller. But the controller is still responsible for managing page navigation as well as instantiating actions. Since the set of pages and the set of available actions are likely to change, a tight coupling between them and the front controller is a bad idea. Similar to decorators, we would like to separate the specialized bits of the code for actions and navigation into their own classes and leave the major objects—like the front controller—to act as frameworks.
Here's an example. Imagine a simple, multistep form, such as an online mortgage application. If each page in the form contained embedded references to the next page, it would be very difficult to change the page order. Each affected page, as well as those that came before and after it, would need to be modified to reflect the change. Even worse, these pages could not be reused. If the first page of our mortgage application took basic name and address information, we might want to use the same view in a credit card application. Unfortunately, we can't, since the first mortgage page contains a reference to the second mortgage page: there's no way to direct it to the credit card application instead. This inflexibility stems from the tight coupling of views to controllers.
To build a flexible application, we need to decouple the pages and controllers. We need the flexibility to add, remove, and reorder pages, reusing them at will. Ideally, we want to do all of this at runtime, while preserving the view-controller separation. The Service to Worker pattern can help us.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Advanced Views
Until now, we have treated the view as a black box, assuming that a single JSP page will convert our model data into HTML to send to the user. In reality, this is a tall order. Large JSP pages with lots of embedded code are just as unwieldy as a large front controller. Like the controller, we would like to break the view into a generic framework with the specialized pieces separated out.
The challenge is to break up the view within the restricted programming model of JSP. Remember that one of our goals was to minimize embedding code in our JSP pages, since it blurs the line between the view and the controller. Thus, we will avoid the kinds of classes and interfaces we used to solve the same problem in the controller.
Fortunately, JSP gives us a different set of tools to work with: JSP directives and custom tags. We will use both of these extensively in the next two patterns to help separate the view into reusable components.
One mechanism for reducing specialization in views is a view helper . A view helper acts as an intermediary between the model and the view. It reads specific business data and translates it, sometimes directly into HTML, and sometimes into an intermediate data model. Instead of the view containing specialized code to deal with a particular model, the view includes more generic calls to the helper. Figure 4-4 shows how a view uses helpers.
Figure 4-4: A view using helpers
View helpers increase reusability in two ways: by reducing the amount of specialized code in a view, helpers make views more reusable; and, since a helper encapsulates a specific kind of interaction with the model, helpers can be reused themselves.
When you think about view helpers in JSP, custom tags should immediately pop to mind. Conceptually, custom tags fit the bill—they adapt Java objects into JSP markup. Moving code embedded in JSP into custom tag classes reduces coupling, since the tag defines a clear interface independent of the underlying objects. And since tags are grouped into libraries by function, they are inherently quite reusable themselves. While it is easy to think of all custom tags (or even all tags) as view helpers, they are not the same thing. A view helper is a tag, or set of tags, that translates model data into a convenient form for the view.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 5: Presentation Tier Scalability
Many developers believe design patterns and scalability do not go hand in hand. They argue that patterns add layers to an application, so the server must perform more operations and use more memory to handle each request. The extra operations slow response time down, and the increase in memory means fewer clients can be supported per server. By itself, this is a fair assessment, and if no two requests were alike, it might be the end of the story.
In an enterprise application, however, many clients need to access similar data. On a site that publishes stock quotes, for example, the server may respond to thousands of requests a minute for the same stock. If the price of the stock changes every five minutes, it would be massively inefficient to contact the stock market for each request. Even in an online bank, where every user wants to view personal data, resources such as database connections do not need to be recreated for every request.
Often, we can sacrifice some speed up front for better performance in the average case. While the first request for a particular stock quote or the first connection to a particular database might require a lot of work, subsequent requests will be much faster. It is fair to say the system's scalability will increase: we can support more requests in the same amount of time.
In this chapter, we look at three patterns that increase the scalability of the presentation tier using variations of this concept:
Asynchronous Page
Shows how to cache data, such as stock prices, and use it to generate dynamic pages.
Caching Filter
Can be used to cache entire dynamic pages after they are generated.
Resource Pool
Describes how to create a "pool" of large or expensive objects that can be loaned out as needed, saving instantiation costs.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Scalability and Bottlenecks
Before we jump into the patterns, let's take a minute to discuss what we mean by a scalable system. Think of a web-based system as a request processor. Requests come in from the clients, and the clients wait until results are generated. Everything in between—whether it's simply returning the contents of a static file or generating a fully dynamic page—is the actual processing.
For a request processor, scalability is related to the number of requests that can be processed simultaneously. In a simple sense, scalability might be the ability to "survive" a certain number of hits at the same time, eventually delivering a proper response to each one, but we know from experience that this is not really the case. If a news site gets 10,000 simultaneous hits and responds to each of them within 3 seconds, we might say the site scales adequately, if not exceptionally. But if the same site gets 100,000 simultaneous hits, responding to each one within three minutes would not be acceptable.
A better definition of scalability is a system's ability to grow in order to handle increased demand. Obviously, no single server can be expected to handle an infinite number of requests. In a scalable system, you have options when a single server has reached its maximum capacity. In general, you can:
  • Buy a faster server
  • Buy more servers
While it may seem obvious that a faster server can handle more requests, it is not always the case. Imagine a bank that stores its total assets in a single record, which must be updated every time money is deposited or withdrawn. If the record can only be updated by one request at a time, the maximum number of transactions will be limited by the time it takes to write this record. Increasing the speed of the server's CPUs might help a little, since the asset total could possibly be updated faster. But the overhead of allowing multiple CPUs to communicate—more processes contending for access to the single resource—could mean that adding CPUs actually decreases overall speed! A single point that limits the scalability of the entire application (like our total asset record) is known as a
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Content Caching
Content preview·Buy PDF of this chapter|