Professional JSP Excerpt
Part Three : The "Dispatcher" Approach
We now move on to look at
architectures based on the dispatcher or "N-tiered" approach,
where a Servlet (or JSP) acts as a mediator or controller, delegating
requests to JSP pages and JavaBeans. We will look at the mediator-view,
mediator-composite
view, and service
to workers
architectures.
In an N-tier
application, the server side of the architecture is broken up into
multiple tiers, as illustrated in the next figure:
In this case, the application is composed of multiple tiers, where the middle tier, the JSP, interacts with the back end resources
via another
object or Enterprise JavaBeans
component. The Enterprise JavaBeans
server and the EJB provide managed
access to resources, support
transactions and access
to underlying security mechanisms,
thus addressing
the resource
sharing and performance issues of
the 2-tier approach. This is the programming model supported by the Java 2 Enterprise
Edition
(J2EE) platform.
The first step in N-tiered application
design should be identifying
the correct
objects and their interactions,
in other words, object modeling. This is the part where class diagrams
and tools like
Rational Rose ( http://www.rational.com
) step in for architects. Quite often a line has to be drawn as
to what objects need to be modeled: being over zealous can cause
unnecessary complexity. If you get this part right, you're half way
there!
The second part
is identifying the JSPs or Servlets. As a rule of thumb, these should be
divided into two categories, depending on the role they play, often
called "web components" in the J2EE terminology
-
Front
end JSPs or Servlets that manage application flow and business logic
evaluation. This is where a lot of method invocation on the objects,
or usage of EJBs, can be coded. They are not responsible for any
presentation, and act as a point to intercept the HTTP requests
coming from the users. They provide a single
entry point to an application,
simplifying security management and making application state easier
to maintain.
-
Presentation
JSPs that generate HTML (or even XML), with their main purpose in
life being presentation of dynamic content. They contain only
presentation and rendering logic.
The
figure below shows the relationship between these two categories. The
front-end component accepts a request, and then determines the
appropriate presentation component to forward it to. The presentation
component then processes the request and returns the response to another
front component or directly back to the user:
These categories
are analogous
to the Model-View design pattern, where the front-end
component is the model and the presentation component
the view. This approach
was referred
to as the "Model
2" programming
model in earlier releases
of the JSP specifications.
It differs
from Model 1 essentially
in the location at which the bulk of the request
processing is performed. The figure below shows the Model 2 approach:
In this
approach, JSPs are used to generate the presentation layer, and either
JSPs or Servlets to perform process-intensive tasks. The front-end
component acts as the controller and is in charge of the request
processing and the creation of any beans or objects used by the
presentation JSP, as well as deciding, depending on the user's actions,
which JSP to forward the request to. There is no processing logic within
the presentation JSP 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 for insertion within static
templates.
Instead of
writing Servlets for the front-end components you can choose, if you
wish, to use a JSP that contains only code and doesn't have any
presentation responsibilities. Whichever option is used (and in this
chapter we only use Servlets in this role), this approach typically
results in the cleanest separation of presentation from content, leading
to delineation of the roles and responsibilities of the developers and
page designers, especially in complex applications.
Beans
that are used by JSPs are not the same as Enterprise JavaBeans (EJBs),
but are usually simple classes that serve as data wrappers to
encapsulate information. They have simple get and set methods for each
bean property .The properties further correspond by name to the HTML
variables on the screen. This allows the bean properties to be set
dynamically by the jsp:usebean
tag without doing an individual request.getParameter(parametername)
on the request
object. See Chapter 4 for bean usage.
Mediator-View
Factoring
common services, such as authentication out to a mediating Servlet
allows us to remove potential duplication from our JSP pages. The code
below is an example of a Servlet that provides us with central
forwarding control for our game system, and includes a simple
authentication check that will be reused across requests. A Bean could
be used for this purpose, but you would have to add the same code to
each page to perform the authentication checks. Instead, each request
will be serviced by the Servlet, which now includes our authentication
code.
The
mediating Servlet, BabyGameServlet.java,
is shown below. It is placed in the <jswdk-root>/examples/WEB-INF/servlets/
directory:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class BabyGameServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response) {
processRequest(request, response);
}
public void doPost(HttpServletRequest request,
HttpServletResponse response) {
processRequest(request, response);
}
protected void processRequest(HttpServletRequest request,
HttpServletResponse response) {
try {
// If we added actual authentication, this is where it would go
// For example purposes, if the parameters exist, we consider the
// auth successful.
if ((request.getParameter("guesser") != null)
&& (request.getParameter("password") != null)) {
// Note: Based on the successful authentication of this user we may
// want to consider writing something into the session state that
// signifies that this is the case. For example, if we wanted to
// limit direct access to certain JSP pages only to authenticated
// users, then we could include code that would check for this
// session state before allowing such access.
// In order to reuse this Servlet for multiple examples, we pass as
// a request parameter the resource name to which we dispatch our
// request.
getServletConfig().getServletContext()
.getRequestDispatcher(request.getParameter("dispatchto"))
.forward(request, response);
} else {
PrintWriter outy = response.getWriter();
outy.println("Unable to authenticate, please try again.");
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
A new
architectural pattern is emerging, with the mediating Servlet working
with a JSP page and worker bean pair to fulfill a service request. This
'Mediator-View' architecture is shown below, and illustrating how each
service is partitioned. The Servlet initially handles the request and
delegates to a JSP software page/worker bean combination. The JSP
populates bean properties from the request parameters and then uses the
bean to prepare the data for presentation:
Continuing
our attempts at creating the appropriate abstractions in our system, we
shall consider how to better partition our business logic and data
access code, attempting to reduce the coupling among the various parts
of the system.
As we
discussed, this request is handled first by a basic Servlet that
dispatches to a JSP page for formatting and display. The JSP is
responsible for delegating the processing of business functionality to a
business delegate via a worker bean, which act as façades for the
back-end resources. We call this component a business
delegate, because it is a client-side business abstraction used
to complete the basic business processing by abstracting out the code
that deals with the back-end communication. The worker beans might
communicate through the business delegate with the back-end via
Enterprise JavaBeans components running in a scalable, load-balancing
container.
In this
case, we are able to decouple the EJB implementation from the JSP worker
bean by moving the EJB-related code, such as the code relating to JNDI
lookup, into the business delegate. If we include our EJB-related code
in our worker beans, then we are closely coupling the bean to a specific
underlying implementation, and if this implementation is modified or
changed we must make modifications to our worker bean accordingly. So we
have succeeded in creating abstractions that have reduced the coupling
among the distinct pieces of our system. We can see how the worker bean
and the business delegate reduce coupling between the client request and
the ultimate back-end resource.
In other words, the public interfaces need not change even if the API to the underlying resource changes. The worker beans can be combined with different JSP pages in order to provide different views of the same underlying information. Also, the Servlet that initially handles each request, and is responsible for authenticating each user, may restrict direct access to our JSP pages, an added security mechanism. Once a user has been successfully authenticated, the Servlet might store certain information in that user's session, and access to certain JSP pages might only be allowed to users whose session state contained such that information. The mechanism could be made more fine grained by including authorization information within this session state that would directly relate to which pages a user may view.
Our baby
guessing game example will need to change, in order to adhere to this
new design. The HTML user interface needs to change only slightly,
adding an input field for each user to enter a password. The table
caption in Babygame1.html
is modified as shown below in order to add the password input field, and
the revised file, babygame2.html,
is placed in <jswdk-root>/examples/jsp/mediatorview/:
<caption>Please
enter your userID:<input type="text"
name="guesser">Please enter your Password:<input
type="password"
name="password"></caption> >
The
other change to the HTML is to change the hidden input field called dispatchto.
The Servlet
uses this value to dispatch the request via the forward()
method to the appropriate JSP, and the value is supplied simply to allow
for easy modification of these examples. In this case we modify the value
attribute to reference the JSP for our "Mediator-View"
example, and the modified line looks like this:
<input
type="hidden" name="dispatchto" value="/jsp/mediatorview/BabyGame2.jsp">
We
have already seen the next piece of the puzzle in the Servlet, which
handles each request, authenticating each user and dispatching the
request appropriately. The Servlet and worker beans now contain Java
code that is owned by an individual in the software development role,
and the syntax and semantics of which may be modified without any
changes to our JSP. The property sheets remain a simple contract between
the web-production team and the software developers.
The benefit of a dispatcher
approach is in the control the Servlet has on the request. Since the
Servlet acts as the mediator, there is central forwarding control. As
discussed, this also creates a nice role separation between the lower
level coding in the Servlet and the JSP coding. A drawback of this
architecture is that it involves more design work and more coding, since
individual beans are created instead of code fragments. A very simple
project might be overly burdened by this extra overhead, while more
sophisticated efforts will benefit from the cleaner design.
Additionally,
encapsulating as much Java code as possible into our Servlet and beans,
and removing it from the JSP source, will also have the added benefit of
encouraging more elegant and refined Java coding, because excessive Java
code embedded within JSP source can quickly start to look like just
another scripting language, and the cut-and-paste mentality tends to
take over. Moreover, a software developer will be more inclined to
refine code that they own, without potential dependencies and conflicts
with a web-production person. Of course the irony is that such unwieldy
pages that are filled with Java code are in more need than any of
refactoring.
Updating
our example to include improvements based on these very forces, we add
to our worker bean a factory that vends multiple storage implementations
based on a parameterized factory method. Our actual storage process may
be modified easily and dynamically, simply by providing an
implementation that adheres to the Store
interface. We move our basic file storage code into an implementation
called SimpleStore
that implements the Store
interface and is created by StoreFactory.
We don't actually include data access code in this example, but it would
reside in a worker bean and would reference the business delegate, which
could then reference an EJB component. The Store
interface also hides implementation details, in this case those of the
storage mechanism behind it, exposing a well-defined interface instead.
The initial
delegation point in this architecture is a JSP . we will see later
another architecture in which the initial point of delegation is a
worker bean (Service-to-Workers). The subtle difference between the two
is worth discussing. In situations where there is on-going workflow that
is necessary for the generation of the data model, requiring multiple
business calls, one needs to decide where to encapsulate these business
calls:
-
In
one case, the JSP may contain the business calls that are required
to obtain the necessary state for the intermediate model, which will
be stored in the associated worker bean(s). The JSP is not only
responsible for the presentation of the data, but additionally has
some responsibility for coordinating the business calls, and may
interact with different beans in order to present multiple views of
the same underlying models.
-
On the other hand, if the Servlet delegates initially to a worker bean, then
the Servlet will
need to adopt the responsibility for controlling these business
calls, utilizing the worker
beans to make its requests. If the required data can be gathered
with a single business call,
then dispatching this call to the worker bean directly from the
Servlet allows the bean to
populate the entire intermediate model. The Servlet is then able to
forward the request to
the JSP for presentation.
As you may
have noticed, in the former scenario the JSP's responsibilities are
increased as it fulfills part of the role of the controller in addition
to the view. One needs to decide what is right for the job and move code
forward or back based on the workflow and presentation needs. One issue
to consider is that adding business calls to a JSP page allows for reuse
of this code conditionally across requests simply by nesting pages, as
in Mediator-Composite View, which we will discuss shortly. Adding
business invocations to the Servlet means either that these calls will
be invoked for each request the Servlet handles, limiting its potential
for reuse, or that the Servlet may become cluttered with conditional
logic dictating which code to execute for a particular request.
The
elements of the current "Mediator-View" example are:
-
The
HTML UI, which POSTs the request to the Servlet:
<jswdk-root>/examples/jsp/mediatorview/babygame2.html
-
The
Servlet, which dispatches to the JSP:
<jswdk-root>/examples/WEB-INF/servlets/BabyGameServlet.java
(source) and
<jswdk-root>/examples/WEB-INF/servlets/BabyGameServlet.class
(bytecode)
-
The
JSP:
<jswdk-root>/examples/jsp/mediatorview/BabyGame2.jsp
-
The
worker bean:
examples.mediatorview.BabyGameWorker2.java
-
The
supporting source code:
examples.mediatorview.StoreFactory,
examples.mediatorview.Store,
examples.mediatorview.SimpleStore,
examples.mediatorview.StoreException,
examples.auth.AuthenticatoFactory,
examples.auth.Authenticator,
examples.auth.SimpleAuthenticator,
examples.auth.AuthContext,
and examples.auth.SimpleAuthContext.
This
supporting code must be included in the CLASSPATH
that is used by the JSP engine.
The RequestDispatcher API
These
architectures rely on a Servlet's dispatching a request to another
resource. Prior to the Servlet API 2.1, dispatching of requests to
another dynamic resource was cumbersome, and limited because it was not
directly supported in the specification.
The Servlet
API 2.1 introduces the RequestDispatcher.
Accessible via the ServletContext,
the RequestDispatcher
implementation can dispatch a request to either a static resource, such
as an HTML page, or another dynamic resource, such as a Servlet or JSP.
A request can be either 'forwarded' or 'included', and state can be made
available to the dispatched resource.
The include()
method allows the programmatic equivalent of Server-Side Includes (SSI),
while the forward()
method transfers control to the designated dynamic resource. A Servlet
that invokes RequestDispatcher.include()
retains overall responsibility for the service request, handling the
response to the client. In this case, the Servlet has the opportunity to
do any sort of clean up after all the nested invocations have completed.
Once the
Servlet dispatches to another dynamic resource with an invocation to RequestDispatcher.forward(),
though, the Servlet has completed its work. An example of the latter
type of forward dispatch is occurs in both the Mediator-View and
Mediator-Composite View architectures.
The method
signatures that a Servlet may use to dispatch a request to another
resource, are:
public
void forward(ServletRequest request, ServletResponse response)
throws ServletException, java.io.IOException
public
void include(ServletRequest request, ServletResponse response)
throws ServletException, java.io.IOException
Here is an
example from within a Servlet:
RequestDispatcher
dispatcher =
getServletContext().getRequestDispatcher("process.jsp");
request.setAttribute("user",
user);
dispatcher.forward(request,
response);
For a
detailed description of the Servlet API, see Appendix C.
Mediator-Composite View
Sometimes
we are building systems with dynamic template text as well as dynamic
content. In other words, there is content being generated dynamically,
and the template text that surrounds this data is also being constantly
changed. Imagine that there are headers and footers in a page that are
modified quite often, notifying individuals of status changes or
information of special interest. It may not be desirable to recompile
our JSP source each time this template text changes, since this involves
a run time delay and resource load. If we simply type our header and
footer text directly into our JSP source, then we will need to modify
that source each time we want to change the template text, and our
modified source will need to be re-translated in order to service
another request, causing this run time latency, and this scenario will
repeat itself each and every time the text undergoes even slight
modification.
Moving the
header and footer template text to external files provides us with a
solution to this dependency on run time re-translation when our template
changes. We move a portion of the template text from our JSP source to
another file, replacing it with the JSP include
action:
<jsp:include
page="header.html" flush="true" />
Unlike its
counterpart the include
directive (signified with <%@),
which includes text at translation time, the JSP include
action substitutes the designated page into the JSP source at run time. Not only have we made our solution more modular, we
have provided a way to change template text on the fly without the need
for recompiling our JSP source. Of course, there is a run time cost for
this processing, but it is far less expensive than a full source
compile. Such flexibility is useful, especially since the JSP
specification does not include much coverage of pre-compilation.
Not only can we
dynamically include resources, such as static HTML template fragments
within our JSP source, but we can also include other JSPs, creating a
multiple-level nesting of pages. In this case, the presentation is built
from numerous static and dynamic resources, which together make up the
page presented to the user. Each of these resources is potentially
composed of additional nested pages. Although the HTML template text is
considered a static resource, meaning it is not generated dynamically at
run time, these template fragments are potentially a dynamic piece of
the interface, since each can be modified continually with immediate
updates occurring in the containing presentation. This
'Mediator-Composite View' architecture is shown visually below:
Each of these
architectures builds on the previous ones in some way, and in this case
we have modified the middle of the 'Mediator-View', while inheriting the
front and back. As with the other dispatcher architectures, the Servlet
mediator dispatches to a JSP, which imports the aforementioned static
and dynamic resources. The figure above shows this nested server page
including dynamic content generated via an invocation on a helper bean,
which gets to the business processing through a delegate which once
again acts as a façade to the back- end resource that may be
"wrapped" in an Enterprise JavaBeans component.
As we have
seen, the role of the delegate as a client-side business abstraction
layer is to make business calls on the actual back-end resource, such as
remote invocations on an Enterprise JavaBeans component. Once again, the
role of the worker bean is to service the JSP page, preparing data for
use and dynamic presentation by the page. The worker bean serves as an
intermediate model for the JSP with which it is paired. Again, we are
creating a more flexible architecture by abstracting out the worker bean
and the business delegate, so that the model for the JSP is not actually
responsible for remote invocations on business resources, and these two
distinct parts of the system are more loosely coupled.
This
architecture allows us to abstract out portions of our page, creating
atomic pieces that can be plugged into a composite whole in different
configurations. For example, a checkout page on a shopping site might
present a user with the contents of a shopping cart. It is easy to
imagine the shopping cart component as an atomic portion of the
presentation that might be included by other pages, as well, such as a
page that allows a user to survey, modify, or remove their current
selections while still in "shopping" mode, as shown in the
next two figures:
A presentation
component that is nested within another page will often represent a
fairly coarse-grained entity, such as the complete line-by-line
inventory of the items in the aforementioned shopping cart, but may be
decorated with different header and footer information based on the
context of its usage within a page.
As an example,
we will factor out the body of the presentation in our ongoing
"Baby Game" example, and decorate it with some headers and
footers. So how has our code changed to support this architecture?
Our HTML simply
requires a change to the hidden input field dispatchto.
Again, this value is used to dispatch to the appropriate JSP and is used
simply to allow for easy modification of these examples. In this case we
modify the value attribute to reference the JSP for our
'Mediator-Composite View' example. The modified line now looks like
this:
<input
type="hidden" name="dispatchto"
value="/jsp/mediatorcompositeview/BabyGame2.jsp">
This
modified user interface is now in
<jswdk-root>/examples/jsp/mediatorcompositeview/babygame2.html
Also,
we have created some files containing the static header and footer
template text to be included at runtime into our composite display.
Here's an example of what one of these header files looks like:
<center>
<H3><B>Baby Game 2000! Fun for the whole family!</B></H3><BR>
<hr>
</center>
<br>
<br>
This file
includes the header template for the presentation that results from our
"Mediator-Composite View" example, as shown in the following
screen shot:
The elements of
the current example are then:
-
The
HTML UI, which POSTs the request to the Servlet:
<jswdk-root>/examples/jsp/mediatorcompositeview/babygame2.html
-
The
Servlet, which dispatches to the JSP:
<jswdk-root>/examples/WEB-INF/servlets/BabyGameServlet.java
-
The
outermost JSP:
<jswdk-root>/examples/jsp/mediatorcompositeview/BabyGame2.jsp
-
The
composite JSP, included within the outermost JSP:
<jswdk-root>/examples/jsp/mediatorcompositeview/compositeinclude.jsp
-
The
worker bean:
examples.mediatorview.BabyGameWorker2
-
The
supporting code, which is reused unchanged from the previous
example:
examples.mediatorview.StoreFactory,
examples.mediatorview.Store,
examples.mediatorview.SimpleStore,
examples.mediatorview.StoreException,
examples.auth.AuthenticatorFactory,
examples.auth.Authenticator,
examples.auth.SimpleAuthenticator,
examples.auth.AuthContext,
and examples.auth.SimpleAuthContext
This
supporting code must be included in the CLASSPATH
that is used by the JSP engine.
-
The
static template text, which is included dynamically at runtime to
create the composite display:
<jswdk-root>/examples/jsp/mediatorcompositeview/gameheader.html,
<jswdk-root>/examples/jsp/mediatorcompositeview/gamefooter.html,
<jswdk-root>/examples/jsp/mediatorcompositeview/nestedheader.html,
<jswdk-root>/examples/jsp/mediatorcompositeview/nestedfooter.html.
Service to Workers
The
initial delegation point of the Service to Workers architecture, shown
visually below, is a worker bean that processes our business and data
access code, once again via a client-side business abstraction. As with
each of the dispatcher architectures, the Servlet handles the request
from the client, providing the opportunity for the processing of common
services. After the worker bean has completed its responsibility of
populating the intermediate model for the JSP, the Servlet dispatches to
the JSP to generate the presentation:
This
architecture provides a cleaner separation between the view and the
controller, since the JSP page is no longer making business calls, but
simply accessing the state of the pre-populated worker bean. Depending
on the workflow needs of the system, a decision will have to be made
about the suitability of this architecture versus one where the initial
delegation point is a JSP, such as the Mediator-View. The cleaner
separation of view and controller is indeed an important factor in the
decision as well, and often an overriding one. Certainly, if there is
not a need for multiple business calls to generate the intermediate
model, then this type of architecture is a good choice.
In some
cases, though, we may only want portions of a model to be utilized in a
display and we may not want to reuse this code across requests. If the
granularity of the data access components necessitate multiple calls to
retrieve the data, then the Mediator-View architecture may be better
suited to handle this type of situation, since the server page will make
business calls on the beans as necessary.
Our example
will allow an authenticated user to examine his or her previously stored
guesses. One could imagine this feature being expanded to allow
modification of the previously stored data, as well.
The HTML for
the user interface consists of a couple input fields for the user to
enter their id and password for authentication purposes. The dispatchto
hidden field has been modified appropriately, and another hidden field,
a flag called delegatetobean,
has been added, and signals to the Servlet that we want to use our
worker bean as the initial dispatch point in this scenario. It allows us
to reuse the Servlet that we have been utilizing throughout our
discussion with minor modifications. The screen shot below shows this
HTML page:
The HTML
source for this page, <jswdk-root>/examples/jsp/servicetoworkers/babygameSW.html,
is as follows:
<!--
babygameSW.html -->
<html>
<head>
<title>Baby
Game -- Retrieve stored guesses</title>
</head>
<body
bgcolor="#FFFFFF">
<form
method="post" action="/examples/servlet/BabyGameServlet"
name="">
<center>
<h3>
Baby
Game -- Retrieve stored guesses</h3></center>
<center>
<hr>
<br>
Please
enter your userID:<input type="text"
name="guesser"><br><br>Please enter your
Password:<input type="password"
name="password">
<br><br>
<input
type=hidden name="dispatchto" value="/jsp/servicetoworkers/BabyGameSW.jsp">
<input
type=hidden name="delegatetobean" value="true">
<input
type=submit value="Retrieve">
</form>
</body>
</html>
The Servlet
has been modified to include a conditional check for the delegatetobean
flag. If it is set, then the Servlet uses the worker bean as the initial
delegation point. In previous examples, the Servlet uses the JSP as the
initial delegation point.
Adding this
conditional, which will evaluate to true
in this Service to Workers example only, allows us to reuse similar
Servlet code throughout our discussion; the Servlet source is shown
below. After successfully authenticating the user, the Servlet delegates
to the worker bean (BabyGameWorkerSW),
instructing it to load previously stored guesses based on the user's id.
The worker bean is set as an attribute of the request object with the
same bean id that is used in the presentation JSP (an id value of "SWworker").
This allows the JSP to share this previously instantiated and
initialized bean, instead of creating a new one. In effect, we have
passed the bean as an argument to the JSP.
The
code for the Servlet is as follows, and is kept in <jswdk-root>/examples/WEB-INF/servlets/BabyGameServlet.java:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import examples.auth.*;
import examples.servicetoworker.BabyGameWorkerSW;
public class BabyGameServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response) {
processRequest(request, response);
}
public void doPost(HttpServletRequest request,
HttpServletResponse response) {
processRequest(request, response);
}
protected void processRequest(HttpServletRequest request,
HttpServletResponse response) {
try {
String guesser = request.getParameter("guesser");
String password = request.getParameter("password");
// Authenticate each request
Authenticator auth =
AuthenticatorFactory.create(AuthenticatorFactory.SIMPLE);
AuthContext authContext =
AuthenticatorFactory
.createContext(AuthenticatorFactory.SIMPLE);
authContext.addValue("guesser", guesser);
authContext.addValue("password", password);
auth.init(authContext);
if (auth.authenticate()) {
String dispatchto = request.getParameter("dispatchto");
String delegateToBean =
request.getParameter("delegatetobean");
// Delegate to worker bean
if (delegateToBean != null) {
BabyGameWorkerSW worker = new BabyGameWorkerSW();
worker.load(guesser);
request.setAttribute("SWworker", worker);
}
// In order to reuse this Servlet for multiple examples, we pass
// as a request parameter the resource name to which we dispatch
// our request
getServletConfig().getServletContext()
.getRequestDispatcher(dispatchto)
.forward(request, response);
} else {
PrintWriter outy = response.getWriter();
outy.println("Unable to authenticate, please try again.");
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
In our
earlier examples, we collected and stored a user's guesses to a text
file. Now the example evolves such that the worker bean called examples.servicetoworker.BabyGameWorkerSW
adds a method that loads our previously stored guesses from the store,
based on an authenticated user's id. The method is defined as follows:
public
void load(String id) {
try {
Store storer = StoreFactory.createStore(StoreFactory.SIMPLE);
Properties props = (Properties) storer.load(id);
setProperties(props);
} catch (StoreException e) {
e.printStackTrace();
}
}
Additionally,
the worker bean has a method that is included so that our example may
make use of JSP error processing. The method is invoked as a guard
clause from within the JSP in order to validate the accuracy of the data
before it is presented to the user. The method throws an exception if
the bean's state is incomplete, and is defined as follows:
public
void validationGuard() throws Throwable {
if (getGender() == null) {
throw new Throwable("Error populating guess data.");
}
}
The JSP, <jswdk-root>/examples/jsp/servicetoworkers/BabyGameSW.jsp,
is shown below. Notice that it includes an explicit reference to an
error page:
<HTML>
<HEAD><TITLE>Baby
Game - Your Guesses</TITLE></HEAD>
<BODY
bgcolor=FFFFFF>
<%@
page language="java" errorPage="errorPageSW.jsp"
buffer="8k" %>
If an
uncaught exception occurs within a JSP, the flow of control immediately
transfers to the referenced error page, in this case errorPageSW.jsp.
As mentioned, the JSP includes a guard clause at the beginning, which
checks for valid data. If valid data does not exist, then the worker
bean will throw an exception, which will present the error page to the
user, notifying him or her of the problem. In our example, if the client
enters an id and password for a valid user who has not previously stored
any guesses, then the bean state will be invalid and the bean's validationGuard()
method will indeed throw an exception. The error page display could
easily be made as informative as necessary for the user:
<jsp:useBean
id="SWworker" class="examples.servicetoworker.BabyGameWorkerSW"
scope="request" />
Notice also
that the JSP has the same bean id that the Servlet previously included
in its setAttribute()
invocation. Thus, the JSP will reuse the existing bean, which was
created in the Servlet:
<jsp:setProperty name="SWworker" property="*" />
<% SWworker.validationGuard(); %>
<br>
<jsp:getProperty name="SWworker" property="guesser" />, here are your previously stored choices:<br>
<table BORDER COLS=5 WIDTH="75%" >
<caption></caption>
<tr>
<td> <jsp:getProperty name="SWworker" property="gender" />
</td>
<td>
<jsp:getProperty name="SWworker" property="pounds" /> lbs <jsp:getProperty name="SWworker" property="ounces" /> oz
</td>
<td>
<jsp:getProperty name="SWworker" property="month" /> <jsp:getProperty name="SWworker" property="day" />
</td>
<td>
<jsp:getProperty name="SWworker" property="length" /> inches
</td>
</tr>
</table>
<br>
</BODY>
</HTML>
The full elements of the
"Service To Workers" example are:
-
The
HTML UI, which POSTs the request to the Servlet:
<jswdk-root>/examples/jsp/servicetoworkers/babygameSW.html
-
The
Servlet, which dispatches to the JSP:
<jswdk-root>/examples/WEB-INF/servlets/BabyGameServlet
-
The
JSP:
<jswdk-root>/examples/jsp/servicetoworkers/BabyGameSW.jsp
-
The
ErrorPage:
<jswdk-root>/examples/jsp/servicetoworkers/errorpageSW.jsp
-
The
worker bean: examples.servicetoworker.BabyGameWorkerSW
-
The
supporting code, which is reused from a previous example:
examples.mediatorview.StoreFactory,
examples.mediatorview.Store,
examples.mediatorview.SimpleStore,
examples.mediatorview.StoreException,
examples.auth.AuthenticatorFactory,
examples.auth.Authenticator,
examples.auth.SimpleAuthenticator,
examples.auth.AuthContext,
and examples.auth.SimpleAuthContext
prev ....
| next ....
|