|
SummaryBy Steven Gould
In this article, Steven Gould reviews the steps involved in developing servlets, then describes how to take the servlet and create a Web application -- in both expanded format and as a WAR. He illustrates how to deploy the Web application in Apache Tomcat -- a widely used, freeware servlet container and the Java Servlet 2.2 Reference Implementation -- and in WebLogic Server 6.0, a full J2EE application server. (3,000 words)
n "Develop N-Tier Applications Using J2EE" (JavaWorld, December 1, 2000), I gave an introduction to Java 2 Enterprise Edition (J2EE) and an overview of the technologies it includes. In this article, we delve into a little more detail with one of the more widely used J2EE technologies: servlets. We begin with a brief recap of servlet development fundamentals, then show how to build a Web application to house them. We discuss the use of Web Application Archives (WARs), then illustrate deployment of the servlet and Web application in Apache Tomcat. In addition, we'll look at deploying the same servlet and Web application in one of the most widely-used servlet containers -- BEA's WebLogic Server.
Develop servlets
Servlets were
designed to allow for extension of a server providing any service. Currently,
however, only HTTP and JSP page servlets are supported. In the future, a
developer may be able to extend an FTP server or an SMTP server using servlets.
Generic servlets
A servlet extends a server's
functionality by offering a specific service within a well-defined framework. It
is a small piece of Java code -- often just a single class -- that provides a
specific service. For example, an HTTP servlet may provide a bank customer with
details of her recent deposits and withdrawals. Another HTTP servlet could allow
a customer to view, and even edit, his mailing address.
To deploy a servlet usually requires configuration of the hosting server application. When the server encounters a particular type of request, it invokes the servlet, passing to it details about the request and a response object for returning the result.
All servlets implement the javax.servlet.Servlet
interface
either directly -- in the case of generic servlets -- or indirectly, in the case
of HTTP or JSP servlets. The javax.servlet.Servlet
interface's
important methods include:
init()
: defines any initialization code that
should be executed when the servlet loads into memory.
service()
: the main method called when the
servlet receives a service request. It defines the bulk of the processing
logic provided by the servlet.
destroy()
: defines any clean-up code
required before removing the servlet from memory. When the servlet container first loads a servlet it invokes the servlet's
init()
method to initialize the servlet. Then, as requests are made
to execute the servlet, the servlet container repeatedly invokes the servlet's
service()
method to provide the required service. Finally, when the
servlet container no longer needs the servlet, it invokes the servlet's
destroy()
method and unloads it from memory. Note that during the
lifetime of a single servlet instance, the init()
and
destroy()
methods will be invoked only once, whereas the
service()
method will be invoked many times -- once each time a
request is made to execute the servlet.
JSP Page servlets are mostly of interest to implementers of JSP containers and are beyond the scope of this article. Rather, we now go on to look specifically at HTTP servlets.
HTTP servlets
HTTP servlets extend the
javax.servlet.http.HttpServlet
class. This class extends the
javax.servlet.GenericServlet
class, which in turn implements
javax.servlet.Servlet
. The HttpServlet
class overrides
the service()
method in such a way as to handle the different types
of HTTP requests: DELETE, GET, OPTIONS, POST, PUT, and TRACE. For each of these
request types the HttpServlet
class provides a corresponding
doXXX() method.
Although you can override the service()
method in your servlet
class, there is rarely any need to do so. More likely you'll want to override
individual doXXX() methods. If you do override the service()
method, be aware that the default doXXX() methods will be called only if you
call super.service
or invoke them directly.
For most applications you will want to override the doPost()
and
doGet()
methods, since one of these usually handles data submitted
by a user from an HTML FORM.
To summarize, when writing your HTTP servlets you should:
javax.servlet.ServletException
javax.servlet.http.HttpServlet
javax.servlet.http.HttpServletRequest
javax.servlet.http.HttpServletResponse
public
HttpServlet
We illustrate these with a simple example below.
A sample servlet: RequestDetails
In the example below we
illustrate a simple HTTP servlet. The first line simply defines what package the
servlet belongs to. The next code block imports the classes used by this
servlet. Then comes the servlet class definition. As you can see, the
RequestDetails
class extends HttpServlet
.
The body of RequestDetails
defines two methods:
doGet()
and doPost()
. The doGet()
method
defines the primary functionality of this servlet. The doPost()
method simply calls doGet()
. The servlet therefore handles both get
and post requests in the same way.
The doGet()
method constructs an HTML page containing details of
the HTTP request sent to the server. Note the method's first two lines. The
first line sets the content type for the response. In general, you will be
constructing an HTML page, in which case the content type should be set to
text/html
. The second line in the doGet()
method
obtains a reference to a PrintWriter
output stream. All output to
be returned to the client is then written to that output stream:
package org.stevengould.javaworld;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import
java.util.Enumeration;
import javax.servlet.ServletException;
import
javax.servlet.http.HttpServlet;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
/**
* This class
provides a simple example of a servlet, and
* illustrates some of the
information available from an
* HTTP request.
*/
public
class RequestDetails extends HttpServlet
{
/**
* Handler for all GET requests. We simply dump out the
* request header information, followed by the body of
* the request.
* @param request the
HTTP request submitted to the
* server for processing. It
is this object that
* contains the details of the
requested URL, and
* it is the details of this object that
we
* output as a response.
* @param
response the response object to be used to
* send a result
back to the client.
* @exception IOException thrown if a
communications
* error occurs.
*
@exception ServletException if the GET request could
*
could not be handled
*/
public void doGet(
HttpServletRequest request,
HttpServletResponse
response )
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Request Details
Example</title>");
out.println("</head>");
out.println("<body>");
out.println("<h3>HTTP Request
Header</h3>");
out.println("<table
border='1'>");
out.println(" <tr
bgcolor=#e0e0e0>");
out.println("
<td><strong>Name</strong></td>");
out.println("
<td><strong>Value</strong></td>");
out.println(" </tr>");
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements())
{
String name = (String)e.nextElement();
String value = request.getHeader(name);
out.println(" <tr>");
out.println(" <td
bgcolor=#e0e0e0>"+name+"</td>");
out.println("
<td>"+value+"</td>");
out.println(" </tr>");
}
out.println("</table>");
out.println("<h3>HTTP Request
Information</h3>");
out.println("<table
border='1'>");
out.println(" <tr
bgcolor=#e0e0e0>");
out.println("
<td><strong>Name</strong></td>");
out.println("
<td><strong>Value</strong></td>");
out.println(" </tr>");
out.println(" <tr>");
out.println(" <td
bgcolor=#e0e0e0>Method:</td>");
out.println("
<td>"+request.getMethod()+"</td>");
out.println(" </tr>");
out.println(" <tr>");
out.println(" <td bgcolor=#e0e0e0>Request
URI:</td>");
out.println("
<td>"+request.getRequestURI()+"</td>");
out.println(" </tr>");
out.println(" <tr>");
out.println(" <td
bgcolor=#e0e0e0>Protocol:</td>");
out.println("
<td>"+request.getProtocol()+"</td>");
out.println(" </tr>");
out.println(" <tr>");
out.println(" <td
bgcolor=#e0e0e0>PathInfo:</td>");
out.println("
<td>"+request.getPathInfo()+"</td>");
out.println(" </tr>");
out.println(" <tr>");
out.println(" <td bgcolor=#e0e0e0>Remote
Address:</td>");
out.println("
<td>"+request.getRemoteAddr()+"</td>");
out.println(" </tr>");
out.println("</table>");
out.println("<hr>");
Date date = new Date();
out.println("<p align=center>Page generated on
"+date);
out.println("</body>");
out.println("</html>");
out.close();
}
/**
* For POST requests, we will
simply perform the same
* operations as for GET requests.
The best way to do this
* is to simply invoke the doGet()
method with the appropriate
* parameters.
* @param request the HTTP request submitted to the server
* for processing. It is this object that contains
* the details of the requested URL, and it is the
* details of this object that we output as a
* response.
* @param response the
response object to be used to send a
* result back to the
client.
*/
public void doPost(
HttpServletRequest request,
HttpServletResponse
response )
throws IOException, ServletException
{
doGet(request,
response);
}
}
Compile the servlet
Since servlets use Java extension
classes (classes that are not part of the core JDK) you must be sure to
correctly set up your CLASSPATH before attempting to compile any servlet. The
Java compiler needs to be able to find the javax.servlet.*
packages
and classes. Other than that, compilation proceeds just as for any other Java
program:
javac RequestDetails.java
Create a Web application
Now that
you have created the servlet, you need to think about deployment. The Java
Servlet 2.2 specification introduced at least two significant new features: a
Web application and a Web application archive (WAR). According to the Servlet
2.2 specifications:
A Web application is a collection of servlets, HTML pages, classes, and other resources that can be bundled and run on multiple containers from multiple vendors.
WARs are simply Java archives of a Web application with a different extension to differentiate them from commonly used JARs.
Before the Servlet 2.2 specifications, servlet deployment differed significantly between servlet containers -- also previously called servlet engines. The 2.2 specifications standardized deployment across containers, thus taking Java code portability one step further. We shall see the power of this later in this article, when we illustrate the creation of a single Web application that is then deployed on both Apache Tomcat and WebLogic Server without any modification or recompilation.
Web application directory structure
The Servlet 2.2
specifications define the directory structure of the files in a Web application.
The top directory -- or root directory -- should be given the name of your Web
application and will define that document root for your Web
application. All files beneath this root can be served to the client except for
files under the special directories META-INF
and
WEB-INF
in the root directory. All private files -- such as servlet
class files -- should be stored under the WEB-INF
directory.
The directory structure of your Web application should look something like that shown in Figure 1.
|
To create a Web application, begin by creating this directory structure. Take
your compiled servlet classes and place them in the WEB-INF/classes
directory. If you have defined your servlet to belong in a package, you must
follow the standard Java rules and create the appropriate subdirectories so the
JVM will be able to find your classes. For example, if your servlet is defined
in a package com.mycompany.myproject
, you should create the
following directory structure:
.../WEB-INF
|--
classes
|-- com
|--
mycompany
|--
myproject
Place your Java classes in the myproject
subdirectory.
A useful alternative to copying the class files into the appropriate directory is to configure your build environment (a Makefile or IDE) to save the compiled class files directly in the required directories. Doing so thereby eliminates that step during development.
Modify the deployment descriptor
You should now have all
of your files in place to create your first Web application. At that point, one
other task needs to be done: update the deployment descriptor to register your
servlets with the servlet container. To easily create a deployment descriptor,
simply edit an existing one. A skeletal web.xml
file is given
below:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
<!-- Your servlet definitions go
here -->
</web-app>
You insert your servlet deployment descriptors between the
<web-app>
and </web-app>
tags in this
file. Servlet deployment descriptors must include the following tags (in this
order):
<servlet>
<servlet-name>name</servlet-name>
<servlet-class>package.name.MyClass</servlet-class>
</servlet>
Various optional tags, which define the servlet's other runtime properties,
are allowed before the closing </servlet>
. Those tags define
properties such as initialization parameters, whether the servlet should be
loaded at startup, security roles, and display properties (including small and
large icons, a display name, and a description). Refer to the specifications for
more details on those.
So far, the deployment descriptors describe the servlet to the servlet
container. Next, we must describe when the servlet container should invoke the
servlet -- referred to as mapping. In other words, we must describe how
to map a URL to a servlet. In the web.xml
file URL, mapping follows
this form:
<servlet-mapping>
<servlet-name>name</servlet-name>
<url-pattern>pattern</url-pattern>
</servlet-mapping>
OK, enough theory. Let's look at an example of a real Web application
deployment descriptor. Below you'll find a minimal web.xml
file
describing our sample RequestDetails
servlet:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
<servlet>
<servlet-name>RequestDetails</servlet-name>
<servlet-class>org.stevengould.javaworld.RequestDetails</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestDetails</servlet-name>
<url-pattern>SampleServlet</url-pattern>
</servlet-mapping>
</web-app>
As you can see from the servlet mapping tags, we have chosen to map our
RequestDetails
servlet to the URL /SampleServlet
.
That's it. We have created our first Web application containing a single servlet. We should now be able to deploy that Web application to any Servlet 2.2-compliant servlet container.
More than likely, that will be how you work with and deploy your Web applications in a development mode. In a production environment, however, keeping related files bundled together is more convenient. In the next section, we look at creating Web application archive files (WARs) that do just that.
Create WARs
As mentioned earlier, a WAR file is simply a
Java archive with the extension changed to reflect its different purpose. We
have already seen the directory structure required by a Web application. To
create a WAR file, we use that same directory structure.
To create a WAR for your Web application, go to the root directory containing your Web application and type the following command:
jar cv0f myWebApp.war .
Note the required period at the end of the above line; it tells the jar program to archive the current directory.
The aforementioned jar command will create a WAR file called
myWebApp.war
. Next, we shall look at how to deploy that WAR file in
both Tomcat 3.2 and WebLogic Server 6.0.
Deploy Web applications in Tomcat 3.2
Tomcat 3.2 serves as the reference implementation for the
Java Servlet 2.2 specifications. For the purpose of this article I assume you
will be using Tomcat 3.2.1 or later. At the time this article was written, 3.2.1
was the latest production release of Tomcat, though version 4.0 was in beta.
To deploy your Web application in Tomcat, copy your root Web application
directory -- the one containing web.xml
and its subdirectories --
into the webapps/ROOT/
subdirectory of your Tomcat installation.
You may want to save a copy of the default Web application before overwriting
it.
Under Unix, for example, if you installed Tomcat into the directory
/opt/jakarta-tomcat-3.2.1
, you would copy your servlet classes into
the directory beneath:
/opt/jakarta-tomcat-3.2.1/webapps/ROOT/
If you run Tomcat under Windows and installed Tomcat into the
C:\Program Files\Jakarta-Tomcat-3.2.1
directory, you would copy
your servlet classes into the directory beneath:
C:\Program Files\Jakarta-Tomcat-3.2.1\webapps\ROOT\
The webapps/ROOT/WEB-INF/classes
subdirectory is the default
directory in which Tomcat will look for your Java classes. If you have defined
your servlet to belong in a package, you must follow the standard Java rules and
create the appropriate subdirectories so that the JVM will be able to find your
classes, as we did before. For example, if you defined your servlet in a package
com.mycompany.myproject
, then you should have the directory
structure shown in Figure 2.
|
Your Java servlet classes will then be in the myproject
subdirectory.
That is all that is involved. There is no further configuration. Keeping with
the RequestDetails
example, try copying the Web application files
into Tomcat's default Web application.
Test the servlet
To test your servlet, start the Tomcat
server, open a Web browser, and open a URL of the following form:
http://{address}:{port}/{servletName}
where:
address
is the name or IP address of the machine running
Tomcat. You can use localhost
if the browser is running on the
same machine as Tomcat.
port
is the port on which Tomcat is listening. By default,
that is port 8080.
servletName
is the name of the servlet you want to invoke.
That should match the value contained in the
<url-pattern></url-pattern>
tags in the
web.xml
deployment descriptor file. For example, if Tomcat is running on the same machine as the browser and
listening on the default port (8080), you can test your
RequestDetails
sample servlet (which is mapped to the URL
SampleServlet
) by opening the following URL:
http://localhost:8080/SampleServlet
Notice how little work was involved deploying the Web application. Copy some files and test. The ease of use is made possible by the Java Servlet 2.2 specifications and the use of the deployment descriptors.
Now that we have seen how to deploy servlets in Tomcat, we shall go on to look at servlet deployment in WebLogic Server.
Deploy Web applications in WebLogic Server 6.0
While WebLogic Server 5.1 was the first release of WebLogic
Server to provide support for the Java Servlet 2.2 specification and Web
applications, WebLogic Server 6.0 has some significant usability improvements
that simplify the deployment of Web applications (in both their expanded form
and when bundled as a WAR file).
Deploy WARs using the Console
With your instance of
WebLogic Server running, start up the WebLogic Server Console. Assuming a
default installation, the Console can be brought up on the
localhost
machine by opening the following URL in a Web browser:
http://localhost:7001/console
You will be prompted for the system user name and password before being allowed into the Console.
To deploy your WAR once you have access to the Console:
That's it. If everything went smoothly, you should see your Web application listed under Web Applications in the left-hand panel of the Console. You may need to refresh the view for that to appear.
As an alternative to using the WebLogic Server Console, it is possible to copy the complete Web application directory structure as we did when deploying with Tomcat.
Deploy Web applications manually
Normally when one
thinks of doing any task manually, or by hand, the automatic reaction is to
expect the task to be a little more involved or cumbersome than the automated
equivalent. In the case of deploying Web applications under WebLogic Server 6.0,
the manual approach is as easy, if not easier, than using the Console.
Simply copy your WAR file or your entire Web application directory structure
into the config/mydomain/applications
subdirectory of your
WebLogic Server distribution (where mydomain
is the name
of your WebLogic Server domain). As soon as the files have been copied, WebLogic
Server deploys the Web application.
Test the servlet
To test your servlet, open up a Web
browser and open a URL of the following form:
http://{address}:{port}/{servletName}
where:
address
is the name or IP address of the machine running
WebLogic Server. You can use localhost
if the browser is running
on the same machine as WebLogic Server.
port
is the port on which WebLogic Server is listening. By
default this is port 7001.
servletName
is the name of the servlet you want to invoke.
This should match the value contained in the
<url-pattern></url-pattern>
tags in the
web.xml
deployment descriptor file. For example, if WebLogic Server is running on localhost
and is
listening on the default port (7001), you can test our
RequestDetails
sample servlet (mapped to the URL
SampleServlet
), by opening the following URL:
http://localhost:7001/SampleServlet
You should see the results of running the servlet displayed in the browser window.
Again, the deployment of your Web application or WAR file required that you simply copy some files and test the servlet -- no configuration necessary.
Reconfigure Web applications
Once you have deployed your
Web applications into WebLogic Server, you can use the Console to configure and
reconfigure the application. As changes are made to any of the settings, the
configuration details will automatically be written to WebLogic Server's
config.xml
file. Next time WebLogic Server restarts, that file will
be used to configure your application.
Conclusion
In this article we
reviewed some of the fundamentals of servlet development. We looked at servlets
in general and HTTP servlets in detail. We then examined how to create a Web
application in which to house the servlets. I also introduced you to Web
Application Archives, or WARs, as a more convenient way to deploy a Web
application or collection of Web components.
We then illustrated deployment of the Web application and of the WAR in two widely used servlet containers: Tomcat 3.2.1 from the Apache organization and WebLogic Server 6.0 from BEA Systems. Anyone who has worked with pre-2.2 versions of the Java servlet specification will agree that the introduction of Web applications significantly improves servlet deployment. Previously, it was necessary to edit configuration files -- with different files and formats for each different servlet engine -- then restart the server. With the introduction of the Java Servlet 2.2 specification, it's simply a matter of copying files and testing.
About
the author Steven Gould is an executive consultant with CGI Information Systems. Based in Dallas, Texas, he develops primarily in Java and C++ under Windows and various Unix platforms. A Sun-certified Java developer, Steven has been using Java since the JDK 1.0 beta release. In addition to his JavaWorld articles, he has had Java-related articles published in Unix Insider and Java Developer's Journal. Thanks to Shari L. Jones for reviewing this article and creating the accompanying graphics. |
|
(c) Copyright 2001 IDG.net, an IDG Communications company