Understanding JavaServer Pages Model 2 architecture Exploring

advertisement
Understanding JavaServer Pages Model 2
architecture
Exploring the MVC design pattern
By Govind Seshadri, JavaWorld.com, 12/29/99
Despite its relatively recent introduction, JavaServer Pages (JSP)
technology is well on its way to becoming the preeminent Java
technology for building applications that serve dynamic Web content.
Java developers love JSP for myriad reasons. Some like the fact that
it brings the "write once, run anywhere" paradigm to interactive Web
pages; others appreciate the fact that it is fairly simple to learn and
lets them wield Java as a server-side scripting language. But most
concur on one thing -- the biggest advantage of using JSP is that it
helps effectively separate presentation from content. In this article, I
provide an in-depth look at how you can gain optimal separation of
presentation from content by using the JSP Model 2 architecture. This
model can also be seen as a server-side implementation of the
popular Model-View-Controller (MVC) design pattern. Please note that
you should be familiar with the basics of JSP and servlet programming
before continuing on, as I do not address any syntax issues in this
article.
Server-Side Java: Read the whole series!

Welcome to the server-side Java series

Create forward-compatible beans in EJB, Part 1

Understanding JavaServer Pages Model 2 Architecture
So, what's wrong with servlets?
While JSP may be great for serving up dynamic Web content and
separating content from presentation, some may still wonder why
servlets should be cast aside for JSP. The utility of servlets is not in
question. They are excellent for server-side processing, and, with
their significant installed base, are here to stay. In fact,
architecturally speaking, you can view JSP as a high-level abstraction
of servlets that is implemented as an extension of the Servlet 2.1 API.
Still, you shouldn't use servlets indiscriminately; they may not be
appropriate for everyone. For instance, while page designers can
easily write a JSP page using conventional HTML or XML tools, servlets
are more suited for back-end developers because they are often
written using an IDE -- a process that generally requires a higher level
of programming expertise. When deploying servlets, even developers
have to be careful and ensure that there is no tight coupling between
presentation and content. You can usually do this by adding a
third-party HTML wrapper package like htmlKona to the mix. But even
this approach, though providing some flexibility with simple screen
changes, still does not shield you from a change in the presentation
format itself. For example, if your presentation changed from HTML to
DHTML, you would still need to ensure that wrapper packages were
compliant with the new format. In a worst-case scenario, if a wrapper
package is not available, you may end up hardcoding the presentation
within the dynamic content. So, what is the solution? As you shall
soon see, one approach would be to use both JSP and servlet
technologies for building application systems.
Differing philosophies
The early JSP specifications advocated two philosophical approaches
for building applications using JSP technology. These approaches,
termed the JSP Model 1 and Model 2 architectures, differ essentially in
the location at which the bulk of the request processing was
performed. In the Model 1 architecture, shown in Figure 1, the JSP
page alone is responsible for processing the incoming request and
replying back to the client. There is still separation of presentation
from content, because all data access is performed using beans.
Although the Model 1 architecture should be perfectly suitable for
simple applications, it may not be desirable for complex
implementations. Indiscriminate usage of this architecture usually
leads to a significant amount of scriptlets or Java code embedded
within the JSP page, especially if there is a significant amount of
request processing to be performed. While this may not seem to be
much of a problem for Java developers, it is certainly an issue if your
JSP pages are created and maintained by designers -- which is usually
the norm on large projects. Ultimately, it may even lead to an unclear
definition of roles and allocation of responsibilities, causing easily
avoidable project-management headaches.
Figure 1: JSP Model 1 architecture
The Model 2 architecture, shown in Figure 2, is a hybrid approach for
serving dynamic content, since it combines the use of both servlets
and JSP. It takes advantage of the predominant strengths of both
technologies, using JSP to generate the presentation layer and
servlets to perform process-intensive tasks. Here, the servlet acts as
the controller and is in charge of the request processing and the
creation of any beans or objects used by the JSP, as well as deciding,
depending on the user's actions, which JSP page to forward the
request to. Note particularly that there is no processing logic within
the JSP page itself; it is simply responsible for retrieving any objects
or beans that may have been previously created by the servlet, and
extracting the dynamic content from that servlet for insertion within
static templates. In my opinion, this approach typically results in the
cleanest separation of presentation from content, leading to clear
delineation of the roles and responsibilities of the developers and
page designers on your programming team. In fact, the more
complex your application, the greater the benefits of using the Model
2 architecture should be.
Figure 2: JSP Model 2 architecture
In order to clarify the concepts behind the Model 2 architecture, let's
walk through a detailed implementation of it: a sample online music
store called Music Without Borders.
SUBHEAD_BREAK: Understanding Music Without Borders The main
view, or presentation, for our Music Without Borders online store is
facilitated by the JSP page EShop.jsp (shown in Listing 1). You will
notice that the page deals almost exclusively with presenting the main
user interface of the application to the client, and performs no
processing whatsoever -- an optimal JSP scenario. Also, notice that
another JSP page, Cart.jsp (shown in Listing 2), is included within
EShop.jsp via the directive <jsp:include page="Cart.jsp" flush="true" />.
Listing 1:
EShop.jsp
<%@ page session="true" %>
<html>
<head>
<title>Music Without Borders</title>
</head>
<body bgcolor="#33CCFF">
<font face="Times New Roman,Times" size="+3">
Music Without Borders
</font>
<hr><p>
<center>
<form name="shoppingForm"
action="/examples/servlet/ShoppingServlet"
method="POST">
<b>CD:</b>
<select name=CD>
<option>Yuan | The Guo Brothers | China | 4.95</option>
<option>Drums of Passion | Babatunde Olatunji | Nigeria |
6.95</option>
<option>Kaira | Tounami Diabate| Mali | 6.95</option>
<option>The Lion is Loose | Eliades Ochoa | Cuba | 3.95</option>
<option>Dance the Devil Away | Outback | Australia |
4.95</option>
<option>Record of Changes | Samulnori | Korea | 2.95</option>
<option>Djelika | Tounami Diabate | Mali | 4.95</option>
<option>Rapture | Nusrat Fateh Ali Khan | Pakistan |
2.95</option>
<option>Cesaria Evora | Cesaria Evora | Cape Verde |
6.95</option>
<option>Ibuki | Kodo | Japan | 3.95</option>
</select>
<b>Quantity: </b><input type="text" name="qty" SIZE="3"
value=1>
<input type="hidden" name="action" value="ADD">
<input type="submit" name="Submit" value="Add to Cart">
</form>
</center>
<p>
<jsp:include page="Cart.jsp" flush="true" />
</body>
</html>
Listing 2:
Cart.jsp
<%@ page session="true" import="java.util.*, shopping.CD" %>
<%
Vector buylist = (Vector)
session.getValue("shopping.shoppingcart");
if (buylist != null && (buylist.size() > 0)) {
%>
<center>
<table border="0" cellpadding="0" width="100%"
bgcolor="#FFFFFF">
<tr>
<td><b>ALBUM</b></td>
<td><b>ARTIST</b></td>
<td><b>COUNTRY</b></td>
<td><b>PRICE</b></td>
<td><b>QUANTITY</b></td>
<td></td>
</tr>
<%
for (int index=0; index < buylist.size();index++) {
CD anOrder = (CD) buylist.elementAt(index);
%>
<tr>
<td><b><%= anOrder.getAlbum() %></b></td>
<td><b><%= anOrder.getArtist() %></b></td>
<td><b><%= anOrder.getCountry() %></b></td>
<td><b><%= anOrder.getPrice() %></b></td>
<td><b><%= anOrder.getQuantity() %></b></td>
<td>
<form name="deleteForm"
action="/examples/servlet/ShoppingServlet"
method="POST">
<input type="submit" value="Delete">
<input type="hidden" name= "delindex" value='<%=
index %>'>
<input type="hidden" name="action" value="DELETE">
</form>
</td>
</tr>
<% } %>
</table>
<p>
<form name="checkoutForm"
action="/examples/servlet/ShoppingServlet"
method="POST">
<input type="hidden" name="action" value="CHECKOUT">
<input type="submit" name="Checkout" value="Checkout">
</form>
</center>
<% } %>
Here, Cart.jsp handles the presentation of the session-based
shopping cart, which constitutes the model in our MVC architecture.
Observe the scriptlet at the beginning of Cart.jsp:
<%
Vector buylist = (Vector)
session.getValue("shopping.shoppingcart");
if (buylist != null && (buylist.size() > 0)) {
%>
Basically, the scriptlet extracts the shopping cart from the session. If
the cart is empty or not yet created, it displays nothing; thus, the first
time a user accesses the application, she is presented with the view
shown in Figure 3.
Figure 3: Music Without Borders, main view
If the shopping cart is not empty, then the selected items are
extracted from the cart one at a time, as demonstrated by the
following scriptlet:
<%
for (int index=0; index < buylist.size(); index++) {
CD anOrder = (CD) buylist.elementAt(index);
%>
Once the variables describing an item have been created, they are
then simply inserted into the static HTML template using JSP
expressions. Figure 4 shows the application view after the user has
placed some items in the shopping cart.
Figure 4: Music Without Borders, shopping cart view
The important thing to observe here is that the processing for all
actions carried out within either Eshop.jsp or Cart.jsp is handled by the
controller servlet, ShoppingServlet.java, which is shown in Listing 3.
Listing 3:
ShoppingServlet.java
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import shopping.CD;
public class ShoppingServlet extends HttpServlet {
public void init(ServletConfig conf) throws ServletException {
super.init(conf);
}
public void doPost (HttpServletRequest req, HttpServletResponse
res)
throws ServletException, IOException {
HttpSession session = req.getSession(false);
if (session == null) {
res.sendRedirect("http://localhost:8080/error.html");
}
Vector buylist=
(Vector)session.getValue("shopping.shoppingcart");
String action = req.getParameter("action");
if (!action.equals("CHECKOUT")) {
if (action.equals("DELETE")) {
String del = req.getParameter("delindex");
int d = (new Integer(del)).intValue();
buylist.removeElementAt(d);
} else if (action.equals("ADD")) {
//any previous buys of same cd?
boolean match=false;
CD aCD = getCD(req);
if (buylist==null) {
//add first cd to the cart
buylist = new Vector(); //first order
buylist.addElement(aCD);
} else { // not first buy
for (int i=0; i< buylist.size(); i++) {
CD cd = (CD) buylist.elementAt(i);
if (cd.getAlbum().equals(aCD.getAlbum())) {
cd.setQuantity(cd.getQuantity()+aCD.getQuantity());
buylist.setElementAt(cd,i);
match = true;
} //end of if name matches
} // end of for
if (!match)
buylist.addElement(aCD);
}
}
session.putValue("shopping.shoppingcart", buylist);
String url="/jsp/shopping/EShop.jsp";
ServletContext sc = getServletContext();
RequestDispatcher rd = sc.getRequestDispatcher(url);
rd.forward(req, res);
} else if (action.equals("CHECKOUT")) {
float total =0;
for (int i=0; i< buylist.size();i++) {
CD anOrder = (CD) buylist.elementAt(i);
float price= anOrder.getPrice();
int qty = anOrder.getQuantity();
total += (price * qty);
}
total += 0.005;
String amount = new Float(total).toString();
int n = amount.indexOf('.');
amount = amount.substring(0,n+3);
req.setAttribute("amount",amount);
String url="/jsp/shopping/Checkout.jsp";
ServletContext sc = getServletContext();
RequestDispatcher rd = sc.getRequestDispatcher(url);
rd.forward(req,res);
}
}
private CD getCD(HttpServletRequest req) {
//imagine if all this was in a scriptlet...ugly, eh?
String myCd = req.getParameter("CD");
String qty = req.getParameter("qty");
StringTokenizer t = new StringTokenizer(myCd,"|");
String album= t.nextToken();
String artist = t.nextToken();
String country = t.nextToken();
String price = t.nextToken();
price = price.replace('$',' ').trim();
CD cd = new CD();
cd.setAlbum(album);
cd.setArtist(artist);
cd.setCountry(country);
cd.setPrice((new Float(price)).floatValue());
cd.setQuantity((new Integer(qty)).intValue());
return cd;
}
}
Every time the user adds an item within EShop.jsp, the request is
posted to the controller servlet. The servlet in turn determines the
appropriate action, and then processes the request parameters for
the item to be added. It then instantiates a new CD bean (shown in
Listing 4) representing the selection, and goes about updating the
shopping cart object before placing it back within the session.
Download