Professional JSP Excerpt
Part Two : The "Page-Centric" Approach
We start by looking at
architectures based on the page-centric or client-server approach. We
will look at the page-view
and page-view
with bean architectures.
Applications
built using a client-server approach have been around for some time;
they consist of one or more application programs running on client
machines, and connecting to a server-based application to work. (A good
example would be a PowerBuilder or Oracle Forms based system.) CGIs and
pre-Servlet applications were generally based on this simple 2-tier
model, and with the introduction of Servlets, 2-tier applications could
also be created in Java.
This model
allows JSPs or Servlets direct access to some resource like a database
or legacy application to service a client's request: the early JSP
specifications termed this a "Model 1" programming approach.
The JSP page is where the incoming request is intercepted processed, and
the response sent back to the client; JSPs only differed from Servlets
in this scenario by providing cleaner code, and separating code from the
content by placing data access in beans.
This is
illustrated in the figure below.
The advantage
of such an approach is that it is simple to program, and allows the page
author to generate dynamic content easily, based upon the request and
the state of the resources.
However this
architecture does not scale up well for a large number of simultaneous
clients since there would be a significant amount of request processing
to be performed, and each request must establish or share a potentially
scarce/expensive connection to the resource in question. (A good example
would be JDBC connections in servlets or JSPs and the need for
connection pools.)
Indiscriminate
usage of this architecture usually leads to a significant amount of Java
code embedded within the JSP page. This may not seem to be much of a
problem for Java developers but it is certainly an issue if the JSP
pages are maintained by designers: the code tends to get in the
designer's way, and you run the risk of your code becoming corrupted
when others are tweaking the look and feel.
Of
course, it is not necessary to use JSPs, to separate code and
presentation in servlets: the use of templates is popular. There are a
lot of toolkits that facilitate this, such as
Page-View
This
basic architecture involves direct request invocations to a server page
with embedded Java code, and markup tags which dynamically generate
output for substitution within the HTML.
This
approach has many benefits. It is very easy to get started and is a
low-overhead approach from a development standpoint. All of the Java
code may be embedded within the HTML, so changes are confined to a
limited area, reducing complexity. The figure below shows the
architecture visually.
The big
trade-off here is in the level of sophistication. As the scale of the
system grows, some limitations of this approach surface, such as
including too much business logic in the page instead of factoring
forward to a mediating Servlet, or factoring back to a worker bean. As
we discussed earlier, utilizing a Servlet and helper beans allow us to
separate developer roles more cleanly, and improves the potential for
code reuse. Let's start with a fairly simple example JSP page that
follows this model, handling requests directly and including all the
Java code within the JSP source page.
We begin
with a basic description of the example application. All the examples in
this chapter use the JSWDK version 1.0.1, using the default web
application directory structure for simplicity. Thus, the JSP and HTML
source are under the <jswdk-root>/examples/jsp/
subdirectory and the Servlet (see later) is under <jswdk-root>/examples/WEB-INF/servlets.
The
user interface is shown below:
The HTML
code for this page, BabyGame1.html,
is stored in <jswdk-root>/examples/jsp/pageview/
and includes a form that prompts a user for selections. The user is
asked to make some guesses about the statistics of a baby to be born in
the near future. The HTML code is as follows:
<html>
<head>
<title>Baby
Game</title>
</head>
<body
bgcolor="#FFFFFF">
<form
method="post" action="BabyGame1.jsp"
name="">
<center>
<h3>Baby
Game</h3></center>
<center>
<br>
<table
BORDER COLS=5 WIDTH="75%" >
<caption>Please
enter your own name:
<input
type="text" name="guesser"></caption>
<tr>
<td>
<br><input
type="radio" name="gender"
value="Female" checked>Female
<p><input
type="radio" name="gender"
value="Male">Male
<p></td>
<td><font
size=-1>Please choose a date:</font>
<p>Month:
<select name="month">
<option
value="January">January</option>
<option
value="February">February</option>
...
<option
value="December">December</option>
<br></select>
<p>Day:
<select name="day">
<option
value="1">1</option>
<option
value="2">2</option>
...
<option
value="31">31</option>
<br></select>
<p> </td>
<td><font
size=-1>Please choose a weight:</font>
<p>Pounds:
<select name="pounds">
<option
value="5">5</option>
<option
value="6">6</option>
...
<option
value="12">12</option>
<br></select>
<p>Ounces:
<select name="ounces">
<option
value="1">1</option>
<option
value="2">2</option>
...
<option
value="15">15</option>
<br></select>
<p> </td>
<td><font
size=-1>Please choose a length:</font>
<p>Inches:
<select name="length">
<option
value="14">14</option>
<option
value="15">15</option>
...
<option
value="25">25</option>
</select><p> <p> <p> </td>
</tr>
</table>
</center>
<br>
<center>
<p><input
type="submit" name="submit" value="Make
Guess">
<input
type="reset" name="reset"
value="Reset">
</center>
<br></form>
</body>
</html>
This HTML
form will POST the choices to our JSP, which is shown in source form
below (BabyGame1.jsp,
stored in directory <jswdk-root>/examples/jsp/pageview/).
These choices are then stored and displayed by our simple JSP, which has
handled the request directly.
The
resulting display looks like this:
The first
part of BabyGame1.jsp
is responsible for extracting the values from each of the request
parameters and populating the state of the JSP. The name of the
individual making the guess is contained in a request parameter called guesser.
Each of the statistics about the baby is stored in a request parameter
with an intuitive name, such as gender,
length,
etc.
<HTML>
<HEAD><TITLE>Baby Game - Your Guesses</TITLE></HEAD>
<BODY bgcolor=FFFFFF>
<%@ page language="java" import="java.util.*,java.io.*" buffer="8k" %>
<%
String guesser = request.getParameter("guesser");
String gender = request.getParameter("gender");
String pounds = request.getParameter("pounds");
String ounces = request.getParameter("ounces");
String month = request.getParameter("month");
String day = request.getParameter("day");
String length = request.getParameter("length");
After this
task is complete, there is a validation check on the data. If the
validation is unsuccessful, then the JSP completes its processing by
sending the user an appropriate message:
if(guesser == null || gender == null || pounds == null || ounces == null
|| month == null || day == null || length == null)
{ %>
<br> There where some choices that were not selected.<br><br>
Sorry, but you must complete all selections to play.<br>
<font size=-1>(Please hit the browser 'back' button to continue)</font><br>
<%}
Otherwise,
upon successful validation of the request parameters, the data is stored
to disk and a table is generated using the JSP state, which corresponds
to the user's guesses. The guesses are stored within a java.util.Properties
object, which, for example purposes, is flattened to a text file in the
root directory of the JSP run time engine, in this case the JSWDK root
directory. The name of the text file corresponds to the name of the
guesser, which was provided in the HTML form. One could easily imagine
an even more basic example where the data was merely displayed but not
stored. Such an example would differ only in that the storage code in BabyGame1.jsp
would be omitted:
else
{
//Store guess info and display to user
Properties p = new Properties();
p.put("guesser", guesser);
p.put("gender", gender);
p.put("pounds", pounds);
p.put("ounces", ounces);
p.put("month", month);
p.put("day", day);
p.put("length", length);
FileOutputStream outer = new FileOutputStream(guesser);
p.save(outer, "Baby Game -- "+guesser+"'s guesses");
outer.flush();
outer.close();
%>
<br>
<%= guesser %>, your choices have been stored.<br> Here they are:<br>
<table BORDER COLS=5 WIDTH="75%" >
<caption></caption>
<tr>
<td> <%= gender %>
</td>
<td>
<%= pounds %> lbs <%= ounces %> oz
</td>
<td>
<%= month %> <%= day %>
</td>
<td>
<%= length %> inches
</td>
</tr>
</table>
<br>
<%}
%>
</BODY>
</HTML>
This JSP
certainly was easy to build and is well suited to handling such simple
needs. It is getting quite cluttered with Java code, though, and its
processing requirements are extremely simplistic. More sophisticated
business rules and data access code would require much more Java within
the JSP and the source would become unwieldy quickly as it grows.
Additionally, we are limited to mostly "cut-and-paste" reuse,
which means duplicate code and a more error-prone environment that is
harder to maintain and debug.
In order to
reduce the clutter of the current example and to provide a cleaner
environment for future growth, we will look at another 'Page-Centric'
architecture called 'Page-View with Bean' in the next section.
Page-View
with Bean
This
pattern is used when the Page-View architecture becomes too cluttered
with business-related code and data access code. The Baby Game example
now evolves into a more sophisticated design, as shown in the figure
below.
The modified
JSP source page, <jswdk-root>/examples/jsp/pageviewwithbean/
BabyGame1.jsp, is
shown below, followed by the worker bean that works in tandem with the
modified JSP in order to process the request. The HTML for the initial
game interface remains unchanged. The resulting display to the user from
the JSP is basically unchanged from that of the previous 'Page-View'
example:
<HTML>
<HEAD><TITLE>Baby Game - Your Guesses</TITLE></HEAD>
<BODY bgcolor=FFFFFF>
<%@ page language="java" buffer="8k" %>
<jsp:useBean id="worker1" class="examples.pageviewwithbean.BabyGameWorker1"
scope="request" />
<%-- Populate the JavaBean properties from the Request parameters. --%>
<%-- The semantics of the '*' wildcard is to copy all similarly-named --%>
<%-- Request parameters into worker1 properties. --%>
<jsp:setProperty name="worker1" property="*" />
<%
if(worker1.getGuesser == null || worker1.getGender() == null ||
worker1.getPounds() == null || worker1.getOunces() == null ||
worker1.getMonth() == null || worker1.getDay() == null ||
worker1.getLength() == null)
{ %>
<br> There where some choices that were not selected.<br><br>
Sorry, but you must complete all selections to play.<br>
<font size=-1>(Please hit the browser 'back' button to continue)</font><br>
<%}
else
{
worker1.store();
%>
<br>
<jsp:getProperty name="worker1" property="guesser" />, your choices have
been stored.<br> Here they are:<br>
<table BORDER COLS=5 WIDTH="75%" >
<caption></caption>
<tr>
<td> <jsp:getProperty name="worker1" property="gender" />
</td>
<td>
<jsp:getProperty name="worker1" property="pounds" /> lbs
<jsp:getProperty name="worker1" property="ounces" /> oz
</td>
<td>
<jsp:getProperty name="worker1" property="month" />
<jsp:getProperty name="worker1" property="day" />
</td>
<td>
<jsp:getProperty name="worker1" property="length" /> inches
</td>
</tr>
</table>
<br>
<%}
%>
</BODY>
</HTML>
The source for
the worker bean, BabyGameWorker1,
is as follows:
package examples;
import java.io.*;
import java.util.Properties;
public class BabyGameWorker1
{
private Properties p = new Properties();
public String getGuesser() {
return(String)p.get("guesser");
}
public void setGuesser(String aString) {
p.put("guesser", aString);
}
// And similar "get" and "set" methods for Gender, Pounds, Ounces, Month,
// Day, Length, and File.
public Properties getProperties() {
return p;
}
public void store() throws IOException {
FileOutputStream outer = new FileOutputStream((String)p.get("guesser"));
p.save(outer, "Baby Game -- "+p.get("guesser")+"'s guesses");
outer.flush();
outer.close();
}
}
We see in these
two listings that the Java code representing the business logic and
simple data storage implementation has migrated from the JSP to the
JavaBean worker. This refactoring leaves a much cleaner JSP with limited
Java code, which can be comfortably owned by an individual in a
web-production role, since it encapsulates mostly markup tags.
Additionally, a
less technical individual could be provided with a property sheet for
the JavaBean workers, providing a listing of properties that are made
available to the JSP page by the particular worker bean, and the desired
property may simply be plugged into the JSP getProperty
action via the markup tag in order to obtain the attribute value. An
example of a potential property sheet is shown below. Additionally, once
a convention and format were agreed upon, an automated tool could be
created to interrogate all JavaBeans of interest and generate property
sheets automatically.
Moreover, we
now have created a bean that a software developer can own, such that
functionality may be refined and modified without the need for changes to the HTML or
markup within the JSP source page.
Another way to
think about these changes is that we have created cleaner abstractions
in our system
by replacing implementation with intent. In other words, we have taken a
bunch of code from our JSP source (the Java code that writes the guesses
to a file on disk ), encapsulated it within a bean, and replaced it with
our intent, which is to store the data, worker1.store().
Looping in JSP
So why might
one choose to embed Java code in a JSP as opposed to using a predefined
tag? An example would be in order to loop through some data and format
HTML for output.
Earlier
versions of the JSP specification included a tag syntax for looping
through indexed properties of JavaBeans, so that no Java code was needed
within the HTML for this purpose.
With the JSP
software specification version 1.0, such tags were removed in favor of
the creation of an extensible custom tag markup mechanism that could be
used for this purpose and much more. Unfortunately, this custom tag
functionality is first available in version 1.1 of the specification,
and JSP engines supporting the custom tag libraries are only now
becoming available.
Further Refinements
After deploying
this solution, our expectant family decides to limit the group of people
who are able to access the system, deciding to include only their family
and friends. To this end, they decide to add an authentication mechanism
to their system so that each individual will have to provide proof of
identity before accessing the system.
If a simple
authentication mechanism is added to the top of our JSP page and we want
to ensure that each JSP page is protected by this device, then we will
need to add some code to each and every JSP page to execute this
authentication check. Whenever there is a duplication of code such as
this, it is beneficial to explore options for migrating the duplicated
code to a common area.
In the previous
example we "factored back," as we moved business logic and
data-access Java code from our JSP to our JavaBean, and in the next
section we expand our example to "factor forward" into a
Servlet, continuing our efforts to minimize the amount of inline Java
code in our JSPs.
prev ....
| next ....
|