Академический Документы
Профессиональный Документы
Культура Документы
Table of contents
If you're viewing this document online, you can click any of the topics below to link directly to that
section.
The content of this tutorial is geared toward Java programmers who are
unfamiliar, or only vaguely familiar, with servlets. It assumes a general
knowledge of downloading and installing software, and a general knowledge of
the Java language (creating classes, importing classes, etc.), but doesn't
assume knowledge of servlets. The tutorial includes a simple example to
illustrate basic servlet concepts, and a more involved example that illustrates
more sophisticated use of servlets in a small contact management application.
If you've been writing Web apps for a decade, this tutorial is not for you. If you
don't know what servlets are, or are only vaguely familiar with them, keep
reading. There is much more to servlets than what is included in this tutorial, but
this is a good place to start your learning.
All code examples in this tutorial have been tested with J2SE 1.4.2 on the
Windows XP platform, but should work without modification using J2SE 1.4.1,
or even 5.0.
To install Tomcat, go to the Jakarta Web site (see Resources on page 41) and
download the binary distribution of Tomcat 5.0.28 (the most current version that
works with J2SE 1.4.2, as of this writing). The package comes with a Windows
installer that makes installing on that platform a breeze. Follow the instructions
in the readme files, and you'll be set.
To install the Tomcat plugin for Eclipse, go to the Sysdeo Web site (see
Resources on page 41) and download the plugin zip file (tomcatPluginV3.zip, as
of this writing). Then simply extract it to your plugins directory, and follow the
instructions at the bottom of the download page to set up the plugin. To be sure
your plugin is working correctly, work through the very simple HelloWorld servlet
setup "tutorial" that is linked at the bottom of the Sysdeo page (see Resources
on page 41 for a direct link).
Once you have Tomcat and the plugin installed, you're ready to begin this
tutorial.
For technical questions or comments about the content of this tutorial, contact
Roy at roy@roywmiller.com or click Feedback at the top of any panel.
A servlet is the gatekeeper for that process. It lives on the Web server and
handles incoming requests and outgoing responses. It has nothing to do with
presentation, generally speaking, and really shouldn't. You can use a servlet to
write to a stream that adds content to a Web page, but that's usually not a good
idea either, because it tends to encourage mixing presentation with business
logic.
Servlet alternatives
Servlets aren't the only way to serve up Web pages. One of the earliest
technologies for this purpose was the common gateway interface (CGI), but that
forked a separate process for each request, which wasn't very efficient. There
were also proprietary server extensions like the Netscape Server API (NSAPI),
but those were, well, proprietary. And in the Microsoft world, there is the active
server pages (ASP) standard. Servlets provide an alternative to all of these, and
they offer several advantages:
° They give you full access to the entire Java language API, including libraries
for data access (like JDBC)
° They're (in most cases) inherently more efficient than CGI, because servlets
spawn new threads, rather than separate processes, for requests
° There is extensive industry support for servlets, including containers for most
popular Web and application servers
When you create a Java servlet, you typically subclass HttpServlet. This
class has methods that give you access to the request and response wrappers
you can use to handle requests and create responses.
A container, like Tomcat, manages the runtime environment for servlets. You
can configure the container to customize the way in which the J2EE server
functions, and you must configure it to expose your servlets to the world. As
we'll see, through various configuration files in the container, you provide a
bridge from a URL (entered by a user in a browser) to the server-side
components that handle the request that you want the URL to translate into.
When your app runs, the container loads and initializes your servlet(s), and
manages their lifecycle.
When we say that servlets have a lifecycle, we simply mean that things happen
in a predictable way when a servlet is invoked. In other words, certain methods
on any servlet you create will always get called in the same order. Here's a
typical scenario:
° A user enters a URL in his browser. Your Web server configuration file says
that this URL points to a servlet managed by a servlet container running on
your server.
° If an instance of the servlet hasn't been created yet (there's only one
instance of a servlet for an application), the container loads the class and
instantiates it.
° If necessary, when the servlet's useful life is done, the container calls
destroy() on the servlet to finalize it.
Hello, World!
In creating this servlet, we'll be able to confirm that Tomcat functions as it ought
to, and that we can use Eclipse to create a Web project like we should be able
to. We'll also walk through the process of configuring your Web app in the
Tomcat servlet container, which can be a bear if you happen make a slight
mistake in an XML file. Don't worry: In this tutorial, at least, it will all work.
In this first example, we'll write output to the browser directly from our servlet.
This will be the last time in the tutorial that we use that approach.
Setting up Eclipse
There are a few things we need to do to make sure we can create and manage
Tomcat projects in Eclipse.
If you've installed the plugin (by simply extracting the Sysdeo zip file to your
eclipse/plugins directory), you should get some additional menu items and tools
on your toolbar. These are indicated in Figure 1.
The toolbar buttons let you start, stop, and restart Tomcat, which you'll have to
do when you want to run your servlets.
To allow us to create Tomcat projects, which have the correct layout to facilitate
Tomcat deployment, we have to tell Eclipse a few things. If you click
Window>Preferences, you'll see the standard Eclipse preferences dialog, with
a new category called Tomcat at the bottom of the list. Clicking on that will
show you the main Tomcat preferences page (see Figure 2).
Select Version 5.x, and specify the Tomcat home location. (On my system,
this location is C:\Program Files\Apache Software Foundation\Tomcat 5.0, but
yours may vary.) Select Context files as the context declaration mode. Then
click on the JVM Settings preferences subcategory and make sure there's a
valid JRE selected in the drop-down menu at the top of the page. You can use
the default JRE, or you can point to your JDK, which you can tell Eclipse about
in the Java>Installed JREs preferences page.
When you're done, click OK. We're now ready to create a Tomcat project.
The Tomcat plugin makes life much easier for the Web developer using
Tomcat. If you click File>New>Project, and expand the Java wizard category
in the dialog (see Figure 3), you'll see a new kind of a project wizard there: a
Tomcat project.
Click Next, name the project "HelloWorld," then click Finish. If you switch to the
Java perspective in Eclipse, you'll be able to see that new project. It has a
structure that will facilitate deployment to Tomcat (see Figure 4).
Testing Tomcat
Click the Start Tomcat toolbar button. Eclipse should update your console with
informational statements as Tomcat tries to launch. If it launches without
showing any stack traces, you're set. If you see any stack traces in there, life
becomes slightly more difficult. Unfortunately, trial and error (with your good
friend Google) is the only way to track down any errors that occur. The good
news is that starting with a fresh, new project like we did should eliminate the
possibility of any nasty errors.
When Tomcat starts, you won't see anything (except the console content).
You'll have to test it to be sure it's working. If you want a quick indication, try
opening a browser and entering the following URL:
http://localhost:8080/
If all goes well, you should see either a nice Tomcat welcome page, or a
directory listing for the Tomcat "ROOT context." Don't worry about the second
one. We'll prove Tomcat is working when we run our first servlet.
Enter this code, then press Ctrl+Shift+O to organize your import statements.
Eclipse should give you imports for the following classes:
° java.io.IOException
° java.io.PrintWriter
° javax.servlet.ServletException
° javax.servlet.HttpServlet
° javax.servlet.HttpServletRequest
° javax.servlet.HttpServletResponse
Right-click on the HelloWorld project and select Properties. Select the Tomcat
category of properties. You should see a context for the project that looks
something like this:
/HelloWorld
Now go look at the filesystem in your Tomcat home. Drill down to the
conf/Catalina/localhost subdirectory. There, you should see a set of XML files.
Specifically, you should see a HelloWorld.xml file. Open that. This file defines a
Web application context for Tomcat.
When Tomcat launches, it reads these context files to tell the servlet container
where to find your classes (which include your servlets). If you look back at the
INFO statements Tomcat spits out to the console when it's loading, you'll see
information about your Web application context in the list.
The last step for configuring your Web application in Tomcat is to create a
web.xml file, which needs to live in the WEB-INF directory of your project.
(Note: Do not put it in the WEB-INF/src directory -- that's for other things.)
Here's what the file should look like for this simple example:
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
This file defines your Web application to Tomcat. The servlet-name element
names your servlet for use in this file. The servlet-class element maps that
name to a particular class that defines the servlet -- HelloWorldServlet, in
our example. The servlet-mapping element tells Tomcat that URLs of the
form (in this case) /hello map to our servlet, which is defined by the mapped
servlet class.
Once we have this file in place, we can fire up Tomcat and see our servlet load.
http://localhost:8080/HelloWorld/hello
Introduction
In the early days of Web development, many professional programmers had to
figure out how to use servlets well as they went along. One of the most common
results was an explosion of servlets on the server. There was one for each type
of request.
In an action servlet, you don't have conditional logic that directs the servlet's
behavior. Instead, you have actions (programmer-defined classes) that the
servlet delegates to in order to handle different types of requests. Most of the
time, that's a much better object-oriented (OO) approach than having multiple
servlets, or multiple if conditions in a single servlet.
Eclipse also should have created a project that has the correct structure, with
the following important directories:
° WEB-INF
° WEB-INF/src
° work
Note that you can see all of this structure in the Resource perspective in
Eclipse, but you'll see only the WEB-INF/src and work directories in the Java
Browsing perspective.
All of these files are contained in the contacts.jar file included with this tutorial
(see Resources on page 41 for a link). To import them, simply create a new
Tomcat project, then import contacts.jar (use the Import>Zip file option). That
will bring all of the files in at the right locations, except the source code. The
source code will end up in the src directory at the root of the project. Move the
contents of that folder to WEB-INF/src and you should be all set.
Presentation
This is a tutorial about servlets, after all, which have almost nothing to do with
presentation. Still, without seeing some results on the screen somewhere, we'd
really be telling only part of the story. You certainly can write servlets that aren't
involved with presentation at all, but most Web apps present information in a
browser, which means that you have to choose a presentation mechanism to
use. JavaServer Pages technology is one typical alternative and is used widely.
With JSP technology, you can create dynamic Web pages. They support static
HTML (or other markup, such as XML) and dynamic code elements that, as the
name implies, can create content dynamically. Under the covers, JSP pages
are compiled into servlets (that is, into Java code) by a container like Tomcat.
You almost never will have to care about that, however. Just know that the
following flow occurs:
° A user types a URL in a browser that the J2EE servlet container points to a
servlet
° The servlet does its job and puts information in the session, or in a bean,
and forwards to the JSP page
° The JSP code translates information in the bean and/or the session, and
sends the response to the browser
You can create simple JSP pages easily and run them in Tomcat with only
minor configuration changes in our Web app and without downloading
additional code libraries, so we'll use them here (see Resources on page 41 for
Our Contacts application will have one primary JSP page or listing existing
contacts and adding new ones. Later, we'll add pages for login and logout.
In my opinion, JSP technology is frequently the wrong choice, and Velocity (or
some other templating approach) is frequently the right one. But for our simple
example, it will serve the purpose of illustrating the concepts we need to cover.
In such a simple case, mixing a little logic and a little presentation is acceptable.
Professionally, however, it's unwise most of the time, even though many
programmers do it.
<servlet-mapping>
<servlet-name>contacts</servlet-name>
<url-pattern>/index.htm</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>contacts</servlet-name>
<url-pattern>*.perform</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jspAssign</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>logVerbosityLevel</param-name>
<param-value>WARNING</param-value>
</init-param>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jspAssign</servlet-name>
<url-pattern>/*.jsp</url-pattern>
</servlet-mapping>
</web-app>
We created a basic web.xml file for our HelloWorldServlet, but it was the
pretty minimal. As your application becomes more complex, your web.xml file
has to become more savvy. Let's analyze this file quickly.
The <servlet> tag specifies a name alias for our servlet that we'll use
elsewhere in the file. It also tells Tomcat which class to instantiate so as to
create the servlet in memory. In my Eclipse workspace, I created a
com.roywmiller.contacts.model2 package to hold the servlet class. You
can call your package whatever you want, but the path to your servlet has to
match what's in your <servlet-class> element here. The second servlet we
define is one that comes with Tomcat when you download it, and you don't
need to change it. It's simply the JSP-handling servlet.
Our initial page will present the list of contacts, which will come from an object
that contains the list. It will also contain a form for adding a new contact. The
page will look like Figure 5.
While not a work of art, the page displays all of our contacts in nicely formatted
rows at the top. Each one has a Delete link that the user can click on to delete
that particular contact. The form contains fields for name and address values,
and radio buttons for the type of contact (family or acquaintance, in our simple
example). This simple page will allow us to explore how to use a simple action
framework in our servlet application. It also will let us explore how to use the
request and response our servlet receives from the browser during a user
session.
<style type="text/css">
body, table, hr {
color: black;
background: silver;
font-family: Verdana, sans-serif;
font-size: x-small;
}
</style>
</head>
<body>
<jsp:useBean id="contacts" scope="session"
class="com.roywmiller.contacts.model.ContactList"/>
<td>Street:<td>
<td><input type="text" size="30" name="street"></td>
</tr>
<tr>
<td>City:<td>
<td><input type="text" size="30" name="city"></td>
</tr>
<tr>
<td>State:<td>
<td><input type="text" size="30" name="state"></td>
</tr>
<tr>
<td>Zip:<td>
<td><input type="text" size="30" name="zip"></td>
</tr>
<tr>
<td>Type:<td>
<td><input type="radio" size="30" name="type" value="family">
Family <input type="radio" size="30" name="type" value="acquai
checked> Acquaintance</td>
</tr>
</table>
<br/>
<input type="submit" name="addContact" value=" Add ">
</form>
</fieldset>
</body>
</html>
At this point, most of what you see there is probably Greek. We won't dissect all
of it, but over the next few panels we'll hit the high points for understanding how
our servlet will interact with this page.
In order to embed Java code in the page, you have to tell the JSP page where
the classes are, just like you do in a Java class. You do that with statements like
this:
Our page displays a list of contacts, which come from a ContactList instance
that the JSP page knows about because of this line:
This line tells the JSP page to use a bean, called contacts elsewhere in the
page. It's an instance of
com.roywmiller.contacts.model.ContactList, and it has session
scope.
Notice that we have a Java for loop in the body of the page:
This illustrates how JSP technology lets you mix HTML and Java statements.
Here we loop through the contact list of our contact object. Each time through
the loop, we add a <tr> element to our HTML table. Within each row of the
table, one per contact, we call getters on the Contact instance to populate our
table cells. For the first cell, we need to create a Delete link for each row. We
set the href attribute to the following string:
removeContactAction.perform?id=<%= contact.getId()%>
When a user clicks that link, that string will be appended to the end of the URL
that gets sent to the server, after an initial slash (/) is added. The question mark
is a delimiter for request parameters, which follow in name=value pairs. In this
case, we send along the ID of each contact.
This same kind of thing happens elsewhere in the page, such as in the form to
add a new contact. Notice the <form> tag:
When a user clicks the Add button (the submit button at the bottom of the form),
addContactAction.perform gets appended to the URL.
That's all there is to it! Some of this syntactical magic is part of the reason many
professional programmers either begrudgingly use JSP technology, or create
various helper classes (such as custom JSP tags) that help make pages easier
to create, read, and maintain. But now that we have the page, we can get to
writing some code.
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.roywmiller.contacts.actions.Action;
public ContactsServlet() {
super();
}
° Derive the action name from the URL that caused the servlet to be invoked
° Instantiate the correct action based on the name
° Tell the action to perform itself
° Forward the response to the URL to which our action points us
We derive the action name from the URL that caused the servlet to be invoked,
which we get from request.servletPath(). Remember that all of the URLs
that cause us to invoke an action will have the form *.perform. We parse that
to get the string to the left of the dot, which is the action name, then pass that
action name to our ActionFactory to instantiate the right action. Now you
see why we told our Web app how to handle URLs of that form, and why we
used those "magic" strings in our JSP page. It was so we could decode them
here and use actions to our advantage. What's the alternative? Lots and lots of
if statements, and lots of extra code. With actions, as we'll see, each action we
need to perform is neatly encapsulated.
This is fine, but we need some additional classes to get the job done. That's
where our action framework comes in.
° An Action interface. This interface defines the very simple public interface
of all actions.
The ActionFactory
Here's our ActionFactory:
import java.util.HashMap;
import java.util.Map;
import com.roywmiller.contacts.actions.Action;
import com.roywmiller.contacts.actions.AddContactAction;
import com.roywmiller.contacts.actions.BootstrapAction;
import com.roywmiller.contacts.actions.RemoveContactAction;
public ActionFactory() {
super();
}
public Action create(String actionName) {
Class klass = (Class) map.get(actionName);
if (klass == null)
return actionInstance;
}
protected Map defaultMap() {
Map map = new HashMap();
map.put("index", BootstrapAction.class);
map.put("addContactAction", AddContactAction.class);
map.put("removeContactAction", RemoveContactAction.class);
return map;
}
}
° BootstrapAction
° AddContactAction
° RemoveContactAction
Recall that the actions to add and remove contacts were sent as URLs to our
servlet by the Add form and the Delete link, respectively. The
BootstrapAction simply fits the calling of /index.htm into our action
framework.
When we tell the factory to create an Action, it instantiates the class and gives
us back the instance. Adding a new action to the factory is as simple as creating
the class for the action, then adding a new entry in the factory's action Map.
Actions
The Action interface looks like this:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
The method we'll be using extensively now is perform(). The other method,
writeToReponseStream(), allows an action to write directly to the output
stream of the response, for passthrough to the JSP page. Whatever gets written
(text, HTML, etc.) shows up on the page. We won't be using that method for the
moment, but it's available on ContactsAction for you to see how it's done.
Remember that we used the code in the body of this method in our
HelloWorldServlet, so it shouldn't be unfamiliar to you.
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
Our action either returns a URL string or writes to the output stream for printing
on the JSP page. If our action returns a URL string, which our
BootstrapAction does, we get the ServletContext, ask it for a
RequestDispatcher on our URL, and finally forward the request and
response to the JSP servlet so it can construct the page. Our action servlet
regains control after that, and can do any remaining work, as long as it doesn't
write to the JSP page's PrintStream, which is now closed.
response.sendRedirect("http://...");
But there's a price to pay for doing that. When we use a dispatcher, we're
delegating the request and response to the JSP servlet, which also forwards
To tell our action framework about this new available action, we added this line
to the factory's action Map:
map.put("index", BootstrapAction.class);
Adding contacts
An app that shows a page that doesn't let you do anything isn't very useful. We
need to be able to add contacts.
To do that, we must:
Telling the factory about the action is as simple as adding another entry in the
factory's map, as we did with BootstrapAction.
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.roywmiller.contacts.model.Contact;
import com.roywmiller.contacts.model.ContactList;
return "/contactList.jsp";
}
return contact;
}
}
All we do here is call createContact() to create a new Contact and set its
instance variables to the corresponding values containing the request
parameters. Then we add the new Contact to the ContactList in the
HttpSession. Finally, we tell our servlet to return to /contactList.jsp.
Recall that every time we create a Contact, the constructor assigns it a unique
ID. Look back at the JSP code for a moment. You'll see two important things in
it with regard to what we're doing in this action. First, notice that we guaranteed
we'd always have a ContactList instance in our session by adding this line:
When the JSP page is first compiled and displayed (as a result of forwarding to
the JSP page after BootstrapAction performs), it will instantiate a
ContactList. This object has nothing in it, which is why our list of contacts
shows up empty when we start the app. In AddContactAction, we're
modifying that object to add new contact information, then reinserting it in the
session. When the page displays after that, it will read the ContactList's list
of Contact instances and display them.
Second, notice that our form to add contacts looks like this:
Removing contacts
Adding contacts is nice, but being able to remove them is just as important.
Being able to edit them would be nice, but this tutorial is only so long. Besides,
adding edit capability would be as simple as adding another action. So for now,
we'll just add the ability to remove contacts and then move on to more
interesting things.
Look at the Delete link for each row of the contacts table in our JSP page:
That link tells our servlet to ask the factory for the right action class for the name
removeContactAction. It also passes a parameter called id in the request,
with a value set to the current contact's ID.
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.roywmiller.contacts.model.ContactList;
return "/contactList.jsp";
}
}
All we do here is grab the id parameter and the ContactList from the
session, tell the list to remove the Contact with that id, then replace the list on
the session. Last, but not least, we tell the servlet to forward back to
contactList.jsp.
http://localhost:8080/contacts/
That's it -- we have a working Web application! But we can't save our list of
contacts, so we have to reenter them every time we launch the app. Worse,
every user of our app has the same list of contacts. We can fix that by adding
support for unique users, and by storing data in a file (the simplest database
that could possibly work). We'll do both in the next section.
Not so bad, really. The only real new concepts are using files (just more
standard Java language work) and directing to new pages. All of the action
handling mechanisms are the same. This will illustrate the power of our action
framework, which took precious little time to create. And it isn't nearly as
complicated as something like Jakarta's Struts framework (see Resources on
page 41), which just might be overkill for what you're doing in your application.
The ContactsUser
Our ContactsUser object looks like this, minus the import statements and
accessors (you can find the complete source code in contacts.jar):
public ContactsUser() {
}
accessors...
}
This class holds information about users of the application. For the most part,
that's all it does. It holds the user's username and password, and maintains a
list of contacts for the user. It allows various actions in our action framework to
add Contacts to the user, and remove them. The no-argument constructor is
there for unit testing purposes. The other constructor, the one that takes three
arguments, is the one the app uses.
You might be asking yourself, "Why doesn't this class have a ContactList
instance variable?" After all, we went to the trouble of creating it in the first
place. Why don't we use it? The simple answer is that we really don't need that
class anymore. It wrapped an ArrayList and gave us some helper methods.
Those helper methods really make more sense on ContactUser. If we used a
ContactList, we'd be calling methods on it from ContactUser that have the
same names, and that we want to accomplish the same thing. For example, if
ContactUser had a ContactList, and we named that instance variable
contactList, here's what addContact() would look like:
Changing contactList.jsp
Changing the JSP page to use our new ContactUser is straightforward. There
are three changes we need to make.
The second change is to update the table row-building logic in the page to use
our new user variable:
<%
List list = user.getContacts();
for (Iterator i = list.iterator(); i.hasNext();) {
Contact contact = (Contact)i.next();
%>
<a href="logoutAction.perform">Logout</a>
We'll put this link next to the "Contacts 1.0" header. When a user clicks the link,
our servlet will perform the LogoutAction.
<body>
<h2>Contact List 1.0</h2>
<hr size="2"/>
<fieldset>
<legend><b>Please Login</b></legend>
<form method="post" action="loginAction.perform">
<table>
<tr>
<td>Username:<td>
<td><input type="text" size="30" name="username"></td>
</tr>
<tr>
<td>Password:<td>
<td><input type="text" size="30" name="password"></td>
</tr>
</table>
<br/>
<input type="submit" name="login" value=" Login ">
</form>
</fieldset>
</body>
This page has a form with two text fields and a submit button. When a user
clicks Login, our servlet will perform the LoginAction.
Here's goodbye.jsp:
<body>
<jsp:useBean id="user" scope="session" class="com.roywmiller.contacts.model.ContactsUser"/>
When a user tries to log in with a username and password that aren't in our
database, our application gives up and routes the user to an error page, which
looks like this:
<body>
<h2>Contact List 1.0</h2>
<hr size="2"/>
<fieldset>
<legend><b>Error</b></legend>
There was an error: <%= session.getAttribute("errorMessage") %>
</fieldset>
</body>
This is the simplest page we have. It uses the default session variable
available in all JSP pages to display an error message.
Adding LoginAction
The LoginAction class looks like this:
This action grabs the username and password parameters from the request,
then checks to see if our database contains a user with that
username/password combination. If it does, we put the user in the session and
go directly to contactList.jsp. If that user doesn't exist in our database, we set an
error message on the session and go to error.jsp.
Adding actions should be easy for us now. We add an entry to our action factory
that looks like this:
map.put("loginAction", LoginAction.class);
With the pages in place, and our factory aware of our new action, we're done.
You should be able to run the app and see the login page. When you enter a
username and password, regardless of what they are, you should see the error
page. In just a little while, you'll be able to log in with a valid username and
password, and see contactList.jsp with an empty contact list.
Adding LogoutAction
Introduction to Java Servlet technology Page 33 of 42
ibm.com/developerWorks Presented by developerWorks, your source for great tutorials
try {
writer = new FileWriter(usersFile.getAbsolutePath());
writer.write(text);
} catch (Exception e) {
throw new RuntimeException("Unable to append to file.", e);
} finally {
closeWriter(writer);
}
}
shutDown() calls writeUsers(), which iterates over all the users we have in
memory (from when we read in the file when the servlet initialized itself),
creates a UserRecord for each, then passes the complete string to
writeText(). That method dumps the string to the file, overwriting the
existing contents. The UserRecord class is a nice helper that encapsulates all
of the somewhat messy tokenizing work for each user record in the file. You can
examine the code for yourself (see contacts.jar for the complete source code
listing).
Once the database is shut down, we tell the servlet to forward to goodbye.jsp to
display our personalized goodbye.
In our application, we'll use a text file. That file will contain a single line per user,
in the following form:
The usernames in our file will be plain text, but the passwords will be Base64
encoded for (arguably too-simple) security. The entries for contacts will be
comma-delimited within each. The contacts themselves will be delimited by pipe
characters (|). There's no magic to this formatting. It just does what we need it
to do, which is to allow us to parse the file easily.
For convenience, we'll put this file at the root of our project so that the path to
the file is straightforward.
testuser cGFzc3dvcmQ=
The password in each entry is encoded with Base64 encoding. You can use the
EncoderDecoder class in contacts.jar to figure out the encoded version of
your password. Its main() method lets you enter a plaintext string, then run the
class to print the encoded password to the console.
The UserDatabase
Our UserDatabase wraps interaction with our text file. The class listing looks
large, but it's not complicated (much of the perceived complexity is the extra
Java coding stuff you need to deal with reading and writing files). We'll discuss
a few high points in this panel (see contacts.jar for the complete code listing).
The class implements the Singleton pattern. The class maintains a single
instance that all users share by calling getSingleton().
In our servlet init() method, we'll tell the UserDatabase where the
database file is located (based on the ServletContext), then we'll tell it to
initialize itself by calling initialize(). That method looks like this:
This method reads in the complete file by calling retrieveText, tokenizes the
big string, creates a UserRecord for each user, then calls put() to place the
new ContactsUser in the map. The real work of this method goes on in the
calls to retrieveText() and put():
try {
bufferedReader = new BufferedReader(new FileReader(usersFile.getAbsolutePath()));
char charBuff[] = new char[(int) new File(usersFile.getAbsolutePath()).length()];
bufferedReader.read(charBuff);
return new String(charBuff);
} catch (Exception e) {
throw new RuntimeException("Unable to read in the file.", e);
} finally {
closeReader(bufferedReader);
}
}
its existing contents. The method hides the same kind of file interaction details.
The put() method creates the key for the user (the username plus the
password) and inserts it in the map of users.
Introduction
In this tutorial, we've only scratched the surface of what you can do with
servlets. Web apps can be as sophisticated as you can imagine. The underlying
mechanisms, though, are essentially the same for all of them, and if you're
writing code in the Java language, servlets are at the core. Creating more
sophisticated apps is a matter of using more sophisticated tools and libraries.
However, that's where many programmers make mistakes that lead them to
create terrible Web applications. This section contains some suggestions on
how to avoid that. Most Java programmers with Web development experience
would agree with some of these. Some are more controversial. In any case,
they should help get you started with servlets well.
Servlets aren't a place for business logic. That's bad OOP design. Think of a
servlet as one of two things:
° An extra layer behind your UI that helps get "events" to the server
° An extra layer in front of the server that allows you to use a browser as your
UI
Either way, it's a place where you quickly dispatch matters to other pieces of
your application, then get out.
Use actions
An action framework, even a simple one like the one we used in this tutorial, is
Some people think this fragments the code, making it more difficult to
understand. I think that this objection stems from two things:
Think about the alternative. Without actions (assuming you don't simply
delegate to other objects that you don't call "actions"), you'll have to have lots of
if statements. If you have two or three, your code will be reasonably easy to
read. If you have ten, that's silly. A method that requires you to scroll through
several screens is bad news. It usually means that the method does too much.
At minimum, you should extract the action-like things you do in service() (or
doGet(), etc.) into other methods that are named well so you know what they
do.
I don't know where these ideas come from. Bruce Eckle suggests that it's a
holdover from the CGI days, when folks grew used to paying attention to
whether a GET or POST was coming in. (See Resources on page 41 for a link to
a more detailed version of his theory.) I've never heard a good reason not to
use service(). In any case, if you use it, and then determine later that it
would be better to use one of the doX() flavors, refactor the code! Until then,
use service(), because it's easier.
It's wiser to keep presentation in the place where it belongs: in the page. JSP
technology allows you to do that, but as I said earlier, it requires a lot more work
to keep business logic and presentation separate. Templating engines like
Velocity are often a better choice. Whatever approach you choose, minimize the
mixing of business logic and presentation.
Many of the packaged Web app development libraries, like Struts (see
Resources on page 41), come with built-in frameworks for handling dynamic
display of messages, including errors. Use those features.
Use a framework when it's wise to use one. Don't assume that you need it. Wait
for your system to tell you that you do. Some programmers consider that "bad
design." Not so. Assuming you'll need a particular framework, or even a
particular feature of that framework, can be overdesign. You should design for
what you need; most often, that changes as the system grows. It's frustrating to
pick a framework before development begins, then hit a brick wall because that
framework doesn't support or allow something you want to do.
Summary
In this tutorial, you've learned about Java servlets, and how to use them
professionally. Certainly the examples in this tutorial are simple, but they
illustrate most of the servlet concepts you would use to create a Web
application. There are more features (configuration and otherwise) available to
you, but the core of almost every Web app written with the Java language is
one or more servlets acting as gatekeepers for business logic on one or more
servers behind the scenes.
More importantly, you've learned some techniques for using servlets well. Web
applications often grow into messy piles of code. By using some simple
techniques driven by fundamental OOP principles, you can avoid the mess, and
create apps that are a joy to enhance and maintain.
Resources
° Download the contacts.jar that accompanies this tutorial.
° If your Web application is too complex for simple JSP components, you may
wish to investigate the Velocity Template Engine
(http://jakarta.apache.org/velocity/) or the Struts Web Application Framework
(http://struts.apache.org/) . Both projects are hosted by the Apache
Foundation.
° Also see the Java technology zone tutorials page for a complete listing of
free Java-focused tutorials from developerWorks.
Your feedback
Colophon
This tutorial was written entirely in XML, using the developerWorks Toot-O-Matic tutorial
generator. The open source Toot-O-Matic tool is an XSLT stylesheet and several XSLT
extension functions that convert an XML file into a number of HTML pages, a zip file, JPEG
heading graphics, and two PDF files. Our ability to generate multiple text and binary formats
from a single source file illustrates the power and flexibility of XML. (It also saves our
production team a great deal of time and effort.)