JSPs or Servlets--Which Architecture is Right for You?
SINCE THE INTRODUCTION of JSP technology, two architectures have emerged for building server-side Web applications in Java. The first involves JSPs only, and the second uses JSPs and Servlets together. Referred to as Model 1 and Model 2 architectures, respectively, each model has its advantages and disadvantages. The Model 2 architecture has become quite popular recently, and has received a great deal of coverage on the Web and in trade magazines. In fact, many developers mistakenly believe this architecture has replaced the Model 1 architecture and is the "right" way to use JSPs.
The trick is to base your decision on what makes the most sense within the context of your current project, not on what seems the most popular at the time. In this article, I'll describe the two architectures, outline the trade-offs, and pass on some rules of thumb to help you decide which technique is right for you.
JSPs provide an elegant solution to the problem many old CGI programs and early Servlet applications faced: Where do you put the HTML? Rendering your HTML via System.out.println() is too inflexible for almost all Web systems, since changes to the HTML require recompilation. JSPs reverse this by allowing the developer to embed Java in HTML instead of HTML in Java. Changes to the HTML don't require the developer to recompile (although this may happen under the covers of the Web server).
The most straightforward way to use JSPs is to treat them as dynamic HTML pages with embedded Java and some special tags. This approach is used in other Web environments such as Active Server Pages (ASPs) and ColdFusion. For many smaller apps, this approach will work fine; however, as your app evolves and your pages increase in complexity, they can quickly become unmanageable.
While this is the simplest approach, most JSP developers find their applications can quickly outgrow this approach. In many current implementations, JSPs are also more difficult to debug than straight Java code. Most Web servers will internally generate JSPs into Servlets and then compile this auto-generated code. If your JSP contains syntactic or other errors, this can result in a failed compilation and a long list of useless compiler errors referring to the auto-generated code.
As implementations improve, we can expect them to do additional parsing of the JSPs and to provide better debugging information to developers. For this reason alone, it is best to strive to minimize the amount of Java code in your JSPs. However, regardless of this debugging issue, it is generally a better design practice to separate your page layout from your business logic as cleanly as possible.
Most JSP developers use a combination of JSPs and JavaBeans to accomplish this separation of business logic from presentation—this is what has been dubbed the Model 1 JSP architecture (see Figure 1). The JavaBeans can represent Data Objects or Business Objects. Moving the majority of Java code outside of the JSP and into JavaBeans can overcome most of the problems just outlined and is recommended for all but the simplest applications. This ensures your code is easy to debug, easy to test, and easy to manage as the application evolves.
Figure 1. Model 1 JSP architecture.
To further separate out the view from the model, view-specific objects can be employed to encapsulate most of the display logic and formatting for the JSP. These view-specific objects in turn talk to the domain model. This is particularly useful for complex JSPs that require tabular data to be formatted in a particular way. There are many variations of this. In the case of a multitier Web application that talks to EJBs on the back end, it is often useful to have a layer of wrapper beans around the EJBs to handle RemoteExceptions and other exceptions on behalf of the JSP.
MVC Using JSPs and Servlets
When JSPs first came out, they were viewed as a direct replacement for Microsoft's ASPs. As developers began to explore the new features JSP technology offered, it became obvious that JSPs offered a greater degree of flexibility than ASPs. Because of their integration with Servlets, it became possible to use JSPs as templates while still utilizing Servlets to control the application.
The Model 2 architecture is based roughly on the Model-View-Controller (MVC) design pattern (see Figure 2). The Servlet acts as the controller, taking in requests and, based on the request information, dispatching them to the appropriate JSP in order to render the response. The Servlet controller also instantiates any JavaBeans needed by the JSP view and exports them to the JSP environment. The JSP view can then use the data in the JavaBeans (the model) by either directly calling the methods or by using the USEBEAN custom tags. The Model 2 architecture is described in detail in Sun's Blueprints for J2EE.
Figure 2. Model 2 JSP architecture.
There are a number of variations on the Model 2 architecture. Most developers need to first decide whether to use a single Servlet for their whole application or multiple Servlets.
In the extreme case of a single Servlet for an entire application, the role of that Servlet is usually that of a gatekeeper and re-director. The gatekeeper functionality provides common services, such as authentication, authorization, login, error handling, and so on. The re-direction is the controller aspect of the Servlet. Based on the user input or the referrer, it can act as a state machine or an event dispatcher to decide upon the appropriate class to handle the request. This approach has the advantage of a central point of control for common services, but runs the risk of growing (and growing and growing) as the application evolves.
On the other end of the spectrum you can have a Servlet for every business function, use case, or use-case scenario (depending on the granularity of your use cases). When using this method, it is often useful to have a base Servlet that all business function Servlets inherit from that handles basic infrastructure services, such as authorization and logging. Each Servlet then provides just the logic necessary to get its business function job done.
The decision of whether to go with one master Servlet, one Servlet per use case or business function, or a balance between the two is most often based on what granularity of service is meaningful within your application. For some applications, every database read and write is a distinct, discrete unit that is reusable and meaningful. For others, subsystems provide the most logical break up. In other projects, use cases and use-case scenarios can provide guidance on how to model your Servlets.
Both the Model 1 and 2 architectures have their good points and bad points. The straight JSP approach tends to be most intuitive to Web developers and designers; however, it can encourage spaghetti JSP pages that bloat as more and more code is added to them. Often, this code is business logic that gets lost in the display pages and would be better off in domain objects. Software engineers often prefer the MVC-like Model 2 due to the decoupling of the Web interface from the domain model. The danger here is that this elegance can be more perceived than actual if flow logic that is hard-coded in the Servlets is something Web designers may change often.
While Model 1 may be more straightforward, JSPs are harder to debug in most current implementations. By relying on them to handle requests, it is all or nothing. With a Servlet controller, most of the business logic can be debugged through the Servlet before it is passed in JavaBeans to the JSP.
Whether you use the Model 1 or Model 2 architecture, there are practices that can be of benefit to you. The first is to factor out any business logic (and sometimes display logic) from both JSPs and Servlets. This is better from the point of view of reusability, maintainability, and unit testing. The last one is particularly important. It can be quite difficult to unit test JSPs and Servlets. By factoring out the business logic into business objects and the complex display logic into view objects, it is far easier to write and run unit tests to aid in functional and regression testing.
How Do I Decide?
The trade-offs between the two architectures are endless. But there are some useful rules of thumb and concepts to help you decide which architecture best fits your needs. Too often, we make generalizations about what is a "best practice" without taking into account that every project has its own challenges. An important part of all design and architectural patterns is the idea of context. Only you can decide what is the best solution in the context of your project.
The most obvious difference between the two architectures is that Model 1 is page-centric, while Model 2 is programming-centric. If you are developing a typical Web application that links from page to page, Model 1 may be the best approach. On the other hand, if each link or button click requires a great deal of processing and decision-making about what should be displayed next, the Servlet/JSP MVC approach is probably better for your needs.
An alternative way to look at it is whether the flow of your application is request- or response-oriented. Servlets are request-oriented, while JSPs are more response-oriented because the JSP page models the HTML response document sent to the browser. If you have far more HTML than Java (or at least very little decision-making about what document to display based on user input), then Model 1 may be a more natural fit.
One tip-off is to look at the mapping between requests and responses. If each request maps to one and only one response in the vast majority of cases, than a Servlet may not add any value to the situation. Sun's J2EE Blueprints describes the role of the Servlet controller as: "Based on the user gesture [request] and the outcome of the model commands, the controller selects a view to be rendered as part of the response to the user request." If the mapping between requests and responses is 1-to-1, there is little need for a controller.
On the other hand, if each request spawns a great deal of logic and a variety of different views can result, a Servlet is ideal for making this decision and then redirecting to the appropriate view. This can be particularly important if your application needs to support multiple, different display formats, such as HTML and XML over the same HTTP channel. The Servlet can contain the logic that determines what the client is and, based on that, can return a different document format or mime type to the client.
It is also worth considering the skill set of the developers who will maintain the application. For example, if the application will be maintained primarily by Web designers and developers as opposed to software engineers, this may be the overriding factor in your architectural decisions. There is little point in having an elegant, ultra-flexible, highly decoupled, uber-architected work of art that can't be maintained by the people who will inherit it.
Finally, remember that the decision between Model 1 and Model 2 does have to be exclusive. If it fits your needs, you can always use the Model 1 JSP-only architecture for the page-centric parts of your application and Servlet/JSPs for the areas that require more processing or decision-making. The only consideration when using a combination of the two models is the importance of architectural consistency within your project or organization. If this is not a major issue, whether it is due to the politics of the situation, the skill set of the developers, or the ability to segment the application into different functional areas, then there is no technical reason the two architectures cannot co-exist.
As a side note, it is useful to think carefully about how you name your URLs and what they point to. Often, the "One Master Servlet" approach leads to less-meaningful URLs. The simple idea that you can name any resource on the entire Internet with a single string is quite profound. In fact, it has been claimed this concept is the real genius behind the Web. Ensuring your URLs actually point to meaningful resources can pay long-term dividends when it comes to maintaining and evolving the application.
Imagine how much simpler your job would be if the developers before you had segmented and designed their applications so that URLs pointed to meaningful resources. Decoding long strings of parameters sent to a CGI program is not unlike working with a legacy procedural system that has one very big, very complex function call that takes an enormous list of parameters and has a wide array of side effects.
Neither architecture is overly difficult to learn or work with; however, picking the wrong one can lead to an inflexible, difficult-to-maintain application. It is important to think through the trade-offs involved and apply them to your own development effort and project needs. When in doubt, give each one a try on a simple project to gain a better understanding. It is easy in the software development field to simply follow the latest trends—even if they may not be the most optimal solution. Learning different solutions and thinking for yourself about whether they fit in the context of your application is the best antidote.