Introducing JavaServer Pages

mobdescriptiveΛογισμικό & κατασκευή λογ/κού

28 Οκτ 2013 (πριν από 3 χρόνια και 11 μήνες)

98 εμφανίσεις

Introducing JavaServer Pages


Based on servlet technology, and currently shaping up at breakneck speed, JavaServer Pages (JSP) is set
to be one of the most important elements of Java serve
r programming. It's by no means complete yet,
but that will change as the Java 2 Enterprise Edition comes together.

So what are JavaServer Pages? Well, they combine markup (whether HTML or XML) with nuggets of Java
code to produce a dynamic web page. Each
page is automatically compiled to a servlet by the JSP engine,
the first time it is requested, and then executed. JSP provides a variety of ways to talk to Java classes,
servlets, applets and the web server. With it, you can split the functionality of your

web applications into
components with well
-
defined public interfaces glued together by a simple page.

This model allows tasks to be sub
-
divided
-

a developer builds custom components and the page designer
assembles the application with a few judicious met
hod calls. In this 'application assembly' model, the
business logic is separated from the presentation of data.

To give you an idea of the future, this separation of logic and presentation may become yet more extreme
with the use of custom tags slated for
JSP 1.1.

JavaServer Pages is a specification that is already implemented by several web servers (for more details,
see the JSP FAQ at http://www.esperanto.org.nz/jsp/jspfaq.html), on which your code will run without
change, making it more portable and the
server market more competitive than its rivals. Finally, it's nice
and simple!

In this chapter, we will :



Discuss the JavaServer Pages (JSP) architecture



Look at the elements of a JSP file, and the tags used to represent them



Encapsulate logic in a JavaB
ean component and integrate it with JSP



Walk through a detailed example using JSP, showing a typical web application architecture

Architectural Overview

A JavaServer Page is a simple text file consisting of HTML or XML content along with JSP elements (a
sort
of shorthand for Java code). When a client requests a JSP page of the web server and it has not been run
before, the page is first passed to a JSP engine which compiles the page to a servlet, runs it and returns
the resulting content to the client. Th
ereafter, the web server's servlet engine will run the compiled page.

It should be no surprise, given the flexibility of the servlet model, that the current reference
implementation of the JSP engine is itself a servlet.

It is possible to view the finished

servlet code that is generated by locating it within the directory structure
of the servlet engine. For example, with JRun, you can find the source code for your JSP files (in servlet
form) in the
jrun/jsm
-
default/services/jse/servlets/jsp

directory. This

is very helpful when
trying to debug your JSP files.

If you take a look in the source files for the
javax.servlet.jsp

package, you'll find the following
classes :



JSPPage



HttpJspPage

They define the interface for the compiled JSP page
-

namely that it m
ust have three methods. Not
surprisingly they are :



jspInit()



jspDestroy()



_jspService(HttpServletRequest request, HttpServletResponse response)

The first two methods can be defined by the JSP author (we'll see how in a moment), but the third is the
com
piled version of the JSP page, and its creation is the responsibility of the JSP engine.



It's time we saw a JSP file :


<html>


<head>


<title>Demo of a JSP pag
e</title>


</head>


<body>



<!
--

Set global information for the page
--
>


<%@ page language="java" %>



<!
--

Declare the character variable
--
>


<%! char c = 0; %>



<!
--

Scriptlet
-

Java code
--
>


<%


for (int i = 0; i < 26; i++)



{


for (int j = 0; j < 26; j++)


{


// Output capital letters of the alphabet, and change starting letter


c = (char)(0x41 + (26
-

i + j)%26);


%>

Continued on Following Page


<!
--

Output the value of c.toString() to t
he HTML page
--
>


<%= c %>





<%


}


%>


<br>


<%


}


%>



</body>


</html>

This page just outputs the alphabet 26 times and changes the starting letter. The HTML is self
-
explanatory
and written the way it should be, rather

than cluttering up methods of a servlet.

Elements of a JavaServer Page

The Java code in the page includes :



Directives


these provide global information to the page, for example, import statements, the
page for error handling or whether the page is part
of a session. In the above example we set the
script language to Java.



Declaratives
-

these are for page
-
wide variable and method declarations.



Scriptlets
-

the Java code embedded in the page.



Expressions
-

formats the expression as a string for inclusi
on in the output of the page.

We will meet the last JSP element type, actions, soon. These elements follow an XML
-
like syntax, and
perform a function behind the scenes. They provide the means to totally separate presentation from logic.
A good example is
<jsp:useBean .../>

which finds or creates an instance of a bean with the given
scope and name. With the tag extension mechanism to be introduced in JSP 1.1, you'll be able to define
similar action tags and put their functionality in a tag library.

Now let'
s examine the basic elements of a JSP in a more complete fashion, before we code some more.

Something I've found very useful to have around while coding is the syntax crib sheet available at
http://java.sun.com/products/jsp/syntax.html in PDF format. This
has a concise summary of what we'll
see here.

JSP Directives


A JSP directive is a statement that gives the JSP engine information for the page that follows. The general
syntax of a JSP di
rective is
<%@ directive { attribute=”value” } %>
, where the directive may
have a number of (optional) attributes. Each directive has an optional XML equivalent, but these are
intended for future JSP tools, so we won't consider them here.

Possible directiv
es in JSP 1.0 are :



Page


information for that page



Include


files to be included verbatim



Taglib


the URI for a library of tags that you'll use in the page (unimplemented at the time of
writing)

As is to be expected, the page directive has many poss
ible attributes. Specifying these is optional, as the
mandatory ones have default values.



Attribute and possible values

Description

language=”java”

The
language

variable tells the server what
language will be used in the file. Java is the
only supported

syntax for a JSP in the current
specification.

Support for other scripting languages is
available at
http://www.plenix.org/polyjsp

and
http://www.caucho.com

(JavaScript).

extends="package.class"

The
extends

variable defines the parent class
of the genera
ted servlet. It isn't normally
necessary to use anything other than the
provided base classes.

import="package.*,package.class"

The
import

variable is similar to the first
section of any Java program. As such, it should
always be placed at the top of the
JSP file. The
value of the import variable should be a
comma
-
separated list of the packages and
classes that you wish to import.

session="true|false"

By default, the
session

variable is
true
,
meaning that session data is available to a
page.

buffer="none
|8kb|sizekb"

Determines if the output stream is buffered. By
default it is set to 8kb. Use with
autoFlush

autoFlush="true|false"

If set to
true
, flushes the output buffer when
it's full, rather than raising an exception.

Continued on Following Page



At
tribute and possible values

Description

isThreadSafe="true|false"

By default this is set
true
, signaling to the JSP engine
that that multiple client requests can be dealt with at
once. It's the JSP author's job to synchronize shared
state, so that the pag
e really is thread safe.

If
isThreadSafe

is set to
false
, the single thread
model will be used, controlling client access to the
page.

This doesn't let you off the hook, however, as servers
may, at their discretion, have multiple instances of the
page run
ning to deal with the client request load. And
there's no guarantee that consecutive requests from
the same client will be directed to the same instance
of JSP page. Any resources or state that are shared
between page requests must therefore be
synchronize
d.

info="text"

Information on the page that can be accessed through
the page's
Servlet.getServletInfo()

method.

errorPage="pathToErrorPage"

Gives the relative path to the JSP page that will
handle unhandled exceptions. That JSP page will have
isErrorPage

set to true

isErrorPage="true|false"

Marks the page as an error page. We'll see this in
action later.

contentType="text/html;


charset=ISO
-
8859
-
1"

The mime type and character set of the JSP and the
final page. This information must come early in the
file, before any non
-
latin
-
1 characters appear in the
file.

In the JSWDK
-
1.0
-
ea1 release there's a bug
-

the
import

statement needs to be
imports

to satisfy the
JSP engine.

We'll see more of the
include

directive in a later example.

JSP Declarations


A JSP declaration can be thought of as the definition of class
-
level variables and methods that are to be
used throughout the page. To define a declarative block, begin the block of code with

<%!
declaration
>
.


<%!


String var1 = "x";


int count = 0;



private void incrementCount()


{


count++;


}


%>

Note that you put semi
-
colons after each variable declaration, just as if you were writing it in a class.

This

is how you would define the optional
jspInit()

and
jspDestroy()

methods that we mentioned
earlier.

JSP Scriptlets


Scriptlets are defined as any block of valid Java code that resides betw
een
<%

and
%>

tags.

This code will be placed in the generated servlet's
_jspService()

method. Code that is defined within a
scriptlet can access any variable and any beans that have been declared. There are also a host of implicit
objects available to a sc
riptlet from the servlet environment.

Implicit Objects

Description

request

The client's request. This is usually a subclass of
HttpServletRequest
.
This has the parameter list if there is one.

response

The JSP page's response, a subclass of
HttpServletRes
ponse
.

pageContext

Page attributes and implicit objects (essentially what makes up the
server environment in which the JSP runs) need to be accessible through
a uniform API, to allow the JSP engine to compile pages. But each server
will have specific impl
ementations of these attributes and objects.

The solution to this problem is for the JSP engine to compile in code that
uses a factory class to return the server's implementation of the
PageContext

class. That
PageContext

class has been initialized with th
e
request

and
response

objects and some of the attributes from the page
directive (
errorpage
,
session
,
buffer

and
autoflush
) and provides the
other implicit objects for the page request. We'll see more on this in a
moment.

session

The HTTP session object
associated with the request.

application

The servlet context returned by a call to
getServletConfig().getContext()

(see Chapter 5).

out

The object representing the output stream.



Implicit Objects

Description

config

The
ServletConfig

object for the p
age.

page

The page's way of referring to itself (as an alternative to
this

in any Java
code).

exception

The uncaught subclass of
Throwable

that is passed to the errorpage URL.

The following snippet shows both how to get a named parameter from the
reques
t

object, and how to
pass a string to the output stream for the page.


<%


String var1 = request.getParameter("lname");


out.println(var1);


%>

Having discussed implicit objects, we're in a better position to understand the code featured in t
he
comment for the
PageContext

source from the JSP 1.0 reference implementation. This shows the code
that the JSP 1.0 reference engine injects into the JSP page's
_jspService()

method.


public void _jspService(HttpServletRequest request,


Http
ServletResponse response)


throws IOException, ServletException


{


JspFactory factory = JspFactory.getDefaultFactory();


PageContext pageContext = factory.getPageContext(


this, // servlet


request,



response,


null, // errorPageURL


false, // needsSession


JspWriter.DEFAULT_BUFFER,


true // autoFlush


);



// Initialize implicit variables for scripting environment



HttpSession session = pageContext.getSession();


JspWriter out = pageContext.getOut();


Object page = this;



try

{


// Body of translated JSP here...


} catch (Exception e)


{


out.clear();


pageContext.handl
ePageException(e);


} finally

{


out.close();


factory.releasePageContext(pageContext);


}


}

JspFactory

returns the
pageContext

implicit object, from which the other implicit objects are obtained
for the duration of the
_jspServi
ce()

method.


JSP Expressions


A JSP expression is a very nice tool for embedding values within your HTML code. Anything between
<%=

and
%>

tags will be evaluated, converted to a string, a
nd then displayed. Conversion from a primitive type
to a string is handled automatically.


The current price of item A100 is


<%= request.getParameter("price") %>

Something you'll want to note is that the expression doesn't end with a semi
-
colon. Th
at's because the
JSP engine will put the expression within an
out.println()

call for you.

JSP expressions allow you to essentially parameterize HTML (just as you would parameterize a SQL query
that differs by only a couple of values). Again and again, your

code will set up conditions and loops using
a one
-
line JSP scriptlet and then include the HTML code directly beneath it. Simply enclose the beginning
and the ending of the condition or loop in separate pairs of
<%

and
%>

tags :


<% for (int i = 0; i < 1
0; i++)


{ %>


<br>


Counter value is <%= i %>


<% } %>

Coding JSP Pages

A big advantage in developing a JavaServer Page is that you can code the HTML without enclosing it in
Java code, as you must do in a servlet. You can then take
advantage of HTML editors to develop your
content.

So from the drudgery of coding HTML output in servlets, we've arrived at the flexibility of coding Java
snippets into the HTML page. But don't be dragged too far the other way. Heard of the people who can'
t fit
their ASP files into Notepad? And if there's one problem with ASP and JSP, it's debugging the pages,
making improvements to them, and untangling the meaning of that section you coded months back.

The "third way" is a mixture of presentation
-
based cal
ls to a Bean or servlet, which components can be
better tested, are well
-
encapsulated and good OOP citizens. This moves from embedding code to
embedding components and action tags in the page.

Note that the current reference JSP implementations don't autom
atically reload the new Bean if you
recompile while the server is running. You'll need to restart the servlet engine.


Using JavaBeans Components with JSP


So, when setting up an applicati
on development architecture that involves JavaServer Pages, it is a good
idea to try and put all of your business logic inside reusable components. These components can then be
'plugged' into any JavaServer Page that requires them.

What is a JavaBean?

The
Java language has implemented the idea of a component as something called a JavaBean. A JavaBean
is a Java class that fits the following criteria :



Public class.



Public constructor with no arguments.



Public set and get methods to simulate properties. The

get method has no arguments, unless it
acts on an indexed property.

The JavaBeans architecture uses reflection to infer the public methods. But you can provide a
BeanInfo

class, called
BeanName BeanInfo

to give more explicit information.

A bean can also
be serialized and saved for later use. It does this by implementing the
Serializable

interface. When a bean component is serialized, it saves its current state. The current state of a bean is
defined by the current values of its public properties.

Properti
es are always set and retrieved using a common naming convention. For each property, two
methods must exist, a
getxxx()

and
setxxx()

method (where
xxx

is the name of the property).

Apart from that a bean is just like any other Java class. Typically, a bean

component is imported into a
program, its properties are set and its methods are called.

Most of the time beans are used to encapsulate visual and non
-
visual elements of a GUI. There's some
snazzy stuff to link beans up, to implement drag
-
and
-
drop and to
save the state of a bean between
instances. The ultimate aim is to make them part of graphical application assembly tools. I found that
once I stopped thinking about these graphical tools and concentrated on it just being a class that follows a
few design
patterns, things were easier to understand!

Because of this history, Beans don't sit obviously in JSP. Don't get me wrong
-

they work well. But it's not
what they were originally designed for. If we think of them as components, simple encapsulations of Jav
a
code, then their purpose is clearer. Beans mean that your page isn't clogged with code.

Much of the business logic we're hinting at might be better placed in an Enterprise JavaBean, where the
transactions and scaling issues are explicitly the problem of
the container and not the bean. That kind of
heavyweight use may be necessary sometimes but I think that beans will stay around for the lighter work
we show here. And beans as graphical components of your final web page, for a richer user interface, will
b
e another expression of their code
-

and complexity
-
wrapping role.

Letters from a Bean


To show how to use a simple bean, we're going to develop the alphabet example from earlier, and
assoc
iate each letter with a color.

The presentation of the letters will remain the responsibility of the JSP page, but the color mapping will be
the bean's job.


package com.wrox.jspexamples;



import java.awt.Color;


import java.util.*;



public class

AlphabetCode


{


HashMap map;


char c = 0;


Integer colorNumber;


static int FIRST_LETTER = 0x41;


static int ALPHABET_LENGTH = 26;


float s = 0.9f;


float b = 0.9f;



public AlphabetCode()


{


this.m
ap = new HashMap(ALPHABET_LENGTH);



for (int i = 0; i < ALPHABET_LENGTH; i++)


{


this.c = (char)(FIRST_LETTER + i);


float h = (float)i/ALPHABET_LENGTH;


this.map.put(new Character(c), Color.getHSBColor(h,

s, b));


}


}



public void setCharacter(String nextChar)


{


this.c = nextChar.charAt(0);


}



public String getCharacter()


{


return (new Character(this.c).toString());


}



public String
getColor()


{


Color rgb = (Color)map.get(new Character(this.c));


StringBuffer htmlColor = new StringBuffer(


colorNumber.toHexString(rgb.getRGB()


& 0x00ffffff));


// toHexString() won't preserve
leading zeros, so need to add them back in


// if they've gone missing...


if (htmlColor.length() != 6)


{


htmlColor.insert(0, "
\
"#00");


} else

htmlColor.insert(0, "
\
"#");



htmlColor.append
("
\
"");



return htmlColor.toString();


}


}

The bean things to note are the public class and constructor, and the set and get methods. The class sets
up the color of each character in the constructor, and then returns the color of the curre
nt character as an
HTML color.

Letters from a Bean


The modified JSP page looks like :


<html>


<head>


<title>


Color demo I</title>


</head>


<body>



<!
--

This page gener
ates a series of rainbow colored letters
--
>


<!
--

It uses plain method calls to the Bean
--
>



<%@ page language="java" %>



<%! char c = 0; %>



<jsp:useBean id="letterColor" scope="application"


class="com.wrox.jspexamples.AlphabetCode"
/>


<%


for (int i = 0; i < 26; i++)


{


for (int j = 0; j < 26; j++)


{


c = (char)(0x41 + (26
-

i + j)%26);


Character thisChar = new Character(c);


letterColor.setCharacter(thisChar.toString()
);


%>



<
FONT

COLOR
=<%= letterColor.getColor() %> >


<%= letterColor.getCharacter() %>


</font>


<%


}


%>


<BR>


<%


}


%>


</body>


</html>

Giving the result :


The JSP page uses straight method calls (

setCharacter()
,
getCharacter()

and
getColor()
) to
the bean. The only new syntax is
<jsp:useBean ... />
, so let's look at that :


<jsp:useBean id="letterColor" scope="application"



class="com.wrox.jspexamples.AlphabetCode" />

The
jsp:useBean

tag first searches for a bean instance that matches the scope and class. If it can't find
one it instantiates the named class, using its public, no
-
args constructor. If there are any initiali
zation
time properties you need to set, you can place the appropriate tags between
<jsp:useBean>

and
</jsp:useBean>

-

these are only executed when a new instance is created.

Letters from a
Bean

In the JSP 1.0 reference implementation, the server looks for your packages in the
CLASSPATH

environment variable (on my machine,
startserver

includes
jsp1.0/examples/WEB
-
INF/jsp/beans
). Let's look at the attributes :

useBean

Attributes

Description

id="name"

The name by which the instance of the bean can be referenced
in the page. Other Bean tags use
name

to refer to it, so do note
the difference.

scope="page"

The scope over which the bean can be called. More below.

class="package.class"

Fully qu
alified name of the bean class. Because this needs to
be the full name, you don't need to
import

these classes in the
page

directive.

beanName="name"

This allows you to specify a serialized bean (
.ser

file) or a
bean's name that can be passed to the
insta
ntiate()

method
from the
java.beans.Beans
. Needs an associated
type

tag
rather than
class
.

type="package.class"

A synonym for
class

that is used for
beanName
.

Scope on a JSP page goes something like this :

Scope

Description

page

This is the scope of the

PageContext

object we saw earlier. It lasts
until the page completes, and there is no storage of state.

page

wasn't implemented in the EA1 reference version of JSP 1.0.

request

The bean instance lasts for the client request. The named bean
instance is av
ailable as an attribute of the
ServletRequest

object,
so it follows the request to another servlet / JSP if control is
forwarded.

session

The bean lasts as long as the client session. A reference is
available through
HttpSession.getValue(
name
)
.

Don't use
this scope level if you have declared the page directive
session

false
.

application

The bean instance is created with the application and remains in
use until the application ends. It's an attribute of the
ServletContext

object.

We're not finished in our

tour of tags, though. With a couple of changes to our JSP code, we can show you
two more
-

jsp:getProperty

and
jsp:setProperty

:

<html>


<head>


<title>


Color demo


</title>


</head>


<body>


<!
--

This page generates a series of rainbow col
ored letters
--
>


<!
--

It uses the JSP property set and get tags
--
>


<%@ page language="java" %>


<%! char c = 0; %>


<jsp:useBean id="letterColor" scope="application"

class="com.wrox.jspexamples.AlphabetCode" />


<%


for (int i

= 0; i < 26; i++)


{


for (int j = 0; j < 26; j++)


{


c = (char)(0x41 + (26
-

i + j)%26);


Character thisChar = new Character(c);


%>


<jsp:setProperty name="letterColor" property="Character"


va
lue="<%= thisChar.toString() %>" />



<FONT COLOR=<jsp:getProperty name="letterColor" property="Color" /> >


<jsp:getProperty name="letterColor" property="Character" />


</FONT>



...

The
<jsp:setProperty ... />

needs the bean name, the property n
ame and the value with which to
set the property. Note that the property name is the
setCharacter()

method without the prefix.
<jsp:getProperty ... />

is self
-
explanatory.

We'll see an alternative version of the
jsp:setProperty

tag later.

A File Viewer

There's a little too much code in the page in the last example. It shows off the various JSP elements quite
well, but it's not a particularly good design.

If we look at the MVC pattern, we s
ee that the bean is the model, and the page is both the view and the
controller (using the HTTP request). The question is how to prevent the control code taking over the page.
Ideally, the page should be almost all presentation with simple JSP elements (me
thods and properties) to
control the bean. Then the code to actually manipulate the model is in the bean, but the control rests with
the page.

To show this simply, let's design a file viewer bean that will read a specific directory (

C:
\
jdk1.2.1
) and
outpu
t the file and directory names, their size (if they're files) and their timestamp :


package com.wrox.jspexamples;



import java.io.File;


import java.util.Date;


import java.util.Iterator;


import java.util.Vector;



public class FileViewerBea
n


{



File myDir;



File[] contents;



Vector vectorList;



Iterator currentFileView;



File currentFile;





public FileViewerBean()



{



myDir = new File("C:
\
\
jdk1.2.1");



vectorList = new Vector();




}





public String getDirectory()



{



return myDir.getPath();



}





public void refreshList()



{



contents = myDir.listFiles();



vectorList.clear();





for (int i = 0; i < contents.length
; i++)



vectorList.add(contents[i]);





currentFileView = vectorList.iterator();



}





public boolean nextFile()



{



while (currentFileView.hasNext())



{



currentFile = (File)currentFileVi
ew.next();



return true;



}



return false;



}





public String getFileName()



{



return currentFile.getName();



}






public String getFileSize()



{



return new Long(currentFil
e.length()).toString();



}




public String getFileTimeStamp()



{



return new Date(currentFile.lastModified()).toString();



}





public boolean getFileType()



{



return currentFile.isDirectory();



}


}

The idea here is to make the interface to the bean as simple as possible, so we need to support the
different requirements of JSP pages. Therefore, a full
-
featured file viewer bean might have further
methods to detail a file's attributes, order the vecto
r by different criteria and so forth.

To put the bean to work, we have the
FileView.jsp

code :


<html>


<head>


<title>


A JSP file viewer


</title>


</head>


<body>



<!
--

This page allows you to see files in selected parts of the drive
-
-
>



<%@ page language="java" %>



<jsp:useBean id="fileViewer" scope="session"
class="com.wrox.jspexamples.FileViewerBean" />



<hr>



<jsp:getProperty name="fileViewer" property="Directory" />



<table>


<%



fileViewer.refreshList();





while(fileViewer.nextFile())



{


%>


<tr>


<td>


<%= fileViewer.getFileName() %>


</td>


<td>


<%



if (!fileViewer.getFileType()) {


%>


<%= fileViewer.getFileSize() %>


<%



}


%>


</td>


<td>


<%= fileViewer
.getFileTimeStamp() %>


</td>


</tr>


<% } %>




</table>


</body>


</html>

The page is much clearer here, with the bean almost acting as an iterator over the directory listing. The
fine
-
grained properties simplify the page further.


Browsing and Querying Databases

To illustrate more functionality of JavaServer Pages, we will build an example that allows you to select
from available databases, see their tables and column nam
es, and query them. Our JSP application will
make use of a bean to access the database.

I tried various architectures as I developed the application, as I saw how things panned out and learnt
how to use more tags! The client's request should yield a
Result
Set

object, which has 22
getXxx()

methods to work with. And I'm not interested in wrapping all that functionality in a bean when it's already
available, so I've decided to handle it in the page.

This is where the tag extension mechanism will prove its wort
h
-

in that we'll be able to define actions that
return
ResultSet

objects for the page to retrieve values from. Similar tags will enable JSPs to handle
iterators.

Working backwards, let's look at the requirements :



Consistent web interface across pages.



S
elf
-
contained parts of the application each have their own JSP.



Central error handling.



Same bean code used for different client requests.

The parts of the project are :



DataList.jsp
-

the main application page. It decodes GET requests and passes them t
o the
relevant pages.



List1.jsp
-

Presents the available databases. This is fixed by the sys
-
admin and is essentially
HTML.



List2.jsp
-

Shows the tables and their columns for the selected database.



List3.jsp
-

Shows the result of a SQL query.



ErrorPage
.jsp
-

Where all the exceptions end up, their origin suitably noted.



Header.html
-

The header for all the application pages.

Ask the Right Question

We want one page to coordinate the ap
plication, to receive all requests. We'll start
DataList.jsp

for the
case where the client has just typed
http://URL/DataList.jsp

and needs the menu of databases.


<%@ page language="java" errorPage="ErrorPage.jsp" %>


<% if (request.getParameterNames(
).hasMoreElements() == false)



{ %>


<jsp:forward page="/jsp/data/List1.jsp" />


<% } %>


We have already sorted out an
errorpage

in the
page

directive for all error reporting. We'll repeat this
code on each page.

The
request

object will have
no parameters for the plain URL, so we can forward the work to the
List1.jsp

page. The
<jsp:forward ... />

needs the URL relative to the servlet context /
application. In this case that's
/jsp/employee/List1.jsp
.

DataList.jsp is only used for checking para
meters and forwarding to the right page, you
can use a regular servlet instead. You can mix and match servlets and JSPs depending
which is most appropriate.

Now we'll code the
List1.jsp

page that shows the client what choice they have. From this they can
f
ormulate a query, or browse those databases :


<html>


<head>


<title>


Database Search


</title>


</head>


<body>


<%@ page language="java" errorPage="ErrorPage.jsp" %>


<%@ include file="Header.html" %>


Choose database:


<form meth
od="GET" ACTION="DataList.jsp">


Database URL:<select name=DbURL size=1>


<option>jdbc:odbc:employee


<option>jdbc:odbc:techlib


<option>jdbc:odbc:this_should_break


</select>


<p>


Database driver:<select name=DbDriver size=1>


<option>sun
.jdbc.odbc.JdbcOdbcDriver


<option>com.imaginary.sql.msql.MsqlDriver


</select>


<p>


Input SQL if you know your query:


<p>


<input type=text name=inputSQL size=40>


<p>


<input type=submit>


</form>


</body>


</html>


This is strai
ghtforward HTML for the most part. We've sorted out the consistent web interface for the
application by using the
<%@ include %>

tag to include
Header.html
. I've put in databases and a
driver I don't have, to test the error handling later.

It should be pos
sible to make this a static HTML file, but the JSP engine complained mightily when I tried.
I'm informed that support for forwards to a static page is a matter of interpretation of the specification at
this time. Something for the future.

Finally, we have
specified that
DataList.jsp

receives any submissions from this page.

To make this example a bit more realistic, we could let the main page (or servlet) create beans with all
available databases and drivers and let this page use the bean to generate the dat
abase option lists.

Querying a Database

Assuming all three sections of the
List1.jsp

form are filled in,
DataList.jsp

needs to forward the
request to a page that can display the
ResultSet

of the query, or report why it didn't work.

The changes to
DataList.jsp

are the following :


<%@ page language="java" errorPage="ErrorPage.jsp" %>




<% if (request.getParameterNames().hasMoreElements() == false)


{ %>




<jsp:forward page=
"/jsp/data/List1.jsp" />





<% } else if ((request.getParameter("DbDriver") != null) &&



(request.getParameter("DbURL") != null) &&



(request.getParameter("inputSQL") != null))


{ %>


<jsp:forward page="/jsp/data/
List3.jsp" />




<% } %>


Browsing our JDBC tutorials, we start to construct a bean that can connect to the specified database URL
with the specified driver, query the database, and provide suitable methods to display the returned data.
Plus if it all

goes wrong the error handling needs to be informative.


package com.wrox.jspexamples;




import java.sql.*;


import java.io.*;




public class DbBean


{



String dbURL;



String dbDriver = "sun.jdbc.odbc.JdbcOdbcDriver";



pri
vate Connection dbCon;


public DbBean()


{



super();



}


public boolean connect() throws ClassNotFoundException, SQLException


{



Class.forName(this.getDbDriver());



dbCon = DriverManager.getConnection(this.getDbURL()
);



return true;



}


public void close() throws SQLException


{



dbCon.close();



}


public ResultSet execSQL(String sql) throws SQLException


{



Statement s = dbCon.createStatement();



ResultSet r = s.exe
cuteQuery(sql);



return (r == null) ? null : r;



}


public String getDbDriver()


{



return this.dbDriver;



}


public void setDbDriver(String newValue)


{



this.dbDriver = newValue;



}


public String getDbU
RL()


{



return this.dbURL;



}


public void setDbURL(String newValue)


{



this.dbURL = newValue;



}


}


Note that we've not wrapped the
ResultSet
, as it's the view of the data that the page will manipulate.

There's also
no error handling in here, bar the mandatory declaration of
throws

clauses. The context of
the error is better handled in the JSP where the call was made.

Let's write a test class to test out the methods and get the error handling sorted :


import java.s
ql.*;


import com.wrox.jspexamples.DbBean;




public class TestDbBean


{





public static void main(String[] argc)


{



DbBean myDbBean = new DbBean();





myDbBean.setDbDriver("sun.jdbc.odbc.JdbcOdbcDriver");




myDbBean.setDbURL("jdbc:odbc:techlib");


int numColumns = 0;



String sql = "select authid, lastname, firstname from authors";


ResultSet rs = null;



ResultSetMetaData rsmd = null;


try {



myDbBean.connect();



} ca
tch (ClassNotFoundException e)


{



System.out.println("connect() ClassNotFoundException: " + e);



} catch (SQLException e)


{



System.out.println("connect() SQLException: " + e);



}


try


{



rs =
myDbBean.execSQL(sql);



} catch (SQLException e)


{



System.out.println("execSQL() SQLException: " + e);



}


try


{



rsmd = rs.getMetaData();



numColumns = rsmd.getColumnCount();


for (int co
lumn = 1; column <= numColumns; column++)


{



System.out.println(rsmd.getColumnName(column));



}


while (rs.next())


{



for (int column = 1; column <= numColumns; column++)


{



System.out.p
rintln(rs.getString(column));



}



}



myDbBean.close();



} catch (SQLException e)


{



System.out.println("Problems with the database
-

SQLException: " + e);



}



}


}

Now we know it wo
rks, we can reuse the logic in the JSP file, and we've got the makings of the error
handling. We want to tell the client what went wrong, and show them how to correct it if possible.

The possible errors are :



The drivers for the database are unavailable (

ClassNotFoundException
)
-

which only a swift
kick to the system administrator will remedy.



The
DriverManager.getConnection()

fails because the
dbURL

is incorrect
-

another job for the
sys admin.



The call to
execSQL()
can throw a
SQLException

if the query

is invalid
-

that's something that
the user should be able to sort out.



The rest are database or page errors, and should be shown as such.

Finally, we can code
List3.jsp

:


<HTML>



<HEAD>



<TITLE>



Database Search


</TITLE>



</HEAD>



<
BODY>





<%@ page language="java" import="java.sql.*" errorPage="ErrorPage.jsp" %>




<%@ include file="Header.html" %>




<jsp:useBean id="db" scope="request" class="com.wrox.jspexamples.DbBean" />




<jsp:setProperty name="db" proper
ty="*" />




<%! int numColumns;



ResultSet rs = null;



ResultSetMetaData rsmd = null;


%>




<CENTER>



<
H2
>Results from</
H2
>


<
H2
><%= request.getParameter("inputSQL") %></
H2
>


<
HR
>


<BR><BR>



<
TABLE

BORDER
="1"
BGCOLOR
="#cccc99"
BORDERCOLOR
="#003366">


<TR>





<%



String sql = request.getParameter("inputSQL");





try


{



db.connect();



} catch (ClassNotFoundException e)


{



throw new ServletException("Database drivers not
available", e);



} catch (SQLException e)


{



throw new ServletException("Database URL is wrong", e);



}




Continued on Following Page



try


{



rs = db.execSQL(sql);



} catch (SQLException e)


{




throw new ServletException("Your query isn't working. " +



"Do you want to browse the database? " +



"If so, leave the SQL input empty", e);



}





try


{



rs
md = rs.getMetaData();



numColumns = rsmd.getColumnCount();





for (int column = 1; column <= numColumns; column++)


{


%>




<
TH
><%= rsmd.getColumnName(column) %></
TH
>




<%



}


%>




</TR>





<%




while (rs.next())


{


%>




<
TR
>




<%



for (int column = 1; column <= numColumns; column++)


{


%>




<
TD
><%= rs.getString(column) %></
TD
>




<% } %>


</TR>



<% }



rs.close();



db
.close();



} catch (SQLException e)


{



throw new ServletException("Database error. The query worked, " +



"but the display didn't", e);



}


%>


</TABLE>



</CENTER>


<P>



<
FORM

ME
THOD
="GET"
ACTION
="DataList.jsp">


<
INPUT

TYPE
=hidden
NAME
=DbDriver
VALUE
=<%= request.getParameter("DbDriver") %>>


<
INPUT

TYPE
=hidden
NAME
=DbURL
VALUE
=<%= request.getParameter("DbURL") %>>




Input SQL:<
INPUT

TYPE
=text
NAME
=inputSQL
SIZE
=40>



<P>





<
INPUT

TYPE
=submit>




</FORM>



</BODY>



</HTML>


There are a couple of noteworthy things in the code. First is the alternative use of
jsp:setProperty

tags.


<jsp:setProperty name="db" property="*" />

This matches request paramete
rs with the property names of the
db

bean instance.
DbDriver

and
DbURL

will match, and so both these will be set with the values in the request.

This is a shorthand form of


<jsp:setProperty name="
beanName
" property="
propertyName
" param="
parameterName
" /
>

where
propertyName

and
parameterName

are identical.

In order that users can requery the database we have an input form that uses the current
DbURL

and
DbDriver

values as hidden input and takes a new SQL query.

The error handling is split into the connect
ion, querying and display parts of the page, each throwing a
different
ServletException

that the
ErrorPage.jsp

file will display.

The errorpage for the application must declare itself with the
isErrorPage

page directive set to
true
.
Then we output the impl
icit
exception

object. And just to see where we came from and what we've
brought with us, we cycle through the parameter and attribute names.


<HTML>



<TITLE>



DataList Error Page


</TITLE>



<BODY>





<%@ include file="Header.html" %>





<!
--

Need an error page to handle the exception message
--
>


<!
--

What error page does an error page use?
--
>


<%@ page language="java" isErrorPage="true" import="java.util.*, java.sql.*" %>




<BR>


<
H4
>Exception details:</
H4
>


<P>



<!
--

The fully
-
qualified class that is the exception
--
>


<%= exception.toString() %>


<BR>



<!
--

The exception's message to the world
--
>


<%= exception.getMessage() %>


<P>





Continued on Following Page


<
A

href="DataList.jsp">Want t
o try again?</
A
>




<
P
>




<%! Enumeration parameterList; %>


<%! Enumeration attributeList; %>




<P>



<
H4
>Parameter listing: </
H4
>


<P>





<%



parameterList = request.getParameterNames();



while (parameterList.hasMo
reElements()) {


%>




<%= parameterList.nextElement().toString() %> <
BR
>




<% } %>




<P>



<
H4
>Attribute listing: </
H4
>


<P>





<%



Enumeration attributeList = request.getAttributeNames();



while (attributeList
.hasMoreElements()) {


%>




<%= attributeList.nextElement().toString() %> <
BR
>




<% } %>




</BODY>



</HTML>


Browsing Databases

What if the user selects one of

the available databases, and the driver, but doesn't enter an SQL query,
because they don't have the table details. We need to decode that, and then show the user the database
information they need to form a query.

First off, how do we look at the tables
of a database? Reaching for Chapter 19 of Beginning Java 2, I see
that you need the metadata of the connection
-

encapsulated in the
java.sql.DatabaseMetaData

object. The following code snippet shows this :


private DatabaseMetaData dbMetaData;


stati
c String[] tableTypes = {"TABLES"};




...




dbMetaData.getTables(catalog, schema, tableName, tableTypes)


dbMetaData.getColumns(catalog, schema, tableName, columnName)


Here are the two new
DbBean

methods we need to return the appropriate
Res
ultSet
s to the JSP,



public ResultSet getTables() throws SQLException


{



dbMetaData = dbCon.getMetaData();



return dbMetaData.getTables(null, null, null, tableTypes);



}


public ResultSet getTable(String tableName) throws SQ
LException


{



return dbMetaData.getColumns(null, null, tableName, null);



}


with
tableTypes

and
dbMetaData

declared as above. Note how both methods can throw a
SQLException
.

Having added the declarations and methods to the
DbBean

class, he
re's the new code for the test bean :



while (rs.next())


{



for (int column = 1; column <= numColumns; column++)


{



System.out.println(rs.getString(column));



}



}



}




catch (SQLException e)


{



System.out.println("Problems with the database
-

SQLException: " + e);



}


// Test for List2.jsp



ResultSet tables = null;


try


{



tables = myDbBean.getTables();



}




catch (SQLException e)


{



System.out.println("getTables() SQLException: " + e);



}


try


{



while (tables.next())


{



String current_table = tables.getString("TABLE_NAME");



System.o
ut.println(current_table);



System.out.println(" ");


ResultSet table = myDbBean.getTable(current_table);



while (table.next())


{



System.out.println(table.getString("COLUMN_NAME"));



}


S
ystem.out.println(" ");



}


myDbBean.close();


} catch (SQLException e) {



System.out.println("Table listing failed: " + e);



}



}


}


From this, we can create the
List2.jsp

file. This will display every table in th
e database, plus the names
of the columns in that table.


<HTML>



<HEAD>



<TITLE>



Database Details


</TITLE>



</HEAD>



<BODY>





<%@ page language="java" import="java.sql.*" errorPage="ErrorPage.jsp" %>




<%@ include file="H
eader.html" %>




<jsp:useBean id="db" scope="request" class="com.wrox.jspexamples.DbBean" />




<jsp:setProperty name="db" property="*" />




<%! int numColumns;



String current_table;


%>




The database (<%= request.getParam
eter("DbURL") %>) you have selected has the
following tables:




<%



try


{



db.connect();



} catch (ClassNotFoundException e)


{



throw new ServletException("Database drivers not available", e);



} catch (SQLExcep
tion e)


{



throw new ServletException("Database URL is wrong", e);



}





try


{



ResultSet tables = db.getTables();



while (tables.next())


{



current_table = tables.getString("TABLE_NAME");



%>


<P>



<
H4
><%= current_table %> </
H4
>


<
TABLE

BORDER
="1"
BGCOLOR
="#cccc99"
BORDERCOLOR
="#003366">


<TR>



<%



ResultSet table = db.getTable(current_table);



// Loop through columns and get their names and characterist
ics



while (table.next())


{


%>


<
TD
><%= table.getString("COLUMN_NAME") %></
TD
>


<%



}


%>




</TR>



</TABLE>







<%



table.close();



}



tables.close();



db.close();



} c
atch (SQLException e)


{



throw new ServletException("Database problems", e);



}


%>






<P>







<
FORM METHOD
="GET"
ACTION
="DataList.jsp">






<
INPUT

TYPE
=hidden
NAME
=DbDriver
VALUE
=<%= request.getParameter("DbDriver") %>>


<
INPUT

TYPE
=hidden
NAME
=DbURL
VALUE
=<%= request.getParameter("DbURL") %>>






Input SQL:<
INPUT

TYPE
=text
NAME
=inputSQL
SIZE
=40>






<P>







<
INPUT

TYPE
=submit>






</FORM>



</BODY>



</HTML>


When you have a text field, the br
owser will always send a parameter with the same name as the text
field element. The value is an empty string if the user has not entered anything, but it's hard to see if
there are just a couple of space characters in the field. To check if
inputSQL

conta
ins something that
could be SQL, you need to check for a non
-
empty string that isn't all space characters. I've created a
convenience method in
DataList.jsp

to do this. The code for the final
DataList.jsp

looks like :


<%@ page language="java" errorPage=
"ErrorPage.jsp" %>


<%! boolean emptySQL(String sql)


{



if (sql != null)


{



if (sql.trim().length() == 0)



return true;



else



return false;



}



return true;



}


%>




<% if (request.getParameterNames().hasMoreElements() == false)


{ %>


<jsp:forward page="/jsp/data/List1.jsp" />


<% } else if ((request.getParameter("DbDriver") != null) &&



(request.getParameter("DbURL") != null) &&



(emptySQL(request.getParameter("inputSQL"))))


{ %>


Continued on Following Page


<jsp:forward page="/jsp/data/List2.jsp" />



<% } else if ((request.getParameter("DbDriver") != null) &&


(request.getParameter("DbURL") != n
ull) &&



(emptySQL(request.getParameter("inputSQL")) == false))


{ %>



<jsp:forward page="/jsp/data/List3.jsp" />



<% } %>


Mixing Servlets and JSPs

As we mention
ed at the start of this example, it's very easy to mix JSP and servlets as and when the need
arises. An ideal candidate is the
DataList.jsp

file which just forwards requests to the right JSP page.

To recast the code as a servlet, we need to use
RequestDisp
atcher

classes. We'll look at these in more
detail in Chapter 11, but they are essentially the servlet equivalent of the
<jsp:forward ... />

and
<%@ include ... %>

tags we've used previously. We need one instance for each forward request.

Remember to chang
e the
ListX.jsp
files to point to the servlet. I installed the servlet in
examples
\
WEB
-
INF
\
servlets

of the reference implementation and pointed my browser at
http://localhost:8080/examples/servlet/DataList
.


import java.io.*;


import javax.servlet.*;



import javax.servlet.http.*;




public class DataList extends HttpServlet


{





public void doGet(HttpServletRequest request, HttpServletResponse response)



throws ServletException, IOException


{






R
equestDispatcher firstListRD = this.getServletContext().



getRequestDispatcher("/jsp/data/List1.jsp");



RequestDispatcher secondListRD = this.getServletContext().




getRequestDispatcher("/jsp/data/List2.jsp");



RequestDispatcher thirdListRD = this.getServletContext().



getRequestDispatcher("/jsp/data/List3.jsp");





if (request.getParameterNames().hasMore
Elements() == false)


{



firstListRD.forward(request, response);



} else if ((request.getParameter("DbDriver") != null) &&



(request.getParameter("DbURL") != null) &&



(emptySQL(request.getParamet
er("inputSQL"))))


{



secondListRD.forward(request, response);



} else if ((request.getParameter("DbDriver") != null) &&



(request.getParameter("DbURL") != null) &&





(emptySQL(request.getParame
ter("inputSQL")) == false))


{



thirdListRD.forward(request, response);



}



}





boolean emptySQL(String sql)


{



if (sql != null)


{



if (sql.trim().length() == 0)



return true;




else



return false;



}



return true;



}


}


Note that you can't forward the request if you have an open output stream or writer. This means that you
can't include a file and then forward the request, and so th
e
DataList

servlet can't act as a template file.



Enhancing the User Interface with Applets and Beans

The
<jsp:plugin> ... </jsp:plugin>

tag included in JSP 1.0 (but not currently implem
ented)
allows you to specify an applet's or bean's place in the final page. For example :


<jsp:plugin type="applet" code="NervousText.class"


codebase="/applets/NervousText"



height="50" width="375" />




<params>


<param name=
text value="<%= someObject.getWhackyText() %>" >


</params>


<fallback> <p>It's messed up
-

apologies</p> </fallback>




</jsp:plugin>


This holds out the potential of creating very rich, dynamic user interfaces within a web page.

Summary

JavaServ
er Pages technology brings together the power of Java Servlets and the ease of HTML coding to
give developers a powerful method in which to create server
-
side web applications. It is currently one of
the most exciting and fast
-
changing topics in the Java w
orld.

In this chapter, we learned the following :



A JavaServer Page file consists of standard markup tags, content, JSP directives, JSP
declarations, JSP scriptlets and actions tags.



The JSP engine performs a first
-
time compile on a JSP file which generat
es a new servlet with the
desired content. If the file changes, then a new servlet is generated, otherwise the compiled
version is used.



JavaBeans can be used within a JSP file to help split presentation and logic into their component
parts.