[difficulty: expert]
In this article I will introduce a simple and
convenient strategy to manage the server-side validation of data inserted by
the user in a web application through the mechanism of exceptions offered by
the Java language and JSP standard. (3338
words; 2 June 2002)
by Luigi
R. Viggiano
JSP (Java
Server Pages) technology is a widely used standard for the development of
enterprise web applications. JSP integrates technologies like JDBC and EJB so
that it offers the developer a complete framework upon which to build complex
internet applications based on Java.
Most web
applications, if not all, require the user to enter data which eventually finds
its way to a database. This implies that the data should be validated so as to
prevent insertion of data which is inconsistent or wrong. Although it is
possible to perform the validation on the client side using a browser with
simple JavaScript routines, there is such a extreme variation in browsers that
this approach is risky because it does not guarantee the correct execution of
the client side scripts. Therefore, it is considered good practice to validate
the data on the server side.
Recognizing
that server side validation is very useful and the existence of a multitude of
diverse web applications which perform this task in different ways, JSP does
not enforce any particular approach to deal with this recurring aspect of web
applications.
|
|
Figure 1 – validation with error page |
Figure 2 – validation with errors reported in
the form |
In the
examples shown above in Figure 1 and Figure 2 implement the Model
View Controller (MVC) Pattern. Notice how I have implemented the general
preference to separate the business logic called “Controller”
(validate.jsp) from the presentation pages called “View” (error.jsp,
form.jsp and target.jsp). The Model classes which would represent the
internal state of the application are not shown. These Model classes are
generally kept by JavaBeans valid for the whole http session.
The Model 2
architecture (see references) anticipates that controllers, that are the
non-visual parts, may be implemented by servlets, However, here I am using JSP
only since in the context of this article there are not remarkable differences.
Moreover, this presentation is intended to simplify the regeneration of the
environment and allow for a faster deployment. For further informations about
the server-side design patterns and about relative methodologies please refer
to the documentation on architectures like Jakarta Struts framework ( http://jakarta.apache.org/struts/index.html
).
Form.jsp
shows the HTML form inside the browser. When the user presses the submit
button, the inserted data is sent to the validate.jsp page. This page
validate.jsp acts as a controller so it does not produce any output, on the
contrary it encapsulates the business logic. It performs the data validation
and consequent entry of the data into the database. Finally it redirects the
validate.jsp to the target.jsp which shows the user a notification message
about the successful completion of the transaction.
If the user
has entered invalid data, validate.jsp handles a validation failure by
redirecting the page to “error.jsp” which will subsequently show the error to
the user (see Figure 1). At this point the user will return to the
form.jsp through a hyperlink so the data can be re-entered correctly.
An
alternative is shown in the example illustrated in Figure 2. In this
approach the user does not have to look for his mistakes but instead the errors
are highlighted when the user is redirected to the form which is now updated
with the appropriate error messages, preserving approved data. Even though this
is an improvement from the usability aspect, mostly when there are many fields
in the form and you don’t want that the user wastes time trying to understand
where he is doing wrong, it implies an increase of complexity from the
developer perspective. For example, the form.jsp must now to deal with the
display of contents and the positioning of the error messages.
The following
is an illustration of the last case in detail.
You may
notice that in the example shown in figure 2, the user first enters the
data, then after pressing the submit button he is redirected back to the
form.jsp until all the inserted data is acceptable, and finally the flow of the
program proceeds.
The classic
approach to implement this mechanism is using JSP forwarding to bounce back the
user to the form when data is wrong, displaying the error messages near to the
rejected fields. To do that, the programmer often falls in the trap of using
cryptic error codes, often ambiguous, which compromises the maintenance of the
application. Also sometimes, performing the error handling in different
locations is commonly implemented using vectors of codes or other complicated
tricks which are not always very clean and could lead to the introduction of
bugs.
In summary,
this a sort of “do-it-yourself” error management used in old procedural
languages. And the state of the error is maintained through numerical codes and
the management is done with “if-then-else” statements which are frequently
cascaded. This is obviously not suitable for object oriented code.
The
solution that I am presenting in this article is to employ exception handling
functionality which is offered by the Java language. Because JSP technology is
based upon Java this exception handling ability can be used to develop clear
code that is easier to maintain.
The goal
one wants to achieve is to manage error as follows:
Example: “Error validating submission”
Example: “Erong date: MM-dd-yyyy”
All this is
done in the validate.jsp code shown in Figure 3. Notice that the code in the
panel of validate.jsp is just a sample. In the reality the code would be
slightly more complex, but the effect and process would be the same, as we will
see later.
Figure 3 – flow of the exception
Form.jsp
collects the data, then, on submit, validate.jsp is executed and raises an
exception in case of unacceptable data. At this point the exception is caught
and managed by form.jsp that now contains validation errors and precompiled
fields when they are accepted by the checks.
Inside the
code contained in validate.jsp, on the first line an instance of
ValidationException class is created and then, in subsequent checks, we add
error messages related to invalid fields invoking method addMessage(). After
having checked all fields, it is necessary to raise the exception using the
keyword throw in case of at least one of inserted data is invalid; otherwise
(all fields are valid) a forward to the confirmation page is made.
When an
exception thrown by the web application reaches the servlet container, this is
forwarded to the exception-page that will manage the mechanism just
illustrated. Generally, in JSP based application, the exception-page is only
used to print the stack trace of un-handled exceptions, that is when the
exception is not caught is automatically sent to the exception page (if
defined), that generally the stack of method invocations that produced the
error, inside the HTML given to the browser.
The
following will illustrate how the exception page can do something much more
useful.
First of
all we have to write the code of a “presentation exception” that distinguishes
itself from the errors that could occur for unexpected conditions
(RuntimeExceptions). This exception will be named ValidationException and will
extend from ServletException.
As usual it
will be present the error message, but this exception needs to be able to
contain also several error messages related to fields that missed validation
checks. This is done by the member instance “messages” that is a Map (interface
of Hashtable and HashMap).
The
exception will also contain an instance variable called “errorPage”, a String,
that indicates to which URL the error messages shall be sent to be shown in the
browser. Finally we define the method raise(), with the responsibility to throw
the exception, if and only if there are some error messages to be shown. If
there are no messages, it means that all the validation checks had success,
then the exception must not be thrown and the program flow can continue forward
(for example, recording data in the database).
Listing 1 – ValidationException.java (partial)
public class ValidationException extends ServletException {
private String errorPage;
private Map messages;
public ValidationException(String
msg, String errorPage) {
super(msg);
setErrorPage(errorPage);
}
public java.util.Map
getMessages() {
if (null ==
messages)
messages = new HashMap();
return
messages;
}
public void addMessage(String message,
String position) {
getMessages().put(position, message);
}
public void raise() throws ValidationException {
if (null !=
messages && ! messages.isEmpty())
throw this;
}
}
After this,
we obtain an exception-page able to catch only ValidationException, executing
the forward (line 9) to the page entrusted to show the errors contained in the
exception:
Listing 2 – ValidationErrorPage.jsp
<%@ page language="java"
contentType="text/html"
import="net.jx.servlet.ValidationException"
isErrorPage="true"
%><%
ValidationException valEx =
(ValidationException)exception;
String errorPage = valEx.getErrorPage();
if (null != errorPage)
pageContext.forward(errorPage);
else
throw new
ServletException("ErrorPage not set",
valEx);
%>
Obviously
our servlet container have to be set up to send to this error-page only the
validation exceptions. We can do that modifying opportunely the descriptor
web.xml as shown in listing 3.
Web.xml is
a configuration file that allows us to specify several parameters that will be
valid in the context of our web application. This file needs to be saved in the
directory WEB-INF created in the root of the web tree. Beyond to exception
pages, it is also possible to specify the welcome-files (generally index.html,
index.jsp, default.html etc…), the mapping between URL and servlets, the
tag-libraries, MIME-TYPES and other security information. Once created the
application tree with the configuration file WEB-INF/web.xml, we can pack it in
a file with extension “.war” and distribute it for the deploy in any web
container implementing the spec.
Listing 3 – web.xml
<?xml version="1.0"
encoding="UTF-8"?>
<!DOCTYPE
web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application
2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd" >
<web-app>
<error-page>
<exception-type>net.jx.servlet.ValidationException</exception-type>
<location>/error/ValidationErrorPage.jsp</location>
</error-page>
</web-app>
So, we have
now indicated to the servlet container to send any exception of type
net.jx.servlet.ValidationException to the URL /error/ValidationErrorPage.jsp.
All the other exceptions will be sent to the default error-page which will
simply print the stack trace. When an exception of type ValidationException
will be caught from the servlet container, it will be sent to
ValidationErrorPage.jsp that works as a dispatcher.
In this
case, if Validate.jsp finds that
the data are not acceptable, it will throw an exception of type
ValidationException containing ”Form.jsp” as error-page that will show error
messages.
Listing 4 – validate.jsp
<%@ page language="java"
%>
<%@ page import="java.text.*"
%>
<%@ page import="java.util.Date"
%>
<%@ page import="net.jx.sample.SampleBean"
%>
<%@ page import="net.jx.servlet.ValidationException"
%>
<%
//
Clear any old bean and create a new one in session keeping data from
//
parameters in the request.
session.removeAttribute("sampleBean");
SampleBean sampleBean = new SampleBean();
session.setAttribute("sampleBean",
sampleBean);
// Get submitted fields
String textField =
request.getParameter("mandatoryTextField");
String dateField =
request.getParameter("dateField");
String currencyField =
request.getParameter("currencyField");
//
Instantiate the exception with the default message
ValidationException
validationEx =
new
ValidationException("Error validating
submission", "/Form.jsp");
//
Mandatory text field validation.
if (null == textField ||
textField.length() == 0)
validationEx.addMessage("Mandatory
field", "msgMandatoryField");
else
sampleBean.setMandatoryTextField(textField);
//
Date field validation.
if (dateField != null
&& dateField.length() > 0) {
String pattern = "MM-dd-yyyy";
DateFormat dateFormatter = new SimpleDateFormat(pattern);
try {
Date date = dateFormatter.parse(dateField);
sampleBean.setDateField(date);
} catch
(ParseException parseEx) {
validationEx.addMessage("Wrong date:
" + pattern, "msgDateField");
}
}
//
Floating point numeric field validation
if (currencyField != null
&& currencyField.length() > 0) {
try {
double
value = Double.parseDouble(currencyField);
sampleBean.setCurrencyField(new Double(value));
} catch
(NumberFormatException nfEx) {
validationEx.addMessage("Wrong numeric
format", "msgCurrencyField");
}
}
//
if any submitted data has been rejected, bounce back...
validationEx.raise();
//make
the bean persistent
sampleBean.store();
//
the sampleBean is now persistent, we do not need it in session.
session.removeAttribute("sampleBean");
//
go in the "success" page.
response.sendRedirect(request.getContextPath() + "/Target.jsp");
%>
Validate.jsp
does not contain HTML code because, as already mentioned, it is a controller
and it just contains application logic. It first extracts the data sent by the
user from the request and then it performs the validation checks, field by
field. Inside the constructor of the exception it is indicated the page that
must show the error messages:
Listing 5 – Creation of ValidationException
ValidationException
validationEx =
new ValidationException("Error validating
submission", "/Form.jsp");
Next the
field controls take over, adding messages through the method addMessage:
Listing 6 – Data validation
if (null == textField ||
textField.length() == 0)
validationEx.addMessage("Mandatory
field", "msgMandatoryField");
else
sampleBean.setMandatoryTextField(textField);
In this
case the textField (that is a mandatory field) has not been filled, so the
message “Mandatory field” is added to the exception, and it will be shown at
position indicated by the label “msgMandatoryField” inside Form.jsp.
After all
checks, we can call the method validationEx.raise() that throws the exception
if at least one of the fields is not valid. Otherwise Validate.jsp continue the
execution making the data contained by the object, persistent in the database
and forwarding the program flow to the page Target.jsp, that notifies the user
that everything went according to plan.
By this
time we now see that form.jsp can show the error messages (if present), to
allow the user to reinsert the wrong data. First of all, we must include the
attribute isErrorPage=”true” in the page directive, so the validation exception
can be visible from inside the JSP page. Then we have to check if exceptions
have occurred, if yes we can get the messages to show up in the form near the
fields to which they are related.
Listing 7 – Getting error messages from the
exception
if (null != exception) {
// fill messages
ValidationException
validationEx = (ValidationException) exception;
msgException = validationEx.getMessage();
msgMandatoryField =
validationEx.getMessage("msgMandatoryField");
msgDateField = validationEx.getMessage("msgDateField");
msgCurrencyField = validationEx.getMessage("msgCurrencyField");
}
Consequently,
to show the exception near a field you just need to insert the scriptlet in the
JSP as shown in listing 8.
Listing 8 – Placing error messages in the HTML
<td align="left">
<font
face="Verdana,Arial,Helvetica,sans-serif"
size="2"
color="red">
<strong><%= msgMandatoryField %>
</strong>
</font>
</td>
One of the
advantages of the exposed architecture (possably important) is that lets you to
reach a more reusable and general purpose code: it becomes very easy to develop
utility classes to handle checks on fields. Moreover it happens often that the
checks to apply to different
fields are the same: let’s think about mandatory field, numeric fields, dates
etc…We can write a class with has responsibilities about data validation, and
we can use it inside our web controllers on which we can delegate data
evaluation jobs.
Listing 9 –Validator.java (partial)
public class Validator {
private String errorPage;
private ValidationException
validationEx;
public Validator(String errorPage)
{
this.errorPage
= errorPage;
}
private ValidationException
getValidationException() {
if (null ==
validationEx)
validationEx = new ValidationException(
"Error validating submission",
errorPage);
return
validationEx;
}
//check
for null/empty string
public Object mandatory(String
field, String name) {
if (null == field || field.length() == 0)
getValidationException()
.addMessage("Mandatory field",
name);
return
field;
}
public Date validateDate(String
field, String name) {
if
(field == null
|| field.length() == 0) return null;
String pattern = "MM-dd-yyyy";
SimpleDateFormat dateFormatter =
new
SimpleDateFormat(pattern);
try {
return
dateFormatter.parse(field);
} catch
(ParseException parseEx) {
getValidationException()
.addMessage("Wrong date: " +
pattern, name);
return null;
}
}
public void validate() throws
ValidationException {
if
(validationEx != null)
validationEx.raise();
}
}
In this
case we instantiate the Validator class inside the controller JSP and then pass
the constructor for the Validator class to the error handler page.
Then we
create methods validateXXX(String field, String name) passing as parameter
fields the content of the field as come from the request and as parameter name
the label related to the field, identifying also where the error-message needs
to be shown inside the errorPage. Finally we invoke the method validate(), that
throws the exception in case of one or more checked fields are not conforming,
and activating the redirection mechanism as described before.
Notice that
the return-type of methods validateXXX() change in relation of the type of the
validation, as the conversion job of the string fields come from the request
will be done during the validation.
Listing
10 shows how the
validation code previously listed (in listing 4) could become using the
Validator class.
Listing 10 – Validating code using Validator
class
String textField =
request.getParameter("mandatoryTextField");
String currencyField =
request.getParameter("currencyField");
String dateField =
request.getParameter("dateField");
Validator validator = new
Validator("/Form.jsp");
sampleBean.setMandatoryTextField(
(String)validator.mandatory(textField, "msgMandatoryField"));
sampleBean.setDateField(
validator.validateDate(dateField, "msgDateField"));
sampleBean.setCurrencyField(
validator.validateCurrency(currencyField, "msgCurrencyField"));
validator.validate();
A similar
example can be found in the book “Core J2EE Patterns” (Chapter 3, pag.45
“Validation Based on Abstract Types”, example 3.5)
Someone
could make the argument that is not agreeable to have a “view” (Form.jsp)
declared with the page attribute isErrorPage=”true”, as it ‘s not really an
ErrorPage. But we need to get the
error messages from the exception, and errors are quite inevitable. Also… how can we make this page to become
a servlet, if we cannot declare it with isErrorPage=”true” ? In reality, it is
possible to get a reference to the exception even if we are not inside an
ErrorPage, defining explicitly the exception object choosing between one of
following lines of code (Listing 11).
Listing 11 – Getting the exception explicitly
Throwable
exception = (Throwable) request.getAttribute("javax.servlet.error.exception");
//
...or...
Throwable
exception = (Throwable) request.getAttribute("javax.servlet.jsp.jspException");
Using
request you can obtain the reference to the exception getting the attribute
"javax.servlet.error.exception". But this attribute has been defined since the Servlet
Specification 2.3 (SRV.9.9.1
Request Attributes), this implies
that servlet engines 2.2 compliant, like Tomcat 3.x, not supporting this
functionality will return null. Instead, Tomcat 4, that is the Servlet 2.3
reference implementation will work perfectly (and this is the recommended way).
But if you
use Tomcat 3.x or another servlet engine implementing servlet spec 2.2, you
must use the second line of code shown, but as you can see it’s a JSP-specific
functionality.
Just to be
more precise, if you are using Tomcat, it’s also possible to obtain the
reference to the exception in the same way using
”tomcat.servlet.error.throwable”,
but that is strongly tied to Tomcat, and clearly it isn’t recommendable
at all.
In this
article we discussed a method to manage server-side validation of an HTML form.
This
mechanism can easily be extended and modified to cover specific needs of your
web applications.
Server side
validation can be used as unique solution in an intranet application. For
internet applications, where the response speed is low, it could actually be
better to combine server side validation with some client script to relieve the
network traffic. Anyway it’s always good to address validation logic on the
server.
Source code
presented in the article is available for download. Inside the zip file you can
find project files following the JBuilder 5 format. Just click on “play” to run
the demo.
In the zip
you can find also the war (Web Archive) file that can be used with Tomcat or
any other compatible servlet engine.
Luigi Rocco Viggiano. Sun
Certified Web Component Developer on J2EE platform, provides consulting, training and
distributed software development on Java/J2EE technologies.
e-mail: lviggiano@tiscalinet.it
I want to
thank Cindy Nelson, helping me to translate this document from Italian, Alex
Garbagnati, Nicola Vota, Mauro Antonaci, Francesco Meschia, for their valuable
suggestions, opinions and technical reviewing.
Here you
can find more information about argument treated in this article:
·
The
JavaServer Pages Home Page
http://java.sun.com/products/jsp/
·
The
JavaServer Pages Syntax Reference
A brief summary of basics JSP tags
http://java.sun.com/products/jsp/tags/11/tags11.html
·
Sun's
J2EE Blueprints section about the web tier
http://java.sun.com/j2ee/blueprints/web_tier/qanda/index.html#directive
·
The
Jakarta Struts Framework
for building MVC architectures with
JSPs
http://jakarta.apache.org/struts/index.html
·
Strut
Your Stuff with JSP Tags
by Thor Kristmundsson
http://www.javaworld.com/javaworld/jw-12-2000/jw-1201-struts.html
·
Core
J2EE Patterns: Best Practices and Design Strategies
by Deepak Alur, John Crupi, and Dan
Malks
(Prentice Hall PTR, 2001; ISBN:
0130648841)
http://www.amazon.com/exec/obidos/ASIN/0130648841
·
Understanding
JavaServer Pages Model 2 Architecture
by Govind Seshadri
http://www.javaworld.com/javaworld/jw-12-1999/jw-12-ssj-jspmvc.html
·
The
JavaBean Home Page:
http://java.sun.com/produtcs/javabeans