|
A powerful advantage of JSP is the ability to separate an application's
business logic from its presentation. Using Smalltalk object-oriented
terminology, JSP encourages MVC (model-view-controller) web applications.
JSP classes or beans are the model, JSP is the view, and a servlet is
the controller.
The example is a simple guest book. Users log in and add
comments. It is also available in the Resin demos
| Role | Implementation
|
| Model | A GuestBook of Guests.
|
| View | login.jsp for new users add.jsp for logged-in users.
|
| Controller | GuestJsp, a servlet to manage the state.
|
The GuestJsp skeleton forwards a "Hello, World" string to a login.jsp
page. The skeleton establishes the architecture for the guest book.
The details will be filled in below.
When the example is compiled, browse
http://localhost:8080/servlet/jsp.GuestJsp
And you should see a page like:
Hello, world
JSP templates start with servlet processing and then forward the
results to a JSP page for formatting.
Forwarding uses a Servlet 2.1 feature of the ServletContext,
getRequestDispatcher(). The request dispatcher lets servlets forward
and include any subrequests on the server. It's a more flexible
replacements for SSI includes. The RequestDispatcher can include the
results of any page, servlet, or JSP page in a servlet's page.
GuestJsp will use dispatcher.forward() to pass control to the JSP page
for formatting.
package jsp.GuestJsp;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* GuestJsp is a servlet controlling user
* interaction with the guest book.
*/
public class GuestJsp extends HttpServlet {
/**
* doGet handles GET requests
*/
public void doGet(HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException
{
// Save the message in the request for login.jsp
req.setAttribute("message", "Hello, world");
// get the application object
ServletContext app = getServletContext();
// select login.jsp as the template
RequestDispatcher disp;
disp = app.getRequestDispatcher("login.jsp");
// forward the request to the template
disp.forward(req, res);
}
}
The servlet and the jsp page communicate with attributes in the
HttpRequest object. The skeleton stores "Hello, World" in the "message"
attribute. When login.jsp starts, it will grab the string and print it.
Since Resin's JavaScript understands extended Bean patterns, it
translates the request.getAttribute("message") into the
JavaScript equivalent request.attribute.message.
<%@ page language=javascript %>
<head>
<title><%= request.attribute.message %></title>
</head>
<body bgcolor='white'>
<h1><%= request.attribute.message %></h1>
</body>
For those coming to JSP from an ASP or CGI background, Servlets replace
CGI scripts taking advantage of Java's strength in dynamic class
loading. A servlet is just a Java class which extends Servlet or
HttpServlet and placed in the proper directory. Resin will
automatically load the servlet and execute it.
-
doc
-
index.html
-
login.jsp
-
add.jsp
-
WEB-INF
-
classes
-
jsp
-
GuestJsp.class
-
GuestBook.class
-
Guest.class
The url /servlet/classname forwards the request to the
Servlet Invoker. The Invoker will dynamically load the Java class
classname from doc/WEB-INF/classes and try to execute the
Servlet's service method.
Resin checks the class file periodically to see if the class has
changed. If so, it will replace the old servlet with the new servlet.
The next step, after getting the basic framework running, is to create
the model.
The guest book is straightforward so I've just included the API here.
It conforms to Bean patterns to simplify the JavaScript. The same API
will work for HashMap, file-based, and database implementations.
JSP files only have access to public methods. So a JSP file
cannot create a new GuestBook and it can't add a new guest. That's
the responsibility of the GuestJsp servlet.
package jsp;
public class Guest {
Guest();
public String getName();
public String getComment();
}
Resin's JavaScript recognizes Bean patterns. So JSP pages using
JavaScript can access getName() and getComment() as
properties. For example, you can simply use guest.name and
guest.comment
package jsp;
public class GuestBook {
GuestBook();
void addGuest(String name, String comment);
public Iterator iterator();
}
Resin's JavaScript also recognizes the iterator() call, so you can
use a JavaScript for ... each to get the guests:
for (var guest in guestBook) {
...
}
To keep the example simple, GuestJsp stores the GuestBook in the
application (ServletContext). As an example, storing data in the
application is acceptable but for full-fledged applications, it's
better just to use the application to cache data stored elsewhere.
// get the application object
ServletContext app = getServletContext();
GuestBook guestBook;
// The guestBook is stored in the application
synchronized (app) {
guestBook = (GuestBook) app.getAttribute("guest_book");
// If it doesn't exist, create it.
if (guestBook == null) {
guestBook = new GuestBook();
guestBook.addGuest("Harry Potter", "Griffindor rules");
guestBook.addGuest("Draco Malfoy", "Slytherin rules");
app.setAttribute("guest_book", guestBook);
}
}
RequestDispatcher disp;
disp = app.getRequestDispatcher("login.jsp");
// synchronize the Application so the JSP file
// doesn't need to worry about threading
synchronized (app) {
disp.forward(req, res);
}
The JSP file itself is simple. It grabs the guest book from
the application and displays the contents in a table. Normally,
application objects need to be synchronized because several clients
may simultaneously browse the same page. GuestJsp has taken care of
synchronization before the JSP file gets called.
<%@ page language=javascript %>
<head>
<title>Hogwarts Guest Book</title>
</head>
<body bgcolor='white'>
<h1>Hogwarts Guest Book</h1>
<table>
<tr><td width='25%'><em>Name</em><td><em>Comment</em>
<%
var guestBook = application.attribute.guest_book
for (var guest in guestBook) {
out.writeln("<tr><td>" + guest.name + "<td>" + guest.comment);
}
%>
</table>
</body>
Hogwarts Guest Book
| Name | Comment
|
| Harry Potter | Griffindor Rules
|
| Draco Malfoy | Slytherin Rules
|
The guest book logic is simple. If the user has not logged in, she
sees comments and a form to log in. After login, she'll see the
comments and a form to add a comment. login.jsp formats the login
page and add.jsp formats the add comment page.
GuestJsp stores login information in the session variable.
| Form Variable | Meaning
|
| action | 'login' to login or 'add' to add a comment
|
| name | user name
|
| password | user password
|
| comment | comment for the guest book
|
...
// name from the session
String sessionName = session.getValue("name");
// action from the forms
String action = request.getParameter("action");
// name from the login.jsp form
String userName = request.getParameter("name");
// password from the login.jsp form
String password = request.getParameter("password");
// comment from the add.jsp form
String comment = request.getParameter("comment");
// login stores the user in the session
if (action != null && action.equals("login") &&
userName != null &&
password != null && password.equals("quidditch")) {
session.putValue("name", userName);
}
// adds a new guest
if (action != null && action.equals("add") &&
sessionName != null &&
comment != null) {
guestBook.addGuest(sessionName, comment);
}
String template;
// if not logged in, use login.jsp
if (session.getValue("name") == null)
template = "login.jsp";
// if logged in, use add.jsp
else
template = "add.jsp";
RequestDispatcher disp;
disp = app.getRequestDispatcher(template);
...
login.jsp and add.jsp just append different forms to the display
code in the previous section.
<%@ page language=javascript %>
<head>
<title>Hogwarts Guest Book: Login</title>
</head>
<body bgcolor='white'>
<h1>Hogwarts Guest Book</h1>
<table>
<tr><td width='25%'><em>Name</em><td><em>Comment</em>
<%
var guestBook = application.attribute.guest_book
for (var guest in guestBook) {
out.writeln("<tr><td>" + guest.name + "<td>" + guest.comment);
}
%>
</table>
<hr>
<form action='GuestJsp' method='post'>
<input type=hidden name='action' value='login'>
<table>
<tr><td>Name:<td><input name='Name'>
<tr><td>Password:<td><input name='Password' type='password'>
<tr><td><input type=submit value='Login'>
</table>
</form>
</body>
The Resin demo shows a few ways to extend the guest book, including
adding some intelligence to the form processing. However, as forms
get more intelligent, even JSP templates become complicated. There is
a solution: XTP templates.
Copyright (c) 1998-2008 Caucho Technology, Inc. All rights reserved. resin® and
quercus®
are registered trademarks,
and Ambertm is a trademark of Caucho Technology, Inc.
|