Sie sind auf Seite 1von 101

Configuring Tomcat with IIS Web Server

by James Goodwill
12/18/2002

In this article, we are going to continue our Tomcat Connector discussions with a look at
how to configure Tomcat and Microsoft's Internet Information Server (IIS) using the JK
v1.2 Connectors.

Note: This article assumes that you have completed the steps from the previous article,
"Configuring Tomcat and Apache With JK 1.2." If you have not already completed these
steps, please go back to the article and complete steps 3-7.

Preparing Your Environment

To connect IIS to the Tomcat server, we are going to leverage a Microsoft technology
called ISAPI. We don't need to know much about this technology; we simply need to
make use of an existing Dynamic Link Library (DLL) that has already been built and is
available here.

The file you are looking for is isapi_redirector.dll, which exists in the win32/
subdirectory.

Once you have this file, you then need to complete the following steps:

1. Install IIS as described in its packaged documentation. This article assumes that
you will be running IIS 5.x.
2. Test the IIS installation, by starting IIS and opening your browser to
http://localhost.

If everything went according to plan, you should now see images similar to the following.

1
2
Configuring Tomcat and IIS with the JK 1.2 Connector

Now that IIS is installed, it is time to begin the actual integration between IIS and
Tomcat. This process can be broken down into two sections: configuring Tomcat, and
configuring IIS. The result of our integration will make the Tomcat examples Web
application available through IIS.

Configuring Tomcat

The complete Tomcat configuration was described a previous article, "Configuring


Tomcat and Apache with JK 1.2."

3
Configuring IIS

Once Tomcat is configured, it is time to configure IIS. We do this by telling IIS that it
should pass all requests to the examples context to Tomcat for processing. This is a
relatively simple process and is broken down into the following four steps:

1. Creating a Tomcat Worker File

We begin the IIS configuration, just as we did with Apache, by creating a Tomcat worker
definition. This worker definition is used to tell IIS how it is to talk to Tomcat. This is
done by creating a Tomcat worker file that contains the definition for at least one Tomcat
worker.

Note: We discussed the worker.properties file in the previous article, so I will only
provide a source listing. If you did not read the previous article, this would be a good
time to check it out.

worker.list=testWorker
worker.testWorker.port=8009
worker.testWorker.host=localhost
worker.testWorker.type=ajp13

If this file does not exist, create it, add the previous lines, and copy it to the
<CATALINA_HOME>/conf directory of the Tomcat instance that you will be integrating
with IIS.

Note: <CATALINA_HOME> represents the base directory of your Tomcat installation.

2. Creating a URI Worker Mapping File

After you have created the worker file, you need to tell IIS which requests will be
serviced by the described worker. This is done by creating a file named
uriworkermap.properties that contains a list of URI patterns mapped to Tomcat workers.
The following listing contains the map for the examples context.

/examples/*=testWorker
/examples/*.jsp=testWorker
/examples/servlet/*=testWorker

Each of the URI mappings begins with the URI pattern of the request and the worker with
which this pattern should be associated. Our mappings tell IIS that all static files, JSPs,
and servlets in the examples context should be serviced by the testWorker. Once you
have this file created, copy it to Tomcat's <CATALINA_HOME>/conf directory.

3. Modifying the Windows Registry

4
In this step, we are going to create the Windows Registry settings that will be used to
point the ISAPI Redirector to our newly-created files.

Note: To add these new registry settings, you will need to use the Windows
REGEDT32.EXE application, which is most likely in your C:\WINDOWS\SYSTEM32
directory.

Before you can add any of these settings, you must add the following registry key:

HKEY_LOCAL_MACHINE
SOFTWARE
Apache Software Foundation
Jakarta Isapi Redirector
1.0

Note: These keys must be entered exactly as they are listed above.

After you have the above keys, select the 1.0 key and add the following name/values
pairs -- all of which are string values.

Name Data
extension_uri /tomcat/isapi_redirector.dll
worker_file <CATALINA_HOME>/conf/workers.properties
worker_mount_file <CATALINA_HOME>/conf/uriworkermap.properties
log_file <CATALINA_HOME>/logs/jk_iis.log
log_level debug

These string values are read by the ISAPI Redirector at startup, and are used to initialize
IIS. The following table provides a description of each of these variables.

Name Data
The extension_uri variable represents the IIS virtual director,
extension_uri which will be created in the next step, plus the name of the ISAPI
redirector file.

worker_file
The worker_file variable tells the ISAPI Redirector the location
of the workers.properties file.

worker_mount_file
The worker_mount_file variable tells the ISAPI Redirector the
location of the uriworkermap.properties file.

log_file
The log_file variable defines the name and location of the ISAPI
Redirector's log file.
log_level The log_level variable defines the debug level used when writing

5
to the log file. The possible values are debug, info, error, and
emerg.

Once you have added the appropriate keys and variables, your registry should resemble
the following figure.

4. Configuring the ISAPI Redirector

We are now at our final step. In this step, we are going to actually tell IIS when to use the
ISAPI Redirector. To do this, we need to complete all of the following steps:

1. Copy the isapi_redirector.dll file to your <CATALINA_HOME>/conf/ directory.


2. Open up your Windows Control Panel.
3. Open the Administrative Tools Application.
4. Open the Internet Information Services Application. You should see an image
similar to the following:

5. Expand your local computer entry, until you see the default Web site

6
6. Right-click on the default Web site and select Virtual Directory from the New
menu.
7. Select the Next button and enter the value tomcat in the Alias text box.
8. Browse to the directory containing your isapi_redirector.dll file, which in our case
should be <CATALINA_HOME>/conf/ and select the Next button.
9. Now make sure that you have the permissions set to read, run, and execute.
10. Now continue through the Virtual Directory Wizard, until you reach the end and
then select the Finish button.
11. We now need to add the ISAPI Redirector to the default Web site, so right-click
on the default Web site and select the menu item Properties.
12. Select the ISAPI Filters tab and press the Add button.
13. Enter a filter name and browse to the location of the isapi_redirector.dll file. The
following image shows the values that I have used in my instance.

14. Now press the OK button until you are back at the Internet Information Services
dialog.

Testing Your New Configuration

At this point, everything should be properly configured and you can now test your
changes. To do this, you must first start Tomcat and then restart the IIS server. When both
servers are up and running, open your browser to one of the following two URLs and
browse around testing your new integration:

• http://localhost/examples/servlets/index.html
• http://localhost/examples/jsp/index.html

Troubleshooting

If you have trouble, check the following items:

1. Double-check the settings in the registry file; they must be entered exactly as they are
listed.

2. Make sure the tomcat virtual directory points to the location of the isapi_redirector.dll
and the permissions are set to read, run, and execute.

7
3. Open the ISAPI filters dialog and make sure that the tomcat filter has a green arrow
next to it. If it does not, then check the Executable entry on the Filter Properties dialog.

4. Make sure the values of your workers.properties and uriworkermap.properties files


contain the previously listed values.

5. If you still cannot find the problem, then open the log file
<CATALINA_HOME>/logs/jk_iis.log and see if you can diagnose the problem from the
ISAPI Redirector's output.

Up Next

That about does it for the basic IIS/Tomcat configuration -- in the next Tomcat article, I
will take another look at server.xml while we configure Tomcat to receive requests from
Apache and the JK2 Connectors.

James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software


consultancy.

Configuring Tomcat and Apache With JK 1.2


by James Goodwill
11/20/2002

In an earlier article, I promised to demystify Tomcat's server.xml file. Now, I'd like to
advance the discussion with an in-depth look at the JK modules. This is the first of five
articles addressing JK. Here's a preview of the whole series:

1. Configuring Tomcat and Apache using JK v1.2


2. Configuring Tomcat and IIS using JK v1.2
3. Configuring Tomcat and Apache using JK v2
4. Configuring Tomcat and IIS using JK v2
5. Advanced Connector Configurations

What is JK v1.2?

In the simplest terms, the JK modules, or mod_jk, are conduits between a Web server and
the Tomcat JSP/servlet container. They replace the previous Web server module,
mod_jserv, which had many shortcomings. The new JK modules include support for a
wider variety of Web servers, better SSL support, support of the AJP13 protocol, and
support for the entire Tomcat series from 3.2.x to 5.x.

8
Preparing Your Environment AJP 13

Before we can continue with our The AJP 13 protocol is a packet-based


discussions, you must get all of the protocol that allows a Web server to
components required to configure Tomcat communicate with the Tomcat JSP/servlet
and Apache. The following lists these container over a TCP connection. For our
components and their current locations. purposes, all that we need to know is that
AJP13 is a more efficient protocol and
• Apache 1.3.27 includes better support for SSL than its
• Jakarta-Tomcat 4.1.12 predecessors. Further information on
• mod_jk v1.2 AJP13 is available at
http://jakarta.apache.org/tomcat/tomcat-
Make sure that you download the 4.1-doc/jk2/common/AJPv13.html.
appropriate binary for your operating
system. You can download the source for each of these components, but we will not be
covering the building of any of them. Once you have downloaded all of the components
listed above, complete the following steps:

1. Install Apache as described in its packaged documentation.


2. Test the Apache installation, by starting Apache and opening your browser to
http://localhost. You should now see an image similar to Figure 1.

Figure 1. The Apache Installation Test Page

9
3. Install Tomcat as described by its documentation.
4. Set the environment variable JAVA_HOME equal to the root directory of your JDK
installation.
5. Set the environment variable CATALINA_HOME equal to the root directory of you
Tomcat installation.
6. Test the Tomcat installation by starting Tomcat and opening your browser to
http://localhost:8080. You should now see an image similar to Figure 2.

Figure 2. The Tomcat Default Homepage

7. Now shut down both Apache and Tomcat, before moving on to the next sections.

Configuring Tomcat and Apache With the JK 1.2 Connector

It is now time to begin the actual integration between Apache and Tomcat. This process
can be broken down into two sections: configuring Tomcat and configuring Apache.

Configuring Tomcat

To begin our Tomcat and Apache integration, we need to first tell Tomcat that it should
start listening for AJP13 requests; both JK and JK2 use AJP13 to communicate with
Tomcat. To do this, we need to add an additional <Connector> element to Tomcat's
server.xml file. Add the following entry to server.xml, making sure that it is inside of the

10
<Service> element and immediately follows any previously-defined <Connector>
elements.

<Connector className="org.apache.ajp.tomcat4.Ajp13Connector"
port="8009" minProcessors="5" maxProcessors="75"
acceptCount="10" debug="0"/>

The only two attributes in this <Connector> worth noting are the port and className
attributes. The port attribute tells Tomcat that it needs to open a new Connector that
listens to port 8009 for incoming requests. The className attribute tells Tomcat that all
of the requests coming in on this port should be serviced by the Tomcat Connector class
org.apache.ajp.tomcat4.Ajp13Connector, which also uses the AJP 1.3 protocol. (If
you have further questions about Tomcat Connectors, you can refer to "Demystifying
Tomcat's server.xml File.") This completes the Tomcat configuration.

Configuring Apache

Now that Tomcat is configured to listen to port 8009 for incoming AJP13 request, let's
tell Apache to actually talk to Tomcat using that port and protocol. This process, while
not terribly complicated, is somewhat more complicated than Tomcat's equivalent
configuration, so I have broken it down into several sections.

Create a Tomcat Worker

We begin the Apache configuration by creating a Tomcat worker definition that will tell
Apache how and when to talk to Tomcat. This is done by creating a Tomcat worker file,
containing the definition for at least one Tomcat worker. A Tomcat worker is a process
that defines a communications link between Apache and a Tomcat container. (If you have
any questions about Tomcat Workers, you can refer to the actual Jakarta documentation.)
We will cover Tomcat workers in much more detail when we get to Part 5 of this series,
"Advanced Connector Configurations."

The Tomcat worker file, in this example, should be named workers.properties and
should be copied into the <CATALINA_HOME>/conf directory of the Tomcat instance
that you will be integrating with Apache. (<CATALINA_HOME> represents the base
directory of your Tomcat installation.)

Now add the following properties to this newly-created file and save your changes.

worker.list=testWorker
worker.testWorker.port=8009
worker.testWorker.host=localhost
worker.testWorker.type=ajp13

11
These entries define a Tomcat worker named testWorker that resides on the same host
as the Apache server, localhost, and listens to port 8009 for a client using the AJP13
protocol. This is accomplished using a series of worker properties.

The first of these properties is the worker.list property. This property defines a list of
Tomcat workers to which our instance of Apache will talk. This list can define any
number of Tomcat workers as long as each name is separated with a comma.

(Note that all of the worker properties are prepended by the string worker. This string
acts the top-level identifier of all worker properties.)

In our example we are defining a single worker named testWorker. Once we have a
worker named, we can then modify the attributes of that worker explicitly using the
following syntax:

worker.testWorker + name of property being modified

Because our current example is so simple, we are only going to modify three of the new
workers properties: port, host, and type. All of these are easy enough to decipher, but
for clarity's sake, they are still described in the following table.

Property Use
The port property defines the port number of this Tomcat worker.
<workername>.port This value must match the port attribute of the previously defined
Tomcat <Connector> element.
The host property defines the hostname of this Tomcat worker.
<workername>.host Because we are configuring both Apache and Tomcat on the same
host, this value is currently set to localhost.

<workername>.type
The type property defines the protocol of this Tomcat worker, which
in our case is ajp13.

Modify Apache's httpd.conf Configuration File

Now that we have defined a Tomcat worker, we need to tell Apache to talk to that worker.
We do this by making several modifications to Apache's
<APACHE_HOME>/conf/httpd.conf file. This process is broken down into several steps:

1. Copy the previously downloaded mod_jk module to the


<APACHE_HOME>/libexec directory.
2. Tell Apache to load the mod_jk module. We do this by adding the LoadModule
and AddModule directives to the bottom of the httpd.conf file, as follows:
3. LoadModule jk_module libexec/mod_jk-1.3.26.dll
AddModule mod_jk.c

12
Note: If your OS is a flavor of Unix, then you will most likely be pointing at the
file mod_jk-1.3-eapi.so. If you are on a Windows box, which is what I am
currently using, then you will most likely be pointing to the file mod_jk-1.3.26.dll.

4. We must now tell mod_jk the location of our workers.properties file. This is done
by using the JkWorkersFile property. Make sure you use the appropriate path
when defining the location of your properties file.

JkWorkersFile C:/Tomcat4_1_12/conf/workers.properties

5. Our next step is an optional but very useful (when you run into problems) step:
naming a log file that will record mod_jk's actions. You do this by adding two
additional properties to the httpd.conf file. The first of these properties,
JkLogFile, identifies the location of the log file. The second, JkLogLevel,
defines the logging level, which can be one of three logging levels: debug, error,
or info, which decrease in level of verbosity, respectively.

Note: If you do not define a log level, then no log file will be generated.

JkLogFile C:/Tomcat4_1_12/logs/mod_jk.log
JkLogLevel debug

6. The next step is to tell Apache that we want all static content requested from the
/examples directory to be served from the
<CATALINA_HOME>/webapps/examples directory. This is accomplished using
the Alias directive, as follows:

Alias /examples C:/Tomcat4_1_12/webapps/examples

7. We now need to tell Apache that we want all requests with the patterns
/examples/servlet/* and /examples/*.jsp to be rerouted and serviced by the
worker named testWorker. This is accomplished using the JkMount directive, as
follows.
8. JkMount /examples/servlet/* testWorker
JkMount /examples/*.jsp testWorker

9. The final step in our Apache/Tomcat integration is a step that restricts all requests
to the /example application's WEB-INF directory. This is done by telling Apache
that it should deny all requests to the /examples/WEB-INF directory. The
following <Location> element enforces this constraint:
10.<location "/examples/web-inf/">
11.AllowOverride None
12.deny from all
</location>

When all of these changes are made, you should have an addition similar to the
following, with appropriate path changes, at the bottom of Apache's httpd.conf file.

13
LoadModule jk_module libexec/mod_jk-1.3.26.dll
AddModule mod_jk.c
JkWorkersFile C:/Tomcat4_1_12/conf/workers.properties
JkLogFile C:/Tomcat4_1_12/logs/mod_jk.log
JkLogLevel debug
Alias /examples C:/Tomcat4_1_12/webapps/examples
JkMount /examples/servlet/* testWorker
JkMount /examples/*.jsp testWorker
<Location "/examples/WEB-INF/">
AllowOverride None
deny from all
</Location>

What Have We Done?

As I mentioned in the previous section, the mod_jk modules act like conduits between a
Web server (Apache, for our purposes) and Tomcat. In the last two sections, we basically
installed and configured this conduit. Now Apache performs in the following manner:

On Apache Startup:

1. Apache loads the mod_jk module.


2. It then tells the mod_jk module that all of its workers are defined in the
worker.properties file, which in our case defines a single worker, testWorker.
3. Apache then associates all requests for the patterns /examples/servlet/* and
/example/*.jsp with the worker testWorker.

When a request, including either of the patterns /examples/servlet/* or


/example/*.jsp, is received:

1. Apache will turn the request over to the mod_jk module.


2. mod_jk will then pass the request to the Tomcat Connector
org.apache.ajp.tomcat4.Ajp13Connector, which is listening on port 8009.
3. This Connector then takes over and services the request as if it were running
inside the Tomcat container.
4. When the request has been serviced, the
org.apache.ajp.tomcat4.Ajp13Connector will return the results back to the
mod_jk module and control will be shifted back to Apache.

Testing Your New Configuration

At this point, you can now test your changes. To do this you must first start Tomcat and
then start the Apache server. When both servers are up and running, open your browser to
either http://localhost/examples/servlets/index.html or
http://localhost/examples/jsp/index.html and browse around, testing your new integration.

That about does it for a basic Apache/Tomcat configuration. Do note that all requests to
the /examples application are no longer using localhost:8080, but are instead using

14
localhost. This is because Tomcat is listening to port 8080, while Apache is servicing
requests using the default port of 80, which is being served by Apache.

Up Next: Next time, I'll take another look at server.xml while we configure Tomcat to
receive requests from Microsoft's Internet Information Server (IIS).

James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software


consultancy.

Demystifying Tomcat 4's server.xml File


by James Goodwill
07/31/2002

The Tomcat server.xml file allows you to configure Tomcat using a simple XML
descriptor. This XML file is at the heart of Tomcat. In this article, I will focus on the
configuration of all of the major Tomcat components found in the server.xml file. To
examine these components, open your server.xml file, which can be found in the conf/
directory of your Tomcat installation. The following listing contains a simplified version
of the default server.xml file.

Note: In this article, we will be focusing on the server.xml file as it is configured for
Tomcat 4.0.4.

Example 1. A simple server.xml file

<Server port="8005" shutdown="SHUTDOWN" debug="0">


<Service name="Tomcat-Standalone">
<Connector
className="org.apache.catalina.connector.http.HttpConnector"
port="8080" minProcessors="5" maxProcessors="75"
enableLookups="true" redirectPort="8443"
acceptCount="10" debug="0" connectionTimeout="60000"/>
<Engine name="Standalone" defaultHost="localhost" debug="0">
<Logger className="org.apache.catalina.logger.FileLogger"
prefix="catalina_log." suffix=".txt"
timestamp="true"/>
<Realm className="org.apache.catalina.realm.MemoryRealm" />
<Host name="localhost" debug="0" appBase="webapps"
unpackWARs="true">
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs" prefix="localhost_access_log." suffix=".txt"
pattern="common"/>
<Logger className="org.apache.catalina.logger.FileLogger"
directory="logs" prefix="localhost_log." suffix=".txt"
timestamp="true"/>
<Context path="/examples" docBase="examples" debug="0"
reloadable="true">
<Logger className="org.apache.catalina.logger.FileLogger"

15
prefix="localhost_examples_log." suffix=".txt"
timestamp="true"/>
</Context>
</Host>
</Engine>
</Service>
</Server>

The <Server> Element

The first element found in the server.xml file is the <Server> element. This element
represents the entire Tomcat container. It is used as a top-level element for a single
Tomcat instance.

The <Server> element is defined by the org.apache.catalina.Server interface. The


Server interface is a simple singleton element that represents the entire Tomcat JVM.
Each <Server> may contain one or more Service instances. The following list defines the
possible attributes that can be set for the <Server> element.

className: Names the fully-qualified Java class name of the class that implements the
org.apache.cataline.Server interface. If no class name is specified, the
implementation will be used, which is the
org.apache.catalina.core.StandardServer.

port: Names the TCP/IP port number to which the server listens for a shutdown
command. The TCP/IP client that issues the shutdown command must be running on the
same computer that is running Tomcat. This attribute is required.

shutdown: Defines the command string to shut down Tomcat. It must be received by the
server on the named port. This attribute is required.

The <Server> element defined in the default server.xml file is contained in the
following code snippet:

<Server port="8005"
shutdown="SHUTDOWN"
debug="0">

Note: The debug attribute is available to all Tomcat elements. It states the debug level to
use when logging messages to a defined Logger. We will look at a Logger definition
later in this article.

The <Server> element cannot be configured as a child of any elements. It can be


configured as a parent to the <Service> element.

16
The <Service> Element

The next element in the server.xml file is the <Service> element, which acts as a
container for one or more <Connector> elements that share a single <Engine> element.
One or more <Service> elements may be nested inside of a single <Server> element.
The <Service> element is defined by the org.apache.catalina.Service interface.
The following list describes the possible <Service> element attributes.

className: Names the fully-qualified Java class name of the class that implements the
org.apache.cataline.Service interface. If no class name is specified, the
implementation will be used, which is the
org.apache.catalina.core.StandardService.

shutdown: Defines the command string to shut down Tomcat. It must be received by the
server on the named port. This attribute is required.

The <Service> element found in our server.xml file describes a service that represents
a stand-alone Tomcat service that will handle all direct requests received by Tomcat.

<Service name="Tomcat-Standalone">

Note: I will discuss how to add additional <Service> elements in a subsequent article.

The <Service> element can be configured as a child of the <Server> element. It can be
configured as a parent to the <Connector> and <Engine> elements.

The <Engine> Element

The third element in the server.xml file is the <Engine> element, which represents the
Catalina servlet container. There can only be one <Engine> element for each defined
<Service>. This single <Engine> component will receive all requests received by all of
the defined <Connector> components. The <Engine> element must be nested
immediately after the <Connector> elements, inside of its owning <Service> element.

The <Engine> element is defined by the org.apache.catalina.Engine interface. The


following list describes the possible <Engine> element attributes.

className: Names the fully-qualified Java class name of the class that implements the
org.apache.cataline.Engine interface. If no class name is specified, the
implementation will be used, which is the
org.apache.catalina.core.StandardEngine.

defaultHost: Names the host name to which all requests will be defaulted if not
otherwise named. The named host must be defined by a child <Host> element.

17
name: Defines the logical name of this engine. The name selected is arbitrary, but it is
required.

The following code snippet contains the <Engine> element defined in the server.xml
file. The element defines an engine named Standalone with a default host of localhost.

<Engine name="Standalone" defaultHost="localhost" debug="0">

The <Engine> element can be configured as a child of the <Service> element. It can be
configured as a parent to the following elements:

• <Logger>
• <Realm>
• <Valve>
• <Host>

The <Host> Element

The <Host> element defines the virtual hosts that are contained in each instance of a
Catalina <Engine>. Each <Host> can be a parent to one or more Web applications, which
are represented by a <Context> component, which will be described in the following
section.

You must define at least one <Host> for each Engine element. The possible attributes for
the <Host> element are described below.

className: Names the fully-qualified Java class name of the class that implements the
org.apache.catalina.Host interface. If no class name is specified, the implementation
will be used, which is the org.apache.catalina.core.StandardHost.

appBase: Defines the directory for this virtual host. This directory is the pathname of the
Web applications to be executed in this virtual host. This value can be an absolute path, or
a path that is relative to the <CATALINA_HOME> directory. If this value is not specified, the
relative value webapps will be used.

unpackWARs: Determines if WAR files should be unpacked, or run directly from the
WAR file. If not specified, the default value is true.

name: Defines host name of this virtual host. This attribute is required, and must be
unique among the virtual hosts running in this servlet container.

The <Host> element defined for the Standalone <Engine> is listed in the following code
snippet:

<Host name="localhost" debug="0" appBase="webapps" unpackWARs="true">

18
The host definition defines a host named localhost that can be accessed by opening the
URL http://localhost:8080/.

Note: The port 8080 appended to the previous URL is defined by the <Connector>
element, which will be described later in this article.

The <Host> element is configured as a child of the <Engine> element. It can be


configured as a parent to the following elements:

• <Logger>
• <Realm>
• <Valve>
• <Context>

The <Context> Element

The <Context> element is the most commonly used element in the server.xml file. It
represents an individual Web application that is running within a defined <Host>. There
is no limit to the number of contexts that can be defined within a <Host> element. Each
<Context> definition must have a unique context path, which is defined by the path
attribute. The possible attributes for the <Context> element are described below.

className: Names the fully-qualified Java class name of the class that implements the
org.apache.catalina.Host interface. If no class name is specified, the implementation
will be used, which is the org.apache.catalina.core.StandardContext.

cookies: Determines if you want cookies to be used for session identifier. The default
value is true.

crossContext: When set to true, allows the ServletContext.getContext() method to


successfully return the ServletContext for other Web applications running in the same
host. The default value is false, which will prevent the access of cross-context access.

docBase: Defines the directory for the Web application associated with this <Context>.
This is the pathname of a directory that contains the resources for the Web application.

path: Defines the context path for this Web application. This value must be unique for
each <Context> defined in a given <Host>.

reloadable: If set to true, causes Tomcat to check for class changes in the WEB-
INF/classes/ and WEB-INF/lib directories. If these classes have changed, the
application owning these classes will automatically be reloaded. This feature should only
be used during development. This setting will cause severe performance degradation, and
therefore should be set to false when in a production environment.

19
wrapperClass: Defines the Java class name of the org.apache.catalina.Wrapper
implementation class that will be used to wrap servlets managed by this Context. If not
specified, the standard value org.apache.catalina.core.StandardWrapper will be
used.

useNaming: Set this value to true if you want Catalina to enable JNDI. The default value
is true.

override: Set this value to to override the DefaultContext configuration.The default


value is false.

workDir: Defines the pathname to a scratch directory that will be used by this
<Context> for temporary read and write access. The directory will be made visible as a
servlet context attribute of type java.io.File, with the standard key of
java.servlet.context.tempdir. If this value is not specified, Tomcat will use the
work directory.

The <Context> element that defines the /examples application is included in the
following code snippet:

<Context path="/examples" docBase="examples" debug="0"


reloadable="true">

The context definition defines a Web application named /examples that will have all of
its resources stored in the directory <TOMCAT_HOME>/Webapps/examples. This context
also states that this application will be reloaded when class files are changed.

The <Context> element is configured as a child of the <Host> element. It can be


configured as a parent to the following elements:

• <Logger>
• <Loader>
• <Realm>
• <Manager>
• <Ejb>
• <Environment>
• <Parameter>
• <Resource>
• <ResourceParams>

Note: If you do not have special configuration needs, you can use the default context
configuration that is described in the default web.xml file, which can be found in the
<CATALINA_HOME>/conf/ directory.

20
The <Connector> Element

The final element we are going to examine is the <Connector> element. The
<Connector> element defines the component that does the actual managing of requests
and responses to and from a calling client. The <Connector> element is defined by the
org.apache.catalina.Connector interface. The <Connector> element's attributes are
described below.

className: Names the fully-qualified Java class name of the class that implements the
org.apache.catalina.Host interface. If no class name is specified, the implementation
will be used, which is the org.apache.catalina.Connector interface.

enableLookups: Determines whether DNS lookups are enabled. The default value for
this attribute is true. When DNS lookups are enabled, an application calling
request.getRemoteHost() will be returned the domain name of the calling client.
Enabling DNS lookups can have an unfavorable impact on performance, so this value
should most often be set to false.

redirectPort: Names the TCP/IP port number to which a request should be redirected, if
it comes in on a non-SSL port, and is subject to a security constraint with a transport
guarantee that requires SSL.

name: Defines host name of this virtual host. This attribute is required, and must be
unique among the virtual hosts running in this servlet container.

The <Connector> element is configured as a child of the <Service> element. It cannot


be configured as a parent to any element.

The HTTP Connector

The most common Tomcat connector is the HTTP connector, which is preconfigured with
Tomcat. Like all connectors, the HTTP connector implements the
org.apache.catalina.Connector interface, which automatically associates it with the
connector attributes described above, but it also defines a set of attributes that are specific
to the HttpConnector. These additional attributes are listed here.

port: Names the TCP/IP port number on which the connector listens for requests. The
default value is 8080. If you want Tomcat to process requests using the default HTTP port
of 80, simply set this attribute to 80.

address: This attribute is used for servers with more than one IP address. It specifies
which address will be used for listening on the specified port. If this attribute is not
specified, this named port number will be used on all IP addresses associated with this
server.

21
bufferSize: Specifies the size, in bytes, of the buffer to be provided for use by input
streams created by this connector. Increasing the buffer size can improve performance, at
the expense of higher memory usage. The default value is 2048 bytes.

className: Names the fully-qualified Java class name of the HTTP connector class. This
value must equal org.apache.cataline.connector.http.HttpConnector.

enableLookups: Same for all connectors.

proxyName: Specifies the server name to use if this instance of Tomcat is behind a
firewall. This is an optional attribute.

proxyPort: Specifies the HTTP port to use if this instance of Tomcat is behind a firewall.
An optional attribute.

minProcessors: Defines the minimum number of processors, or instances, to start at


initialization time. The default value is 5.

maxProcessors: Defines the maximum number of allowed processors, or instances, that


can be started. The default value is 20. An unlimited number of processors can be started
if the value of the maxProcessors attribute is set to a number less than zero.

acceptCount: Specifies the number of requests that can be queued on the listening port.
The default value is 10.

connectionTimeout: Defines time, in milliseconds, before a request terminates. The


default value is 60000 milliseconds. To disable connection timeouts, the
connectionTimeout value should be set to -1.

An example <Connector> defining a HTTP connector is contained in the following code


snippet:

<Connector className="org.apache.catalina.connector.http.HttpConnector"
port="8080"
minProcessors="5"
maxProcessors="75"
enableLookups="true"
redirectPort="8443"
acceptCount="10"
debug="0"
connectionTimeout="60000"/>

This <Connector> defines an HttpConnector that listens for requests on port 8080. It
starts with a minimum of five processors and can start up to as many as 75 processors.

Up Next: in the next Tomcat article, I will take another look at the server.xml while we
configure Tomcat to receive requests from the Apache Web server.

22
James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software
consultancy.

Embedding Tomcat Into Java Applications


04/03/2002

n this article, we'll extend our Tomcat discussions to the application level by creating a
Java application that manages an embedded version of the Tomcat JSP/servlet container.
Tomcat can be broken down into a set of containers, each with their own purpose. These
containers are by default configured using the server.xml file. When embedding, you
will not be using this file; therefore, you will need to assemble instances of these
containers programmatically. The following XML code snippet contains the hierarchy of
the Tomcat containers:

<Server>
<Service>
<Connector />
<Engine>
<Host>
<Context />
</Host>
</Engine>
</Service>
</Server>

Note: Each of the previously listed elements contains attributes to set their appropriate
behaviors, but for our purposes, only the element hierarchies and relationships are
important.

This is the structure that we need to create with our embedded application. The <Server>
and <Service> elements of this structure are going to be implicitly created, therefore we
do not have to create these objects ourselves. The steps to create the remainder of the
container structure are listed below.

These are the same steps that we must perform in order to create our own embedded
version of the Tomcat container:

1. Create an instance of an org.apache.catalina.Engine; this object represents


the <Engine> element above and acts as a container to the <Host> element.
2. Create an org.apache.catalina.Host object, which represents a virtual host,
and add this instance to the Engine object.
3. Now you need to create n-number of org.apache.catalina.Context objects
that will represent each Web application in this Host.

23
4. Once each of your Contexts are created, you then need to add each of the created
Contexts to the previously created Host. For our example, we'll create a single
Context that will represent our onjava application.
5. The final step is to create an org.apache.catalina.Connector object and
associate it with the previously created Engine. The Connector object is the
object that actually receives a request from the calling client.

To create this application, we'll leverage some existing Tomcat classes that have been
developed to ease this type of integration. The main class we will use is the
org.apache.catalina.startup.Embedded class, which can be found in the
<CATALINA_HOME>/src/catalina/src/share/org/apache/catalina/startup
directory. The following source listing contains our sample application that builds these
containers using the org.apache.catalina.startup.Embedded class.

package onjava;

import java.net.URL;
import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Deployer;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.logger.SystemOutLogger;
import org.apache.catalina.startup.Embedded;
import org.apache.catalina.Container;

public class EmbeddedTomcat {

private String path = null;


private Embedded embedded = null;
private Host host = null;
/**
* Default Constructor
*
*/
public EmbeddedTomcat() {

/**
* Basic Accessor setting the value of the context path
*
* @param path - the path
*/
public void setPath(String path) {

this.path = path;
}

/**
* Basic Accessor returning the value of the context path
*
* @return - the context path
*/

24
public String getPath() {

return path;
}

/**
* This method Starts the Tomcat server.
*/
public void startTomcat() throws Exception {

Engine engine = null;


// Set the home directory
System.setProperty("catalina.home", getPath());

// Create an embedded server


embedded = new Embedded();
// print all log statments to standard error
embedded.setDebug(0);
embedded.setLogger(new SystemOutLogger());

// Create an engine
engine = embedded.createEngine();
engine.setDefaultHost("localhost");

// Create a default virtual host


host = embedded.createHost("localhost", getPath()
+ "/webapps");
engine.addChild(host);

// Create the ROOT context


Context context = embedded.createContext("",
getPath() + "/webapps/ROOT");
host.addChild(context);

// Install the assembled container hierarchy


embedded.addEngine(engine);

// Assemble and install a default HTTP connector


Connector connector =
embedded.createConnector(null, 8080, false);
embedded.addConnector(connector);
// Start the embedded server
embedded.start();
}

/**
* This method Stops the Tomcat server.
*/
public void stopTomcat() throws Exception {
// Stop the embedded server
embedded.stop();
}

/**
* Registers a WAR with the container.
*
* @param contextPath - the context path under which the

25
* application will be registered
* @param warFile - the URL of the WAR to be
* registered.
*/
public void registerWAR(String contextPath, URL warFile)
throws Exception {

if ( contextPath == null ) {

throw new Exception("Invalid Path : " + contextPath);


}
if( contextPath.equals("/") ) {

contextPath = "";
}
if ( warFile == null ) {

throw new Exception("Invalid WAR : " + warFile);


}

Deployer deployer = (Deployer)host;


Context context = deployer.findDeployedApp(contextPath);

if (context != null) {

throw new
Exception("Context " + contextPath
+ " Already Exists!");
}
deployer.install(contextPath, warFile);
}

/**
* Unregisters a WAR from the web server.
*
* @param contextPath - the context path to be removed
*/
public void unregisterWAR(String contextPath)
throws Exception {

Context context = host.map(contextPath);


if ( context != null ) {

embedded.removeContext(context);
}
else {

throw new
Exception("Context does not exist for named path :
+ contextPath);
}
}

public static void main(String args[]) {

try {

26
EmbeddedTomcat tomcat = new EmbeddedTomcat();
tomcat.setPath("d:/jakarta-tomcat-4.0.1");
tomcat.startTomcat();

URL url =
new URL("file:D:/jakarta-tomcat-4.0.1"
+ "/webapps/onjava");
tomcat.registerWAR("/onjava", url);

Thread.sleep(1000000);

tomcat.stopTomcat();

System.exit(0);
}
catch( Exception e ) {

e.printStackTrace();
}
}
}

You should begin your examination of the EmbeddedTomcat application source with the
main() method. This method first creates an instance of the EmbeddedTomcat class. It
then sets the path of the Tomcat installation that will be hosting our Tomcat instance. This
path is equivalent to the <CATALINA_HOME> environment variable. The next action
performed by the main() method is to invoke the startTomcat() method. This is the
method that implements the container-construction steps described earlier. The steps
performed by this method are listed below.

1. The main() method begins by setting the system property to the value of the path
attribute:
2. // Set the home directory
System.setProperty("catalina.home", getPath());

Note:

Make sure you use the value of <CATALINA_HOME> as the directory value passed
to the setPath() method.

3. The next step performed by this method is to create an instance of the Embedded
object and set the debug level and current logger.
4. // Create an embedded server
5. embedded = new Embedded();
6. embedded.setDebug(5);
7. // print all log statments to standard error
embedded.setLogger(new SystemOutLogger());

27
Note:

The debug level should be 0, when deploying a production Web application.


Setting the debug level to 0 reduces the amount of logging performed by Tomcat,
which will improve performance significantly.

8. After the application has an instance of the Embedded object, it creates an instance
of an org.apache.catalina.Engine and sets the name of the default host. The
Engine object represents the entire Catalina servlet container.
9. // Create an engine
10. engine = embedded.createEngine();
engine.setDefaultHost("localhost");

11. After an Engine has been instantiated, we create an org.apache.catalina.Host


object, named localhost, with a path pointing to the
<CATALINA_HOME>/webapps/ directory, and add it the Engine object. The Host
object defines the virtual hosts that are contained in each instance of a Catalina
Engine.
12. // Create a default virtual host
13. host = embedded.createHost("localhost", getPath() +
14. "/webapps");
15.
engine.addChild(host);

16. The next step performed by the startTomcat() method is to create an


org.apache.catalina.Context object, which represents the ROOT Web
application packaged with Tomcat, and add it the to the previously created Host.
The ROOT Web application is the only application that will be installed by default.
17. // Create the ROOT context
18. Context context = embedded.createContext("",
19. getPath() + "/webapps/ROOT");
host.addChild(context);

20. The next step adds the Engine containing the created Host and Context to the
Embedded object.
21. // Install the assembled container hierarchy
embedded.addEngine(engine);

22. After the engine is added to the Embedded object, the startTomcat() method
creates an org.apache.catalina.Connector object and associates it with the
previously created Engine. The <Connector> element defines the class that does
the actual handling of requests and responses to and from a calling client
application. In the following snippet, an HTTP connector that listens to port 8080
is created and added to the Embedded object.
23. // Assemble and install a default HTTP connector
24. Connector connector = embedded.createConnector(null,
25. 8080, false);
embedded.addConnector(connector);

28
26. The final step performed by the startTomcat() method starts the Tomcat
container.

embedded.start();

When startTomcat() returns, the main method calls the registerWAR() method, which
installs the previously deployed onjava application to the Embedded object. The URL
used in this example can point to any Webapp directory that follows the specification for
Java Servlet 2.2 and later.

URL url =
new URL("file:D:/jakarta-tomcat-4.0.1"
+ "/webapps/onjava");
tomcat.registerWAR("/onjava", url);

The main application is then put to sleep to allow the embedded server time to service
requests. When the application awakes, the embedded server is stopped and the
application exits.

To test this application, you must complete the following steps:

1. Compile the EmbeddedTomcat.java class.


2. Make sure all other instances of Tomcat are shut down.
3. Add the following jar files, all of which can be found in the Tomcat installation,
to your application classpath.
o <CATALINA_HOME>/bin/bootstrap.jar
o <CATALINA_HOME>/server/lib/catalina.jar
o <CATALINA_HOME>/server/lib/servlet-cgi.jar
o <CATALINA_HOME>/server/lib/servlets-common.jar
o <CATALINA_HOME>/server/lib/servlets-default.jar
o <CATALINA_HOME>/server/lib/servlets-invoker.jar
o <CATALINA_HOME>/server/lib/servlets-manager.jar
o <CATALINA_HOME>/server/lib/servlets-snoop.jar
o <CATALINA_HOME>/server/lib/servlets-ssi.jar
o <CATALINA_HOME>/server/lib/servlets-webdav.jar
o <CATALINA_HOME>/server/lib/jakarta-regexp-1.2.jar
o <CATALINA_HOME>/lib/naming-factory.jar
o <CATALINA_HOME>/common/lib/crimson.jar
o <CATALINA_HOME>/common/lib/jasper-compiler.jar
o <CATALINA_HOME>/common/lib/jasper-runtime.jar
o <CATALINA_HOME>/common/lib/jaxp.jar
o <CATALINA_HOME>/common/lib/jndi.jar
o <CATALINA_HOME>/common/lib/naming-common.jar
o <CATALINA_HOME>/common/lib/naming-resources.jar
o <CATALINA_HOME>/common/lib/servlet.jar
o <CATALINA_HOME>/common/lib/tools.jar

29
4. Make sure that your classpath includes the directory containing the compiled
EmbeddedTomcat class.
5. Execute the following command:

java onjava.EmbeddedTomcat

If everything went according to plan, you should see some log statements in the console
window:

HttpProcessor[8080][0] Starting background thread


HttpProcessor[8080][0] Background thread has been started
HttpProcessor[8080][1] Starting background thread
HttpProcessor[8080][1] Background thread has been started
HttpProcessor[8080][2] Starting background thread
HttpProcessor[8080][2] Background thread has been started
HttpProcessor[8080][3] Starting background thread
HttpProcessor[8080][3] Background thread has been started
HttpProcessor[8080][4] Starting background thread
HttpProcessor[8080][4] Background thread has been started

Once you see the previous text, you will be able to access the ROOT and /onjava Web
applications using the following URLs:

• http://localhost:8080/
• http://localhost:8080/onjava/

Note: The onjava application that we are using throughout this article is the Web
application from my previous Tomcat articles.

Up next: in the next Tomcat article, we will continue our embedded discussions by
debugging a Web application that is running in our embedded container.

James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software


consultancy.

Using SOAP with Tomcat


02/27/2002

The Apache SOAP Project is an open source Java implementation of the Simple Object
Access Protocol (SOAP) v1.1. SOAP is a wire protocol that leverages HTTP or SMTP as
its transport layer and XML as its data layer, to execute remote methods, known as SOAP
services.

30
The Apache implementation of SOAP provides two methods for invoking SOAP
services: a Remote Procedure Call (RPC) model and a message-based model. The RPC
method, which is the focus of this article, is a synchronous technique using a client-server
model to execute remote SOAP services. The message-based model uses SMTP to
transport SOAP documents to and from the appropriate SOAP server. While this method
is interesting, it is out of the scope of this article.

The RPC model can be defined using the following steps:

1. A client application builds an XML document containing the URI of the server
that will service the request, the name of the remote method to execute, and the
parameters associated with that method.
2. The targeted server receives and unwinds the XML document. It then executes the
named method.
3. After the named method has returned its results, the results are packed into a
response XML document, which is sent back to the calling client.
4. The client application receives the response and unwinds the results, which
contains the response of the invocated method.

Integrating Apache SOAP into Tomcat


Before we begin using the Apache SOAP project, we must acquire the necessary
components to execute SOAP services. Listing 1 provides a list of these items and where
they can be found.

Listing 1. Components required to execute SOAP clients and services

SOAP v2.2 (soap.jar)


http://xml.apache.org/soap/index.html
mail.jar v1.2
This .jar file is packaged with Tomcat in the <TOMCAT_HOME>/common/lib/
directory.
activation.jar v1.0.1
This .jar file is packaged with Tomcat in the <TOMCAT_HOME>/common/lib/
directory.
xerces.jar v1.4.2
This .jar file is packaged with Tomcat in the <TOMCAT_HOME>/common/lib/
directory.

Once we have all of these items, we need to extract the SOAP archive to a local directory.
We then need to add each of the previously mentioned .jar files to your classpath,
including soap.jar, which comes packaged with the SOAP archive. This step is very
important and must not be ignored.

31
Deploying Apache-SOAP using Tomcat

There are several ways to deploy a SOAP project to Tomcat. Of these methods, we are
going to perform the easiest, which is simply to copy the soap.war file to the
<TOMCAT_HOME>/webapps/ directory. You can find this file in the SOAP 2.2 archive.

Once you have moved the soap.war file into <TOMCAT_HOME>/webapps/directory, you
need to make sure that each of the previously listed .jar files are in the
<TOMCAT_HOME>/common/lib/ directory, excluding the soap.jar file.

After you have copied the above files to the named locations, restart Tomcat. You should
now be able to access the SOAP Web application by opening your Web browser to
http://localhost:8080/soap/

You should see a page similar to Figure 1.

At this point, you should also be able to use the SOAP admin tool, which can be accessed
by selecting the Run link. Figure 2 shows the home page for the SOAP admin tool. From
this page, you can list the current services, deploy new services, and remove previously-
deployed services.

Creating a Sample SOAP Application


Now let's develop a simple SOAP application that acts as a simple integer calculator, with
only addition and subtraction functions. To do this, we need to first develop a SOAP
service for handling our calculator functions, and then create a client to access the
service.

SOAP Services

Writing an RPC-based SOAP service is a very simple process that can be broken down
into three steps.

• Create the Java class that contains the SOAP service to publish.
• Create a deployment descriptor that describes the service.
• Publish the service.

Creating a SOAP Service

Creating a SOAP service is the simplest step of the entire "SOAPifying" process. A
SOAP service can be just about any Java class that exposes public methods for
invocation. The class does not need to know anything about SOAP, or even that it is
being executed as a SOAP service.

32
The only restriction is that the method parameters of a SOAP service must be
serializable. The available types that can, by default, be used as SOAP service parameters
are shown in Listing 2.

Listing 2. Types that can be used as SOAP service parameters.

• All Java primitive types and their corresponding wrapper classes


• Java arrays
• java.lang.String
• java.util.Date
• java.util.GregorianCalendar
• java.util.Vector
• java.util.Hashtable
• java.util.Map
• java.math.BigDecimal
• javax.mail.internet.MimeBodyPart
• java.io.InputStream
• javax.activation.DataSource
• javax.activation.DataHandler
• org.apache.soap.util.xml.QName
• org.apache.soap.rpc.Parameter
• java.lang.Object (must be a JavaBean)

The source listing for our service, a simple adding and subtracting calculator, is shown in
its entirety in Example 1.

Example 1. Simple calculator service

package onjava;
public class CalcService {
public int add(int p1, int p2) {

return p1 + p2;
}

public int subtract(int p1, int p2) {

return p1 - p2;
}
}

As you can see, there is really nothing special about this class. It simply defines two
public methods, add() and subtract(), each with a parameter list containing two native
ints. To make this class available, build and copy it into the
<TOMCAT_HOME>/webapps/soap/WEB-INF/classes/onjava/ directory.

33
Creating the Deployment Descriptor

The next step in creating a new SOAP service is to create a deployment descriptor. The
deployment descriptor describes the SOAP service. This description is required for the
service to be published as an Apache SOAP service. The deployment descriptor for our
service is contained in Example 2.

Example 2. Deployment descriptor for a simple calculator

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:onjavaserver">
<isd:provider type="java"
scope="Application"
methods="add subtract">
<isd:java class="onjava.CalcService"/>
</isd:provider>
<isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:fau
ltListener>
</isd:service>

The deployment descriptor for our calculator service contains only three elements that we
need to look at. The first element is the service element, which defines two attributes, the
XML namespace and the unique ID of the service to be deployed. The ID defined in the
service element must be unique, since this attribute is used to uniquely identify a
published SOAP service.

The next element we need to examine is the provider element. It defines the actual
implementation of the SOAP service. It does this with three attributes, each of which are
defined as follows:

Listing 3. Attributes of the Provider Element

type
The type attribute defines the implementation type of the SOAP service. We
defined our service as a Java service.
scope
The scope attribute defines the lifetime of the SOAP service. The possible values
are page, scope, session, and application. These scope values map one-to-one
with the scope values defined by the JSP specification.
methods
The methods attribute defines the names of the methods that can be invoked on
this service object. This list should be a space-separated list of method names.

The final element of the deployment descriptor that we'll look at here is the java
element. This element contains a single attribute, class, which names the fully qualified
class of the named service.

34
Running the Server-Side Admin Tool to Manage Services

Now that we have defined our SOAP service and its deployment descriptor, we can
publish it so that it can start servicing requests. To do this, you need to first compile the
service and make sure it is in your classpath.

After you have compiled the service, you're ready to deploy it. The Apache SOAP Project
is packaged with two administration tools -- a graphical tool and a command-line tool.
They both allow you to easily deploy and undeploy services to the SOAP server. The
three functions provided by these tools are listed below:

• The deploy function allows you to deploy a new service to a SOAP server.
• The undeploy function removes an existing SOAP service from a SOAP server.
• The list function lists all deployed SOAP services.

For our examples, we are going to use the Apache SOAP command-line tools to manage
our service. SOAP command-line management functions are implemented by the
org.apache.soap.server.ServiceManagerClient class. Using the
ServiceManagerClient is very easy. We'll go through each of its functions in this
section.

As we cover the following commands, you should note that each command references a
servlet named rpcrouter. This servlet is at the core of all SOAP actions. It performs all
service management and execution.

• List

The list command lists all currently deployed services. To execute the list
command, type the following line:

java org.apache.soap.server.ServiceManagerClient
http://localhost:8080/soap/servlet/rpcrouter list

If you have not published any other SOAP services, you should get a response
that shows no deployed services. If you examine this command, you see that it
executes the Java application ServiceManagerClient with two parameters: the
location of the SOAP server, and the command to perform (list, in this case).

• Deploy

The deploy command deploys our service to the SOAP server. This command
also uses the ServiceManagerClientwith the deployment descriptor describing
the SOAP service. To deploy our service, execute the following command:

java org.apache.soap.server.ServiceManagerClient
http://localhost:8080/soap/servlet/rpcrouter
deploy DeploymentDescriptor.xml

35
This command takes three parameters: the URL to the SOAP server, the command
deploy, and the file containing our deployment descriptor. After you have
executed this command, execute the list command. You should now see output
listing urn:onjavaserver, which is the unique ID of our service. You can also
view this service from the Web admin tool. Go to
http://localhost:8080/soap/admin/index.html and select the "List" button.
Typical results are shown in Figure 3.

If you select the service name, you will see the details of the service, as shown in
Figure 4.

• Undeploy

The undeploy command removes a previously deployed service. To execute the


undeploy command, type the following line:

java org.apache.soap.server.ServiceManagerClient

http://localhost:8080/soap/servlet/rpcrouter undeploy
urn:onjavaserver

Note: Do not execute this command until you have completed the remaining
exercises in this article.

The undeploy command takes three parameters: the location of the SOAP server,
the actual command to perform (in this case, the undeploy command), and the
name of the service to remove.

SOAP Clients

Now that we have a service defined and deployed, let's write a client that will execute one
of the service's methods. The Apache SOAP Project provides a client-side API that makes
it extremely simple to create SOAP clients. An example client, which we will use to
execute the subtract method of our service, can be found in Example 3.

Example 3. A SOAP Client

package onjava;

import java.io.*;
import java.net.*;
import java.util.*;
import org.apache.soap.*;
import org.apache.soap.rpc.*;

public class CalcClient {

public static void main(String[] args) throws Exception {

36
URL url = new URL ("http://localhost:8080/soap/servlet/rpcrouter");

Integer p1 = new Integer(args[0]);


Integer p2 = new Integer(args[1]);

// Build the call.


Call call = new Call();
call.setTargetObjectURI("urn:onjavaserver");
call.setMethodName("subtract");
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
Vector params = new Vector();
params.addElement(new Parameter("p1", Integer.class, p1, null));
params.addElement(new Parameter("p2", Integer.class, p2, null));
call.setParams (params);

// make the call: note that the action URI is empty because the
// XML-SOAP rpc router does not need this. This may change in the
// future.
Response resp = call.invoke(url, "" );

// Check the response.


if ( resp.generatedFault() ) {

Fault fault = resp.getFault ();


System.out.println("The call failed: ");
System.out.println("Fault Code = " + fault.getFaultCode());
System.out.println("Fault String = " + fault.getFaultString());
}
else {

Parameter result = resp.getReturnValue();


System.out.println(result.getValue());
}
}
}

This client follows a simple process that is common to most SOAP RPC clients. It first
creates a URL referencing the rpcrouter (which we noted earlier) on the HTTP server
localhost. This is done in the following code snippet:

URL url = new URL


("http://localhost:8080/soap/servlet/rpcrouter");

The next step performed by the client application is to parse the arguments from the
command line. These values will be passed to the SOAP service in a subsequent method.
The values created will be integers.

After the client has parsed to command-line arguments, it creates an instance of an


org.apache.soap.rpc.RPCMessage.Call. The Call object is the main interface used
when executing a SOAP RPC invocation.

To use the Call object, we first tell it which service we want to use. We do this by calling
the setTargetObjectURI, passing it the name of the service that we want to execute. We

37
then set the name of the service method we want to execute using the setMethodName()
method, with the name of the method we want to execute. The next step is to set the
encoding style used in the RPC call. The final step is to add the parameters that are
expected when executing the named method. This is done by creating a Vector of
Parameter objects and adding them to the Call object using the setParams() method.
All of these steps are completed using the following code snippet:

Call call = new Call();

call.setTargetObjectURI("urn:onjavaserver");

call.setMethodName("subtract");

call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);

Vector params = new Vector();

params.addElement(new Parameter("p1", Integer.class, p1, null));

params.addElement(new Parameter("p2", Integer.class, p2, null));

call.setParams (params);

The next step performed by the client application is to call the service method that we are
interested in. This is done using invoke() with the URL we created earlier. Here is the
snippet of code calling the invoke() method:

Response resp = call.invoke(url, "" );

You will notice the return value of the invoke() method is a Response object. The
Response object returns two very important items -- an error code and the value returned
from the executed service method. You check for an error by calling the
generatedFault() method. This method will return true if there was an error returned;
then you can check the getFault() method. If generatedFault() returns false, you can
then get the value returned in the Response object, using the
Response.getReturnValue() method. The following code snippet shows how you
should process the response of an invoke():

if ( resp.generatedFault() ) {

Fault fault = resp.getFault();

System.out.println("The call failed: ");

System.out.println("Fault Code = " + fault.getFaultCode());

System.out.println("Fault String = " + fault.getFaultString());

else {

38
Parameter result = resp.getReturnValue();

System.out.println(result.getValue());

That is all there is to it. To test your client and service, compile the client and execute it
using the following command line:

java onjava.CalcClient 98 90

Note:At this point, you should have the CalcService deployed and Tomcat should be
running.

Summary
In this article we discussed the Apache SOAP Project. We described each of the steps
involved when integrating SOAP into the Tomcat container. We also created a sample
SOAP application demonstrating how each of the SOAP components works together.

As for the next topic we examine, I am leaving it up to you. From this point, we can go in
many different directions. Some of the topics that we can discuss include:

• Further Apache SOAP topics


• The Java Web Services Developers Pack (WSDP)
• The Apache Axis Project (the next Apache SOAP implementation)

Please let me know where you think we should go next, or if you have other related
topics that you would like to see covered. You can reach me at jgoodwill@virtuas.com
Please include "onjava" in the subject line.

James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software


consultancy.

Using Tomcat 4 Security Realms


07/24/2001

In this article, we will

• Define security realms


• Describe Memory Realms
• Describe JDBC Realms

39
Security Realms

A security realm is a mechanism used for protecting Web application resources. It gives
you the ability to protect a resource with a defined security constraint and then define the
user roles that can access the protected resource. Tomcat has this type of realm
functionality built in. The component that provides this functionality is the
org.apache.catalina.Realm interface. It provides a mechanism by which a collection
of usernames, passwords, and their associated roles can be integrated into Tomcat. If you
download the Tomcat source, you will find this interface in the following location:

<tomcat_home>/src/catalina/src/share/org/apache/catalina/Realm.java

There are two Realm implementations provided in Tomcat 4. We will discuss each of
these implementations in the following sections.

Memory Realms

The first Realm implementation provided with Tomcat is a memory realm. The class that
defines the memory realm is org.apache.cataline.realm.MemoryRealm. The
MemoryRealm class uses a simple XML file as a container of users. The following code
snippet contains a sample memory realm XML file:

<tomcat-users>
<user name="tomcat" password="tomcat" roles="tomcat" />
<user name="role1" password="tomcat" roles="role1" />
<user name="both" password="tomcat" roles="tomcat,role1" />
</tomcat-users>

Note: The default location of the MemoryRealms XML file is the


<tomcat_home>/conf/tomcat-users.xml. You can change the location of this file by
substituting a new relative or absolute path in the pathname attribute of the <realm>
element described in the following section.

As you can see, there is nothing terribly complicated about this file. It has a root element
of <tomcat-users>, which contains n-number of the sub-element <user>. The <user>
element contains all of the necessary information to validate a user. This information is
contained in the attributes of the <user> sub-element. Table 1 contains a description of
each of the attributes required in the <user> sub-element.

Table 1. The Attributes of the <user> Sub-Element


Attribute Description
name The name attribute contains a string representing the username that will be
used in the login form.
password The password attribute contains a string representing the password that will be

40
used in the login form.
roles The roles attribute contains the role or roles assigned to the named user. This is
the value that must match the <role-name> sub-element of the security
constraint defined in the web applications web.xml file. If more than one role
is assigned to the user, then the value of the roles attribute must contain a
comma-separated list of roles.

Protecting a Resource with a MemoryRealm

To actually see how a MemoryRealm works, let's create a realm that protects a sample web
application named /onjava. At this point, if you have not already done so, take a look at
my previous OnJava article, Deploying Web Applications to Tomcat. We will be using the
/onjava web application from it. The steps involved in setting up a new MemoryRealm
are described in the following list.

1. Open <tomcat_home>/conf/server.xml and uncomment the following line.

<Realm className="org.apache.catalina.realm.MemoryRealm" />

By un-commenting this <realm> entry, you are making the MemoryRealm the
default realm implementation for the entire default container. If you cannot find
this entry, add it directly under the Engine sub-element.

2. Open <tomcat_home>/webapps/onjava/WEB-INF/web.xml and add the


following security constraint:

<security-constraint>
<web-resource-collection>
<web-resource-name>OnJava Application</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>onjavauser</role-name>
</auth-constraint>
</security-constraint>

There are only two sub-elements that you need to focus upon. The first is the
<url-pattern> sub-element. This sub-element defines the URL pattern that will
be protected by the resource. The entry you included protects the entire /onjava
Web application. The second sub-element, <role-name>, defines the user role
that can access the resource protected by the previously defined <url-pattern>.
In summary, this entire entry states that the /onjava Web application can only be
accessed by users with a defined role of onjavauser.

3. Add the following <login-config> sub-element directly following the


<security-constraint>.

41
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>OnJava Application</realm-name>
</login-config>

The <login-config> sub-element defines the authentication method for the


defined realm. The possible values are BASIC, DIGEST, and FORM. And the
<realm-name> sub-element names the Web resource that this <login-config>
maps to.

4. Open <tomcat_root>/conf/tomcat-users.xml and add the following <user>


sub-element:

<user name="bob" password="password" roles="onjavauser" />

The <user> sub-element you are adding will create a new user in the
MemoryRealm database with a name of bob, a password of password, and a role of
onjavauser. You should notice that the value of the roles attribute matches the
value of the <role-name> sub-element of the previously-defined <security-
contstraint>.

5. To complete this configuration, stop and restart the Tomcat server.

Now let's actually look at how your newly defined realm affects the /onjava web
application. Point your browser at the following URL:

http://localhost:8080/onjava/login.jsp

If everything went according to plan you should see a dialog box similar to the one in
Figure 1.

42
Figure 1. The BASIC Authentication Dialog

Go ahead and enter bob for the User Name, password for the Password, and press "OK."
Again, if everything goes according to plan, you should see the login page of the
/onjava web application. You now have a Web application that is protected by a security
realm that uses the Basic Authentication method to authenticate its users.

JDBC Realms

The second Realm implementation provided with Tomcat is a JDBC realm. The class that
implements the JDBC realm is org.apache.cataline.realm.JDBCRealm. The
JDBCRealm class is much like the MemoryRealm discussed in the previous section, with
the exception of where it stores its collection of users. A JDBCRealm stores all of its users
in a user-defined, JDBC-compliant database. There are several steps involved when
setting up a JDBC realm, but once it is configured it is really simple to manage.

Defining the Users Database

Before you begin configuring Tomcat to use a JDBCRealm, you must first create a
database to hold your collection of users. Our user database is going to contain three
tables. The first table is the users table. The users table contains the user name and
password for each of our users. Table 2 contains the description of the users table.

Table 2. The users Table Definition


Column Description
user_name The user_name column contains a string representing the username that will
be used in the login form. The user_name has a type of varchar(12).
user_pass The user_pass column contains a string representing the user's password.
The user_pass has a type of varchar(12).

43
The second table in the users database is the roles table. The roles table contains all of
the possible roles for the users defined in this database. The roles table contains a single
column, role_name, that is a varchar(12) string representing each role name.

The last table in the users database is the user_roles table. The user_roles table is a
mapping table between the roles and users defined in this database. Table 3 contains the
table definition for the user_roles table.

Table 3. The user_roles Table Definition.


Column Description
user_name The user_name column contains a string representing the username that will
be used in the login form. The user_name has a type of varchar(12).
role_name The role_name column contains a string representing the user's role. The
role_name has a type of varchar(12).

The contents of each of the users database's tables are listed in Tables 4, 5, and 6.

Table 4. The Contents of the users Table


user_name user_pass

robert password
bob password
tomcat password
joe $joe$

Table 5. The Contents of the roles Table


user_name

onjava
manager
tomcat

Table 6. The Contents of the user_roles Table

44
user_name user_pass

bob onjavauser
joe onjavauser
joe manager
tomcat tomcat
robert onjavauser

Creating and Configuring a MySQL Users Database

Now that you have defined the users database, you can actually create the physical
database. Before you can create the users database, you will need to download and
install the MySQL server, which can be found at http://www.mysql.com. You should
also download the latest JDBC driver for MySQL, which can also be found at the
previously mentioned Web site.

Note: For this example we are using MySQL. You can use any JDBC-compliant database
server of your choosing.

After you have MySQL installed, you need to complete the following steps to create and
configure a MySQL Users database:

1. Start the mysql client found in the <mysql_home>/bin/ directory.


2. Create the Users database, which will be explicitly named tomcatusers, by
executing the following command:

create database tomcatusers;

3. Create the users table using the following command:

create table users


(
user_name varchar(15) not null primary key,
user_pass varchar(15) not null
);

4. Create the roles table using the following command:

create table roles


(
role_name varchar(15) not null primary key
);

5. Create the user_roles table using the following command:

45
create table users
(
user_name varchar(15) not null,
role_name varchar(15) not null,
primary key(user_name, role_name)
);

6. Insert the user data into the users table, by executing the following commands:

insert into users values("bob", "password");


insert into users values("joe", "$joe$");
insert into users values("robert", "password");
insert into users values("tomcat", "password");

7. Insert the roles data into the roles table with the following commands:

insert into roles values("onjavauser");


insert into roles values("manager");
insert into roles values("tomcat");

8. Insert the user roles data into the user_roles table with the following commands:

insert into user_roles values("bob", "onjavauser");


insert into user_roles values("joe", "onjavauser");
insert into user_roles values("joe", "manager");
insert into user_roles values("robert", "onjavauser");
insert into user_roles values("tomcat", "tomcat");

Configuring Tomcat to Use a JDBC Realm

Now that you have a container of users, let's configure Tomcat to use the JDBC container
instead of the previously configured MemoryRealm. The steps involved in configuring a
JDBCRealm are described in the following list.

1. Open <tomcat_home>/conf/server.xml and place comment tags around the


previously uncommented <realm> element.

<!-- <Realm className="org.apache.catalina.realm.MemoryRealm" />


-->

2. Place the following code snippet directly below the previously referenced
<realm> element:

<realm classname="org.apache.catalina.realm.JDBCRealm" debug="99"


drivername="org.gjt.mm.mysql.Driver"
connectionurl="jdbc:mysql://localhost/tomcatusers?user=test;p
assword=test"
usertable="users" usernamecol="user_name"
usercredcol="user_pass"
userroletable="user_roles" rolenamecol="role_name"/>

46
Make sure that the JAR file containing the JDBC driver referenced by the
driverName attribute is placed in Tomcat's CLASSPATH. If you are using the
JDBC-ODBC bridge, the driver will already be in Tomcat's CLASSPATH. You will
also need to replace the user and password values with the appropriate values for
your database installation. This new <realm> entry defines a JDBCRealm that
leverages our database as its container of users. The attributes used in the
<realm> element, with additional optional attributes, are described in Table 7.

Table 7. The Attributes of the <Realm> Element


Attribute Description
classname The fully qualified class name of the Realm
implementation.
driverName The name of the driver used to connect to the database
containing the users.
connectionURL The URL referencing the database containing the users.
connectionName The username to use when connecting to the database. If
you are using MySQL, you can encode the username
directly on the connectionURL.
connectionPassword The password to use when connecting to the database.
Again, if you are using MySQL, you can encode the
password directly on the connectionURL.
userTable The database table containing the user's information.
userNameCol The column in the userTable that references the user's
username.

userCredCol The column in the userTable that references the user's


password.

userRoleTable The database table containing the mapping between the


userTable and the table containing the possible user roles.

roleNameCol The column in the userRoleTable that contains a roles


given to a user

3. To complete this configuration change, stop and restart the Tomcat server.

That is all there is to it; your Web application is now protected by a JDBCRealm. To test
this change, try logging in to the /onjava Web application, entering a username from the

47
users table that has a role of onjavauser. You should see a dialog similar to Figure 1
above.

James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software


consultancy.

Deploying Web Applications to Tomcat


04/19/2001

In this article we are going to cover the deployment of web applications using Tomcat.
We are performing a manual deployment to fully explain the steps involved when
deploying a web application.

The best way to describe the deployment process is to create a web application of our
own that includes the important components found in most Java web applications; then
package it for deployment. The following sections will take you through the steps
involved in deploying a web application. The name of our web application will be
onjava.

In this article, we

• create the web application directory structure,


• create a web application ServletContext,
• add JSPs,
• add Servlets,
• add Tag Libraries, and
• create and deploy a WAR file.

Creating the Web Application Directory Structure

The first thing you need to do is create the directory structure that will contain the
application. We discussed this structure in Part 1, Java Web Applications, and I include
the relevant details in Table 1.

Table 1. The Web Application Directory Structure


Directory Contains
/onjava This is the root directory of the web
application. All JSP and XHTML files are
stored here.

48
/onjava/WEB-INF This directory contains all resources related
to the application that are not in the
document root of the application. This is
where your web application deployment
descriptor is located. Note that the WEB-
INF directory is not part of the public
document. No files contained in this
directory can be served directly to a client.
/onjava/WEB-INF/classes This directory is where servlet and utility
classes are located.
/onjava/WEB-INF/lib This directory contains Java Archive files
that the web application depends upon. For
example, this is where you would place a
JAR file that contained a JDBC driver.

The name of our web application, onjava, is the root of our directory structure.

While in development I suggest creating the directory directly in the Tomcat /webapps
directory. When it comes time for deployment, you can package your web application
into a WAR file and go though the production deployment process.

The last step in creating the web application directory structure is adding a deployment
descriptor. At this point you'll be creating a default web.xml file that contains only the
DTD, describing the web.xml file, and an empty <webapp/> element. Listing 1 contains
the source for a default web.xml file.

Listing 1 web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>
</web-app>

Now copy this file to the TOMCAT_HOME/onjava/WEB-INF/ directory, and we'll begin
adding web application components to it in the following sections.

Creating a Web Application ServletContext

After you've created the web application directory structure, you must add a new
ServletContext to Tomcat. The ServletContext defines a set of methods that are used
by components of a web application to communicate with the servlet container. The
ServletContext acts as a container for the web application. There is only one

49
ServletContext per web application. We will discuss the relationship between a
ServletContext and its web application in much more detail in Part 4, "Web
Applications and the ServletContext."

To add a new ServletContext to Tomcat you need to add the following entry to the
TOMCAT_HOME/conf/server.xml file, setting the values for the path and docBase to the
name of your web application. Notice again that the name we are using is onjava.

<Context path="/onjava" docBase="onjava" debug="0" reloadable="true" />

There are two things here we need to focus on. The first, path="/onjava", tells the
servlet container that all requests with /onjava appended to the server's URL belong to
the onjava web application. The second, docBase="onjava", tells the servlet container
that the web application exists in the /onjava directory.

Adding JSPs

Now that you have created the web application directories and added ServletContext,
you can add server-side Java components. The first components we are going to add are
JSPs.

The first JSP will include a simple login screen. Listing 2 contains the source for the
login.jsp page.

Listing 2 login.jsp

<html>
<head>
<title>OnJava Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-
1">
</head>

<body bgcolor="#FFFFFF" onLoad="document.loginForm.username.focus()">

<table width="500" border="0" cellspacing="0" cellpadding="0">


<tr>
<td> </td>
</tr>
<tr>
<td>
<img src="/onjava/images/monitor2.gif"></td>
</tr>
<tr>
<td> </td>
</tr>
</table>
<table width="500" border="0" cellspacing="0" cellpadding="0">
<tr>
<td>

50
<table width="500" border="0" cellspacing="0" cellpadding="0">
<form name="loginForm" method="post"
action="servlet/com.onjava.login">
<tr>
<td width="401"><div align="right">User Name: </div></td>
<td width="399"><input type="text" name="username"></td>
</tr>
<tr>
<td width="401"><div align="right">Password: </div></td>
<td width="399"><input type="password" name="password"></td>
</tr>
<tr>
<td width="401"> </td>
<td width="399"><br><input type="Submit" name="Submit"></td>
</tr>
</form>
</table>
</td>
</tr>
</table>
</body>
</html>

As you look at this JSP, you'll see nothing very special about it. The only thing you
should pay attention to is the action of the form. It references a servlet in the package
com.java named login. This servlet will retrieve the username-password parameters
from the request and perform its own processing.

There isn't much to deploying a JSP. You just copy it to the public directory of your web
application, which in this case is TOMCAT_HOME/webapps/onjava/. Any images that it
references should be placed in an images folder that you have created in the /onjava
directory.

To see this JSP in action, open the following URL in a browser:

http://localhost:8080/onjava/login.jsp

If you changed the default HTTP port, as mentioned in Installing and Configuring
Tomcat, you will need to request the URL from the appropriate port value. If everything
was configured correctly, you should see an image similar to Figure 1.

51
Figure 1. If everything is configured correctly, you'll see something like this.

If you do not see a page similar to this image, make sure you have the correct entry in the
server.xml file, as described in the section, "Creating a Web Application
ServletContext."

The second JSP is the target JSP referenced by the servlet defined in the following
section. This JSP will retrieve the request attribute USER that was added to the request
with the following servlet. It will then output the String value of the attribute. Listing 3
contains the source for the target JSP.

Listing 3 welcome.jsp

<html>
<head>
<title>OnJava Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-
1">
</head>

<table width="500" border="0" cellspacing="0" cellpadding="0">


<tr>
<td> </td>
</tr>
<tr>
<td>
<img src="/onjava/images/monitor2.gif"></td>
<td>
<b>Welcome : <%= request.getAttribute("USER")
%></b>

52
</td>
</tr>
<tr>
<td> </td>
</tr>
</table>
</body>
</html>

As we stated earlier to deploy this JSP, you simply need to copy it to the public directory
of your web application, which in this case is TOMCAT_HOME/webapps/onjava/.

Adding Servlets

The next component to add is a servlet. This servlet will be the action of login.jsp's
form. It will retrieve the username and password values from HttpServletRequest, look
up the associated user, and then forward the request to a target JSP. The source code for
this servlet can be found in Listing 4.

For our example the value of the USER is static. Normally you would perform a real
lookup of some sort, but, to keep things simple, I'm just returning String Bob.

Listing 4 com.onjava.login.java

package com.onjava;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class login extends HttpServlet {

private String target = "/welcome.jsp";

private String getUser(String username, String password) {

// Just return a static name


// If this was reality, we would perform a SQL lookup
return "Bob";
}

public void init(ServletConfig config)


throws ServletException {

super.init(config);
}

public void doGet(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, IOException {

// If it is a get request forward to doPost()

53
doPost(request, response);
}

public void doPost(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, IOException {

// Get the username from the request


String username = request.getParameter("username");
// Get the password from the request
String password = request.getParameter("password");

String user = getUser(username, password);

// Add the fake user to the request


request.setAttribute("USER", user);

// Forward the request to the target named


ServletContext context = getServletContext();

RequestDispatcher dispatcher =
context.getRequestDispatcher(target);
dispatcher.forward(request, response);
}

public void destroy() {


}
}

To deploy a servlet as part of a web application you first need to compile the servlet and
move it into the web application's /WEB-INF/classes directory. For this example, you
should compile this servlet and move it to the /onjava/WEB-INF/classes/com/onjava/
directory.

This class file is in the subdirectory com.onjava because of its package name.

The next step in deploying the login servlet is to add a servlet entry into the web
application's web.xml file. An example <servlet> element can be found in the following
code snippet.

It isn't necessary to add all servlets to the web.xml file; it's only necessary when the
servlet requires additional information, such as initialization parameters.

Example <servlet> Element

<servlet>
<servlet-name>ExampleServlet</servlet-name>
<servlet-class>packagename.ExampleServlet</servlet-class>
<init-param>
<param-name>parameter</param-name>
<param-value>value</param-value>
</init-param>

54
<load-on-startup>1</load-on-startup>
</servlet>

That's all there is to it. To see your web application in action, restart the Tomcat server
and open the following URL in your browser:

http://localhost:8080/onjava/login.jsp

You should see an image similar to Figure 1 (which was referred to above). Now enter a
username and password and press the "Submit Query" button. If everything went
according to plan, you should see an image similar to Figure 2.

Figure 2. Results after pressing "Submit Query".

If you didn't see an image similar to Figure 2, make sure that you have the servlet class in
the appropriate directory and the entry in the web.xml file matches the code snippet
referenced above.

Adding Tag Libraries

The final component that we're adding is a tag library. This library contains a single tag,
HelloTag, that replaces every occurrence of the text <onjava:hello/> with the literal
string "Hello". While this is a perfectly silly example of a tag library, it allows us to
present a practical example of deploying a tag library. The source code for the tag handler
can be found in Listing 5 and the source for the tld can be found in Listing 6.

Listing 5 HelloTag.java

package com.onjava;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.TagSupport;

55
public class HelloTag extends TagSupport
{
public void HelloTag() {

// Method called when the closing hello tag is encountered


public int doEndTag() throws JspException {

try {

// We use the pageContext to get a Writer


// We then print the text string Hello
pageContext.getOut().print("Hello");
}
catch (Exception e) {

throw new JspTagException(e.getMessage());


}
// We want to return SKIP_BODY because this Tag does not support
// a Tag Body
return SKIP_BODY;
}

public void release() {

// Call the parent's release to release any resources


// used by the parent tag.
// This is just good practice for when you start creating
// hierarchies of tags.
super.release();
}
}

Listing 6 taglib.tld

<?xml version="1.0" encoding="ISO-8859-1" ?>


<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

<!-- a tag library descriptor -->

<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>onjava</shortname>
<uri>/onjava</uri>

<tag>
<name>hello</name>
<tagclass>com.onjava.HelloTag</tagclass>
<bodycontent>empty</bodycontent>
<info>Just Says Hello</info>
</tag>
</taglib>

56
To deploy this tag library, we need to make an entry to the web.xml file. The modified
web.xml file can be found in Listing 7.

Listing 7 web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app PUBLIC


'-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN'
'http://java.sun.com/j2ee/dtds/web-app_2_3.dtd'>

<web-app>

<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.onjava.login</servlet-class>
</servlet>

<taglib>
<taglib-uri>/onjava</taglib-uri>
<taglib-location>/WEB-INF/lib/taglib.tld</taglib-location>
</taglib>

</web-app>

The added <taglib> entry contains two elements. The first <taglib> element, <taglib-
uri>, tells the container how the tag library is to be referenced. For this example we use
the value /onjava, which is the way we'll reference the tag library in our JSPs.

The second <taglib> element, <taglib-location>, defines the location of the tag
library's descriptor (TLD). The TLD defines the tags contained in the library and the
handlers that will process the defined tags.

To complete your deployment, copy the compiled tag library and the taglib.tld into the
TOMCAT_ROOT/onjava/WEB-INF/lib directory.

To test you new tag library, you need to modify the welcome.jsp page, replacing the
Welcome message with a reference to the <onjava:hello /> tag. You need to also add a
taglib directive referencing the taglib.tld to the welcome.jsp file. The modified JSP is
in Listing 7.

Listing 7 Modified welcome.jsp

<%@ taglib uri="/onjava" prefix="onjava" %>


<html>
<head>
<title>Onjava Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-
1">
</head>

57
<table width="500" border="0" cellspacing="0" cellpadding="0">
<tr>
<td> </td>
</tr>
<tr>
<td>
<img src="/onjava/images/monitor2.gif"></td>
<td>
<b><onjava:hello /> : <%= request.getAttribute("USER") %></b>
</td>
</tr>
<tr>
<td> </td>
</tr>
</table>
</body>
</html>

Now open the login.jsp page as described previously and run through the demo again.
This time instead of Welcome: Bob, you should see the message Hello: Bob.

Creating and Deploying a WAR File

When your web application is ready for deployment, you need to package it for
distribution. As we discussed previously in Java Web Applications, web applications are
packaged in WAR files. The steps required to "WAR-up" your /onjava web application
and deploy it are as follows:

1. Change to the root directory of your web application. In this case the root
directory would be TOMCAT_HOME/webapps/onjava/.
2. Archive the web application:

jar cvf onjava.war .

3. Copy the resulting WAR file, onjava.war, to the TOMCAT_HOME/webapps


directory.

If you're deploying this WAR file to the Tomcat installation that you were
developing in, then you will need to back up your development directory and
remove it from the TOMCAT_HOME/webapps directory.

4. Add a new Context entry to the /TOMCAT_HOME/conf/server.xml file,


referencing the onjava web application.
5. Restart Tomcat.

That's it. Your application should now be deployed and running. If it isn't, check your
entry in the TOMCAT_HOME/conf/server.xml file to ensure that you have set the
appropriate values.

58
Now that you know how to create and deploy a web applications, we'll examine the
relationship of the web application and its ServletContext in a future article.

James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software


consultancy.

Installing and Configuring Tomcat


03/29/2001

This article, in which we examine issues specific to Tomcat, is the second in our series on
the Jakarta-Tomcat server. In this article we will discuss

• the requirements for installing and configuring Tomcat,


• the process of installing and configuring Tomcat, and
• deploying Tomcat web applications.

Requirements for Installing and Configuring Tomcat

Before performing the tasks outlined by this article you will need to download the items
listed in the Table 1.

Table 1. Requirements

Name Location

Tomcat 4.0 beta 1 http://jakarta.apache.org/

JDK 1.3 Standard Edition

For this article we will be using the latest versions of the tools listed above.

Installing and Configuring Tomcat

In this article we will be installing Tomcat as a stand-alone server. This means that
Tomcat will service all requests, including static content, JSPs, and servlets.

To configure Tomcat as a stand-alone server you will need to download the Tomcat 4.0
beta 1 and the JDK 1.3 Standard Edition from the locations listed above. You should
choose the appropriate downloads based on your OS. We will be covering the steps
involved in installing to both NT/2000 and Linux.

Installing to Windows NT/2000


The first installation we will be performing is to Windows NT/2000. The first thing you

59
need to do is install the JDK, following its installation instructions. For this article I am
installing the JDK to drive D:, therefore my JAVA_HOME directory is D:\jdk1.3.

Now you need to extract the Tomcat server. Again, I am installing to drive D:, which will
make my TOMCAT_HOME directory D:\jakarta-tomcat-4.0-b1.

After you have extracted Tomcat, the next step is putting your JDK into Tomcat's
CLASSPATH and setting the TOMCAT_HOME environment variable. To do this under
NT/2000, you must

1. Open the NT/2000 Control Panel. You should see an image similar to Figure 1.

Figure 1. NT/2000 Control Panel


2. Start the NT/2000 System Application and select the Advanced tab. You should
see a screen similar to Figure 2.

60
Figure 2. NT/2000 System Application
3. Select the Environment Variables button. You will see a screen similar to Figure 3.

61
Figure 3. Environment Variables Dialog
4. Select the New button on the System Variables section of the Environment
Variables dialog. Add a JAVA_HOME variable and set its value to the location of
your JDK installation. Figure 4 shows the settings associated with my installation.

Figure 4. JAVA_HOME Environment Settings


5. Repeat Step 4 using TOMCAT_HOME for the variable name and the location of your
Tomcat installation as the value. For my installation I am setting the value to
D:\jakarta-tomcat-4.0-b1.

That's all there is to it. You should skip the following section "Installing to Linux" and
move on to "Testing You Tomcat Installation."

Installing to Linux
The installation is much simpler on a Linux than a Windows machine. The first thing you

62
need to do is install the JDK. For our purposes, we will assume that the JDK will be
installed to /bob/java.

After you have the JDK installed, you need to set the JAVA_HOME environment variable.
To do this under Linux, find your shell in Table 2 and type the matching command. You
will need to replace /bob/java with the root location of your JDK installation.

Table 2. Java Environment Settings


Shell JAVA_HOME
bash JAVA_HOME=/bob/java
tcsh setenv JAVA_HOME /bob/java

You should add the location of the Java interpreter to your PATH environment variable.

You now need to extract the Tomcat server to a directory of your choosing. This directory
will become the TOMCAT_HOME directory. For this installation, we will assume that Tomcat
will be installed to /bob/jakarta-tomcat-4.0-b1.

The last step is to set the TOMCAT_HOME environment variable. To do this under Linux,
find your shell in Table 3 and type the matching command. You will need to replace /bob
with the name of the directory located directly above your Tomcat installation.

Table 3. Tomcat Environment Settings


Shell TOMCAT_HOME
TOMCAT_HOME=/bob/jakarta-tomcat-4.0-b1
bash export TOMCAT_HOME

tcsh setenv TOMCAT_HOME /bob/jakarta-tomcat-4.0-b1

Testing Your Tomcat Installation

To test the Tomcat installation, first start the Tomcat server. Table 4 contains the startup
and shutdown commands for each OS.

Table 4. Tomcat Startup/Shutdown Commands


OS Startup Shutdown
Windows TOMCAT_HOME\bin\startup.bat TOMCAT_HOME\bin\shutdown.bat
NT/2000
Linux TOMCAT_HOME/bin/startup.sh TOMCAT_HOME/bin/shutdown.sh

63
Once Tomcat has started, point your browser at

http://localhost:8080/

You should see a page similar to Figure 5.

Figure 5. The Tomcat Default Page

If you would like to have all requests serviced on the default HTTP port of 80, instead of
port 8080, you will need to make the following change to the
TOMCAT_HOME/conf/server.xml file and restart Tomcat.

Change

<!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->


<Connector className="org.apache.catalina.connector.http.HttpConnector"
port="8080" minProcessors="5" maxProcessors="75"
acceptCount="10" debug="0"/>

to

<!-- Define a non-SSL HTTP/1.1 Connector on port 80 -->


<Connector className="org.apache.catalina.connector.http.HttpConnector"
port="80" minProcessors="5" maxProcessors="75"
acceptCount="10" debug="0"/>

Now point your browser at

64
http://localhost/

and you'll see results similar to those in Figure 5.

The next step is to verify the installation of your JDK, which is done by executing one of
the JSP examples provided with the Tomcat server. At the page shown in Figure 5, choose
JSP Examples. You should see a page similar to Figure 6.

Figure 6. The JSP Examples Page

Now choose the JSP example Date and select the Execute link. If everything was
installed properly you should see a page similar to Figure 7 (of course with a different
date).

65
Figure 7 The JSP Date Page

If you do not see the previous page, then you need to make sure that the location of your
JAVA_HOME environment variable matches the location of your JDK installation.

Deploying Web Applications to Tomcat

Once Tomcat is installed and running, let's look at the steps necessary to deploy a web
application. To deploy a web app, we need to examine the directory structure of Tomcat.
Table 5 describes the directories that make up a Tomcat installation. It is assumed that the
value of TOMCAT_HOME precedes each of these directories.

And because we are using a beta release of Tomcat, these directories could change
without notice.

Table 5. The Tomcat Directory Structure

/bin This directory contains the startup and shutdown scripts for both Windows and
Linux.

This directory contains the main configuration files for Tomcat. The two most
/conf
important are the server.xml and the global web.xml.
/server This directory contains the Tomcat Java Archive files.

66
/lib This directory contains Java Archive files that Tomcat is dependent upon.
/logs This directory contains Tomcat's log files.
This directory contains the source code used by the Tomcat server. Once
/src
Tomcat is released, it will probably contain interfaces and abstract classes only.
/webapps All web applications are deployed in this directory; it contains the WAR file.
This is the directory in which Tomcat will place all servlets that are generated
/work from JSPs. If you want to see exactly how a particular JSP is interpreted, look
in this directory.

We will examine most of these directories in future articles. For the remainder of this
article we're interested in the /webapps directory, which is where all of our WAR files
will be deployed.

In our last article we described the contents of a web application and how they are
packaged. Once you have a WAR file, containing your web application, deploying web
applications to Tomcat is a simple two-step process.

Steps Involved in Deploying a Web Application to Tomcat

1. Copy your WAR file to the TOMCAT_HOME/webapps directory.


2. Add a new Context entry to the TOMCAT_HOME/conf/server.xml file, setting the
values for the path and docBase to the name of your web application.

<Context path="/onjava" docBase="onjava" debug="0"


reloadable="true" />

Restart Tomcat after completing these steps. Your application should now be running.

The previously described application can be accessed by pointing your browser at

http://localhost/onjava/

If you look at the TOMCAT_HOME/webapps directory, you will see a new directory
matching the name of your WAR file. This is where your working web application now
exists. When Tomcat starts it will extract all WAR files that have been recently placed
into the TOMCAT_HOME/webapps directory.

In the next article we will learn how to add Servlets, JSPs, and custom tag libraries to a
web application. We will also discuss the relationship between a web application and its
ServletContext.

James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software


consultancy.

67
How to Publish Multiple Websites Using a Single
Tomcat Web Application
by Satya Komatineni
08/30/2006

Knowledge Folders is a web application that holds and displays content for multiple
users. I had been wondering if I could expose the content from this single web application
as multiple websites with their own domain names. Could I use virtual hosts to do this?
Or would I need to use reverse proxies? How and where would I register domain names?
What entries would I need to make in Tomcat configuration files? How would I handle
emails for these independent domains? What else would I need to do in my web
application? What would the end result look like?

After a few weeks of effort, I was able to expose Knowledge

Folders as multiple websites with their own domain names. It turned out I didn't need to
go to reverse proxies for now, and could use virtual hosts instead. I was able to get my
multiple domain names from GoDaddy.com. I was also able to use Tomcat host/alias
settings to effectively route traffic from all of these domains to the same web app. Using
the index.jsp of the web app, I was able to separate the content between different
domains. After all of this effort, I ended up with a way to publish online websites very
quickly and expose them as their own domains. The resulting websites have a number of
features that static websites can't accomplish easily.

Background

I wrote Knowledge Folders a few years ago as a workaround for keeping my notes
online. I used to keep these "rapid notes" using Microsoft Outlook and a few macros. In
particular, the ability to file these notes into classified folders appealed to me. When I
took that application to the web, it was natural for me to make it a multiuser system that
allowed a number of users to manage their own notes and perhaps share them as well.

Originally, these notes were various SQL scripts that ran on a database. The initial release
even had an execution engine to run these notes against a target database and return the
results. I abandoned this later to focus on the transformation to come.

At about the same time, I was in search of something to document my open source tool
Aspire/J2EE. I was looking more along the lines of wikis and weblogs. Unhappy with
what I found, I changed Knowledge Folders to focus on documenting open source
software. At this point, Knowledge Folders was basically a collection of accounts (or
users), files, and folders in which knowledge was created and classified (hence the name).

Later that year, I introduced the idea of master pages (background HTML, similar to
tiles) to give a facelift and proper presentation to the content. This took Knowledge

68
Folders toward a content-management system, where content could be portrayed with
appropriate backgrounds.

Later, I added some collaboration features and task management for individual users.

During this time, there was a single domain through which users accessed their accounts.
Although not difficult, it was awkward to pass the account URLs for individual users to
their friends or any intended audience. I wanted to expose each user as his/her own
domain.

My Original Thought Involving Reverse Proxy Servers

Initially, the problem sounded like a case where I could have the individual domains
pointing to a "piece of code" on the server that would in turn read the content from a
single web app. This intermediate piece of code would somehow associate the incoming
domain name to an account in Knowledge Folders and use Knowledge Folders as a
source/sink to read/write web pages. In essence, it would be working like a proxy to the
actual Knowledge Folders.

Wikipedia's definition of Reverse Proxy implied it could be used for this purpose. (In
fact, this may turn out to be a good solution in the future if I were to segregate content
further. Perhaps the document Reverse Proxy Patterns PDF by Peter Sommerlad [PDF,
328 KB] might throw some light on the possibilities.) I also hoped to use the reverse
proxy facility of Apache to accomplish my goal.

Although I have mentioned the links on reverse proxies here for further research, I will
mention the key elements briefly.

What Are Reverse Proxies?

Reverse proxies are web servers that stay in front of other web servers, possibly internal
to a corporate network. This indirection is useful in a number of situations. These proxy
servers typically read or intercept communications from a browser and rewrite to the
back-end servers. Users are usually exposed only to the domain names of these reverse
proxy servers and not to the back-end servers. The reverse proxy servers will, in turn, call
some internal servers to fulfill the request. They typically break the incoming IP pipe and
open a separate pipe to the target servers. As a result, implementing a proxy server
reliably is not easy because it must behave like a genuine target server, while also truly
intercepting all of the data and HTTP headers.

What Are Reverse Proxies Used For?

Reverse proxies are routinely used to offload SSL certificates. In this scenario, https
traffic is routed to a reverse proxy server. The reverse proxy server converts the traffic
from https to http and then forwards that request to an HTTP internal server. In this
approach, a single reverse-proxy server can be used to offload SSL (and hence save

69
certificates) to multiple back-end servers. Nevertheless, sometimes this approach poses
issues for sendRedirect on the target server. When sendRedirect is used, sometimes a
relative URL is translated into an absolute URL using the wrong scheme (http versus
https). Fortunately, this can be resolved by rewriting SendRedirect.

Reverse proxies can also be used to expose a single domain for multiple web applications
on the back end. Each separate server can be mapped to a path based on the main domain.
There are also approaches that provide role-based security using Proxy server
gatekeepers by monitoring every URL.

Implications To Web Application Development in the Face of Reverse


Proxies

It is imperative for all of the URLs to be relative for reverse proxies to work well. This is
because the reverse proxy is rewriting the page using a different (and typically external)
name. Internal names are unknown to the outside world. So, URLs on your web pages
delivered by back-end servers should typically read:

/webapp/resource1.html

What Are Virtual Hosts?

Although a solution involving reverse proxies seemed possible, I found out that the
hosting facility at Indent that I use hosts my web app on Tomcat, not Apache. After some
initial research, I couldn't figure out whether Tomcat supported reverse proxies, so my
exploration led me to virtual hosts--maybe they could solve the problem.

A virtual host allows multiple domain names for a given IP address. In other words, a
given IP address can have any number of host names. When requests are received on
behalf of these host names, a web server can decide to deliver content from different root
directories, or different web apps in the case of Tomcat.

For example, you could have an arrangement where

• www.host1.com points to /webapp1


• www.host2.com points to /webapp1
• www.host3.com points to /webapp2

In Tomcat, the host names and web apps are bound in a many-to-many relationship.
There will be one host entry for each host. When multiple host names are bound to the
same web app, one can use Tomcat's aliases facility.

Examples of Virtual Hosts in Tomcat

Based on this, here is a sample setup for Knowledge Folders:

70
<Host name="www.knowledgefolders.com"
appBase="D:/webpage_demos/akc"
unpackWARs="true"
autoDeploy="true"
xmlValidation="false"
xmlNamespaceAware="false">

<Alias>knowledgefolders.com</Alias>

<Alias>www.knowledgefolders.net</Alias>
<Alias>knowledgefolders.net</Alias>

<Alias>www.knowledgefolders.org</Alias>
<Alias>knowledgefolders.org</Alias>

<Alias>www.satyakomatineni.com</Alias>
<Alias>www.kavithakomatineni.com</Alias>

<Context path="" docBase="D:/webpage_demos/akc"


debug="0" reloadable="false"/>
<Context path="/akc" docBase="D:/webpage_demos/akc"
debug="0" reloadable="false"/>
</Host>

Notice how all of the following host names point to the same web app, akc (which was
the previous name for Knowledge Folders).

• knowledgefolders.com
• www.knowledgefolders.com
• knowledgefolders.net
• www.knowledgefolders.net
• knowledgefolders.org
• www.knowledgefolders.org
• www.satyakomatineni.com
• www.kavithakomatineni.com

Registering A Domain Name

Originally, Knowledge Folders was hosted on a static IP at Indent, Inc. With potential
changes to internal IP, I decided to get a proper domain address for Knowledge Folders.

I went to GoDaddy.com on the advice of a friend. I found it had excellent support and its
prices seemed very cheap. I registered three domains in the process:

knowledgefolders.org
knowledgefolders.net
knowledgefolders.com

Registering these domains was quite simple at GoDaddy, but setting up the rest took
some work. Knowledge Folders was physically hosted with Indent at Peak 10, a hosting

71
facility, on a dedicated Windows server, whereas the domain names were registered at
GoDaddy.

Securing The Name Servers and Setting Up IP Address Association

To make the domains work, the first thing I needed to know from Peak 10 was the name
servers that would be used to resolve the host names. I needed two name servers. For
instance, the name servers for Peak 10 are:

NS1.JAX.PEAK-10.COM
NS1.CLT.PEAK-10.COM

The next step was to tell the Peak 10 staff the domain names I'd registered and the
physical IP address the host names should be pointing to. With these changes, I was able
to access Knowledge Folders with all of the domain names.

Changes to Knowledge Folders

Thus, I was able to take multiple domain names and point them to the same web app on a
given physical IP. So for instance, when I accessed http://www.satyakomatineni.com, I
was taken to the home page of Knowledge Folders.

But my intention was to go to the homepage of the account identified by the userid of
satya. This required changing two things in Knowledge Folders:

• index.jsp
• some new definitions in the properties files

The general idea was to have the index.jsp identify the incoming host name and, provided
that there was a way to associate the domain name to an account, the index.jsp would
transfer control to the home page of that account.

Example index.jsp and Properties File

The source code of index.jsp that accomplishes this is as follows

<!--
*************************************************************
* Sample code for knowing the Knowledge Folders url:
* Standard aspire libraries
*************************************************************
-->
<%@ page import="com.ai.htmlgen.*" %>
<%@ page import="com.ai.application.utils.*" %>
<%@ page import="com.ai.common.*" %>

<!--
*************************************************************
* html header

72
*************************************************************
-->
<html><head>
<title>Welcome to Aspire Knowledge Center</title>
<link rel="stylesheet" type="text/css" href="/akc/style/style.css">
<script src="/akc/js/genericedits1.js"></script>

<!--
*************************************************************
* Figure out home page,
* if not found use the main home page of Knowledge Folders
*************************************************************
-->
<%
String hostname = request.getServerName();
String homepageurl = AppObjects.getValue("aspire.multiweb."
+ hostname + ".homepageurl",null);
String targeturl = "";
if (homepageurl == null)
{
targeturl = "/akc/akchome.html";
}
else
{
//hostuserid exists
targeturl = homepageurl;
}
String debug = request.getParameter("debug");
%>
<script>

<!--
*************************************************************
* gotoHomePage() on load
*************************************************************
-->
function gotoHomePage()
{
debugAlert("gethost on the client side:" + getHost());
debugAlert("<%=hostname%>:<%=homepageurl%>");
var targeturl = "<%=targeturl%>";
debugAlert(targeturl);
document.location.replace(targeturl);

}
<!--
*************************************************************
* some debugging support
*************************************************************
-->
function debugAlert(message)
{
var debug = "<%=debug%>";
if (debug == "true")
{
alert(message);
}

73
}
</script>
</head>
<!--
*************************************************************
* onload
*************************************************************
-->
<body onload="gotoHomePage()">
</body></html>

Here is the Aspire/J2EE configuration file to support the "domain name to account"
translation or mapping:

aspire.multiweb.www.satyakomatineni.com.userid=satya
aspire.multiweb.www.satyakomatineni.com.homepageurl=\
/akc/update?request_name=GotoHomepageURL&ownerUserId=satya

Summary of Setup Procedures for Creating A New Website in


Knowledge Folders

1. Register a domain with a domain name registrar (such as GoDaddy.com).


2. Provide name servers for the domain.
3. Associate/inform IP address with name servers via an email to the hosting
providers.
4. Add an alias to Tomcat server.xml under the host corresponding to the web app.
5. Make changes to the aspire configuration to tie the domain name to an individual
account.
6. Optionally set up an email account for the domain.
7. Write down the passwords for all of the accounts you have set up.

The Email Option

As it exists today, Knowledge Folders is quite flexible and convenient to create websites
without any additional tools. This is a great advantage for small companies that want to
have a web presence quickly without having to buy any hosting space. Nevertheless,
these small companies also usually want a basic email address at the domain so that they
can use it on business cards or as a general advertisement. This can be done in two ways.

Setting It Up with Indent

You see, there are three players in this solution. The domains are registered at GoDaddy.
The Windows server on which the software runs is sitting on the Peak 10 network, which
requires that I use their name servers. The actual Windows server is owned and operated
by Indent.

74
So the first option involves alerting Indent to create email accounts. Indent uses the
James mail server. Indent usually uses a manual process to either create a full-fledged
email account or provide email forwarding for that account.

Using GoDaddy's Email Accounts

Registering a domain at GoDaddy generates a free email account for each registered
domain. You can also purchase additional email accounts if needed. GoDaddy also offers
email forwarding and online tools to manage these email accounts.

But it is tricky to use these email accounts at GoDaddy if the original mail server for your
domain is at a hosting facility. You have to set up MX records and CNAME records at the
mail server to accomplish this.

GoDaddy recommends adding the following to the domain-name system manager:

MX 0 - smtp.secureserver.net
MX 10 - mailstore1.secureserver.net

What Are MX Records and How Do They Work?

According to an MX FAQ, a mail-sender program checks the domain-name system to see


if the server has an MX record pointing to another mail server. If it does, then it will use
that server as the target server. It may even be recursive. This is one way to redirect the
mail. This is how the MX records at Peak 10 will reroute the mail to GoDaddy, meaning I
could use the email accounts at GoDaddy. It is sufficient to set the MX records only for
the root domain name and not the CNAMES. For instance, to set up MX records for
www.knowledgefolders.com, it is sufficient to set them for "knowledgefolders.com",
because the email is going to be addressed to somemail@knowledgefolders.com.

Adding CNAMES To Fine-Tune The Email Solution

According to another CNAME document, CNAME records are aliases at the domain-
name server (in this case, Peak 10) redirecting the traffic. For example, I can set up a
CNAME record at Peak 10 for pop.knowledgefolders.com pointing to pop.godaddy.com.
This will allow Outlook to specify the pop-name server as your domain-name server.
Something similar can be done for smtp CNAME, and for the webmail at GoDaddy if
needed.

Limitations of CNAMES

CNAMES are aliases to host names. They also introduce new names into the domain-
name space. For example, if I have a domain registered as knowledgefolders.com, then a
CNAME record can introduce another host into the domain-name space called
myhost.knowledgefolders.com. This only works as long as the new host names you are
introducing are all suffixed with knowledgefolders.com. For example, you can not

75
introduce a CNAME called somehost.some-domain.com when you don't own some-
domain.com.

Nevertheless, you are entitled to point somehost.your-domain.com to some-other-


host.someoneelsesdomain.com, which is how the indirection of SMTP and POP mail
servers is achieved.

End Result

At the end of all of this, I was able to publish the following distinct websites using
various accounts in Knowledge Folders. Let's take a look:

1. www.knowledgefolders.com points to the original home page of multi-account


Knowledge Folders.
2. www.knowledgefolders.org points to the documentation website for Knowledge
Folders.
3. www.knowledgefolders.net points to my personal account in Knowledge Folders.
4. www.satyakomatineni.com points to my personal account in Knowledge Folders,
as well.
5. www.kavithakomatineni.com points to the website I manage for my daughter.

I use Knowledge Folders for a number of things:

1. I manage my weblogs.
2. I manage documentation for Aspire/J2EE.
3. I support Aspire/J2EE using feedback.
4. I manage documentation for Knowledge Folders.
5. I collaborate with teams to develop websites using a project portal concept where
project documentation is maintained.
6. I create static websites for small companies.
7. I manage my daily, weekly, and monthly tasks and to-do lists.
8. I run tutorials.
9. I conduct my research.
10. I publish articles.

Future Possibilities for Web Hosting

Currently the process of creating websites is very disjointed; one must follow numerous
steps to get a web presence today, but the trend is certainly toward simplification.
Especially with something like Knowledge Folders, it is possible to imagine a time when
consumers could visit a site, create an account, and post their content on the Web right
away. The back-end details can be automated. By managing our tasks and schedules,
publishing, and collaborating from the same site, we're heading toward something like a
"web OS."

76
Satya Komatineni is the CTO at Indent, Inc. and the author of Aspire, an open source
web development RAD tool for J2EE/XML.

Database Connection Pooling with Tomcat


by Kunal Jaggi
04/19/2006

Software object pooling is not a new concept. There are many scenarios where some type
of object pooling technique is employed to improve application performance,
concurrency, and scalability. After all, having your database code create a new
Connection object on every client request is an expensive process. Moreover, with
today's demanding applications, creating new connections for data access from scratch,
maintaining them, and tearing down the open connection can lead to massive load on the
server.

Connection pooling eliminates JDBC overhead. Further, object pooling also helps to
reduce the garbage collection load. In this article, we'll look at an elegant way of creating
a pool of open database-connection objects in Tomcat, so that they are handy whenever
an application needs to access a DB resource.

With Database Connection Pooling (DBCP), we can scale our applications to handle
increased load and deliver high performance benefits. Using recycled database
connection objects cuts the time taken to re-instantiate and load frequently used objects,
thus reducing unnecessary overheads. Configuring a DB pool can be a daunting task,
because there has to be a way for different components within an application to know
about the available pooled objects, and a mechanism to locate them. This is exactly where
JNDI steps in, tying these dependencies together.

JNDI to the Rescue

The Java Naming and Directory Interface (JNDI) has been at the core of Java EE since its
inception. JNDI offers a generic mechanism for Java EE components to find other
components, resources, or services indirectly at runtime. The primary role of JNDI in a
Java EE application is to provide an indirection layer, so that components can find
required resources without being particularly aware of the indirection. This indirection is
almost transparent. JNDI helps in holding applications together, but this coupling is very
flexible, so that apps can be easily reassembled. JNDI spares you from providing direct
references to the data source, JDBC driver class names, user names and passwords, or
any vendor-specific quirks of setting up pooling. We just look up all of these
dependencies at runtime through a JNDI call. The developer, on the other hand, is
ignorant of the external resources.

77
Tomcat Configuration

Our approach to DBCP uses the Jakarta-Commons database connection pool. But first,
we need to configure the JNDI DataSource in Tomcat by adding a declaration for the
resource to server.xml file, which resides inside the /conf directory of your Tomcat
installation (indicated by the environment variable CATALINA_HOME). The JNDI
DataSource is used as a factory for connections. One of the major advantages of using a
configuration like this is that the characteristics of the pool can be changed without
affecting the application code. Our application's use of connection pooling is almost
transparent. The following code snippet shows us how to configure the container to
enable connection pooling.

<Context path="/dbcp" docBase="dbcp" debug="5"


reloadable="true" crossContext="true">

<Resource name="jdbc/TestDB" auth="Container"


type="javax.sql.DataSource" removeAbandoned="true"
removeAbandonedTimeout="30" maxActive="100"
maxIdle="30" maxWait="10000" username="kunal"
password="java_facier"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost/dbcptest"/>

</Context>

We can configure a maximum number of DB connections in the pool. Make sure you
choose a maximum connection count large enough to handle all of your database
connections--alternatively, you can set 0 for no limit. Further, we can set the maximum
number of idle database connections to be retained in the pool. Set this value to -1 for no
limit. The most optimal performance is attained when the pool in its steady state contains
just enough connections to service all concurrent connection requests, without having to
create new physical database connections at runtime. We can also specify the maximum
time (in milliseconds) to wait for a database connection to become available, which in
this example is 10 seconds. An exception is thrown if this timeout is exceeded. You can
set this value to -1 to wait indefinitely. Please make sure your connector driver, such as
mysql.jar, is placed inside the /common/lib directory of your Tomcat installation.

To achieve performance and high throughput, we also need to fine-tune the container to
work under heavy traffic. Here's how we'll configure the Connector element for the
maxProcessors and acceptCount parameters in the server.xml file:

<!-- Configuring the request and response endpoints -->


<Connector port="80" maxHttpHeaderSize="8192" maxProcessors="150"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="150"
connectionTimeout="20000" disableUploadTimeout="true" />

78
Configuring JNDI Reference

In order for JNDI to resolve the reference, we have to insert a <resource-ref> tag into
the web.xml deployment descriptor file. We first begin by setting a <listener> tag for
registering a ServletContextListener as shown below:

<listener>
<listener-class> com.onjava.dbcp.DBCPoolingListener</listener-
class>
</listener>

<!-- This component has a dependency on an external resource-->


<resource-ref>
<description> DB Connection Pooling</description>
<res-ref-name> jdbc/TestDB</res-ref-name>
<res-type> javax.sql.DataSource</res-type>
<res-auth> Container</res-auth>
</resource-ref>

<servlet>
<servlet-name> EnrolledStudents</servlet-name>
<servlet-class>
com.onjava.dbcp.CourseEnrollmentServlet</servlet-class>
<load-on-startup> 1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name> EnrolledStudents</servlet-name>
<url-pattern> /enrollment.do</url-pattern>
</servlet-mapping>

This binding is vendor-specific, and every container has its own mechanism for setting
data sources. Please note that this is just a declaration for dependency on an external
resource, and doesn't create the actual resource. Comprehending the tags is pretty
straightforward: this indicates to the container that the local reference name jdbc/TestDB
should be set by the app deployer, and this should match with the resource name, as
declared in server.xml file.

Putting DBCP into Action

As our application first starts asking the pool for Connection objects, they will be newly
created, but when the application has finished with an object, it's returned to the pool
rather than destroyed. This has huge performance benefits. Now, as the application needs
more Connection objects, the pool will be able to issue recycled objects that have
previously been returned by the application.

79
As an example, let's create a listener class to work with the pool. Our listener class
implements the ServletContextListener interface; thus, it'll be initialized when the
container starts and creates a ServletContext for this web app. Remember, there's only
one ServletContext per web app. Any class implementing the
ServletContextListener interface is initialized when the container starts. This early
initialization cuts unnecessary overhead later, since it's ideal to have a cached set of open
database connection objects available when the container starts rather than waiting for a
client request. Inside the listener class, we'll do the necessary JNDI lookup and then set
the DataSource as a ServletContext attribute so that it's available to the entire web
app. The following code snippet shows us how to extract DataSource through a JNDI
call:

public class DBCPoolingListener implements


ServletContextListener{
public void contextInitialized
(ServletContextEvent sce){

try {
// Obtain our environment naming context
Context envCtx = (Context) new InitialContext().
lookup("java:comp/env");

// Look up our data source


DataSource ds = (DataSource) envCtx.lookup
("jdbc/TestDB");

sce.getServletContext().setAttribute
("DBCPool", ds);
} catch(NamingException e){ e.printStackTrace();
}
}
public void contextDestroyed(ServletContextEvent
sce){
}
}

The component naming context is indicated by the prefix java:comp/env/.

For the sake of simplicity, we'll create a simple servlet, hard-coding the business logic
and presentation. We'll use the JDBC 2.0 Standard Extension API, which specifies that a
database service provider can implement a pooling technique that can allow multiple
Connection objects to be shared among several requesting clients. Here's how we'll
extract DataSource from the ServletContext attribute and then establish a Connection
to pooled DB connection objects.

public void init() throws ServletException {


try {
//Create a datasource for pooled connections.
datasource = (DataSource) getServletContext().
getAttribute("DBCPool");

80
//Register the driver for non-pooled connections.
Class.forName("com.mysql.jdbc.Driver").
newInstance();
}
catch (Exception e) {
throw new ServletException(e.getMessage());
}
}

The servlet is written to use either pooled or non-pooled database connections, depending
on the query string passed in its URL. The servlet fetches a pooled connection object
using Tomcat DBCP, and non-pooled connections directly from MySQL connector.

Here's an example of obtaining a Connection object. If the pooledConnection flag is


set, it simply calls getConnection() on the DataSource. If not, it manually creates a
new Connection object:

private synchronized Connection getConnection


(boolean pooledConnection)
throws SQLException {
if (pooledConnection) {
pooledCount++;

// Allocate and use a connection from the pool


return datasource.getConnection();
}
else {

nonPooledCount++;
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost/dbcptest","kunal",
"java_facier");
return con; //return a newly created object
}
}

Having acquired a Connection, the servlet executes a simple join between the course and
enrollment tables, and then formats and outputs the results as HTML. The example uses
PreparedStatement to pre-compile SQL and run it repeatedly. This eliminates the
tedious task of parsing and compiling the SQL query on every client request. Pre-
compilation improves performance and offers enhanced security by preventing SQL
injection attacks. For thread safety, we'll keep Connection, PreparedStatement, and
ResultSet as local variables inside of the doGet() method.

Connections issued from the JNDI DataSource factory will be returned to the pool when
closed. Clients use a connection pool by borrowing a connection object, using it, and then
returning it to the pool by closing it. We have to make sure that after we are done with the
Connection, we close it. If a Connection is not closed, it will never be returned to the
pool and become available for reuse. Of course, that would tie up resources. The finally
block guarantees that used ResultSet, PreparedStatement, and Connection objects
are closed and prevents any connection pool leaks, as shown below:

81
finally {
try {if (rs != null) rs.close();} catch (SQLException e) {}
try {if (pstmt != null) pstmt.close();} catch (SQLException e) {}
try {if (connection != null) connection.close();} catch
(SQLException e) {}
}

Performance Measurement

Before our application hits the ground running, we would like to stress test the app,
evaluate its performance, and compare the results between the cached set of pooled
connection objects and the non-pooling alternative. For this, we'll rely on JMeter, an open
source tool for load testing with a drag-and-drop-style GUI. I have written a test plan for
stress testing the web app. I have set up JMeter to stimulate 50 concurrent users,
accessing a common servlet two times without any interval. The results are pretty
apparent after looking at the JMeter graph results shown in Figures 1 and 2, below.

Figure 1. Pooled DB connections deviation (click for full-size image)

82
Figure 2. Non-pooled DB connections deviation (click for full-size image)

After several test runs, it turned out that connection pooling is at least four times faster
than explicitly creating a DB connection object from the ground up. Admittedly, to get
more accurate results, JMeter should run on a different machine. The ramp-up period,
which describes the amount of time for creating the total number of threads, should be
carefully chosen. It's not considered to be a good idea to set it to zero if you have a large
number of threads, because that would create all of the threads at once and send out
requests immediately. At the same time, a higher ramp-up period is also not appropriate,
as it might underestimate the results.

Conclusion

Connection pooling is a technique used for sharing a cached set of open database
connections among several requesting clients. It doesn't require you to modify your code
significantly; rather, it provides enhanced performance benefits. Object pooling should be
used with care. It does require additional overhead for such tasks as managing the state of
the object pool, issuing objects to the application, and recycling used objects. Pooling is
best suited for objects that have a short lifetime. If you are already working in a rich Java
EE environment, then most likely you would be using an out-of-box connection pooling
facility provided by your app server, and your applications' use of connection pooling is
almost transparent.

Resources

• Example source code used in this article


• Jakarta-Commons home
• Sun's JNDI tutorial

83
• Tomcat JNDI DataSource how-to document
• MySQL's official JDBC driver
• JDBC 2.0 tutorial
• Apache JMeter
• Creating a JMeter test plan

Kunal Jaggi is an independent Java consultant, primarily focused on enterprise solutions


with Java-based technologies.

Top Ten Tomcat Configuration Tips


by Jason Brittain and Ian F. Darwin, authors of Tomcat: The Definitive Guide

06/25/2003

Coauthor's note: Now that writing Java web applications has become a common way to
create and deploy new web content, people around the globe are finding the Jakarta
Tomcat servlet and JSP container useful. It's free, it's multiplatform, it's rich in features,
it's rapidly evolving and improving, and it's never been more popular.

The only catch seems to be this: how can you configure Tomcat to do what you want it to
do? Tomcat is capable, as long as you can configure it to suit your needs. Below is my
list of ten Tomcat configuration tips, taken from Tomcat: The Definitive Guide, to help
you do just that. -- Jason Brittain

1. Configuring the Admin Web Application

Most commercial J2EE servers provide a fully functional administrative interface, and
many of these are accessible as web applications. The Tomcat Admin application is on its
way to becoming a full-blown Tomcat administration tool rivaling these commercial
offerings. First included in Tomcat 4.1, Admin already provides control over contexts,
data sources, and users and groups. You can also control resources such as initialization
parameters, as well as users, groups, and roles in a variety of user databases. The list of
capabilities will be expanded upon in future releases, but the present implementation has
proven itself to be quite useful.

The Admin web application is defined in the auto-deployment file


CATALINA_BASE/webapps/admin.xml.

You must edit this file to ensure that the path specified in the docBase attribute of the
Context element is absolute; that is, the absolute path of
CATALINA_HOME/server/webapps/admin. Alternatively, you could just remove the
auto-deployment file and specify the Admin context manually in your server.xml file. On

84
machines that will not be managed by this application, you should probably disable it
altogether by simply removing CATALINA_BASE/webapps/admin.xml.

If you're using a UserDatabaseRealm (the default), you'll need to add a user and a role to
the CATALINA_BASE/conf/tomcat-users.xml file. For now, just edit this file, and add a
role named "admin" to your users database:

<role name="admin"/>

You must also have a user who is assigned to the "admin" role. Add a user line like this
after the existing user entries (changing the password to something a bit more secure):

<user name="admin" password="deep_dark_secret" roles="admin"/>

Once you've performed these steps and restarted Tomcat, visit the URL
http://localhost:8080/admin, and you should see a login screen. The Admin application is
built using container-managed security and the Jakarta Struts framework. Once you have
logged in as a user assigned to the admin role, you will be able to use the Admin
application to configure Tomcat.

2. Configuring the Manager Web Application

The Manager web application lets you perform simple management tasks on your web
applications through a more simplified web user interface than that of the Admin web
app.

The Manager web application is defined in the auto-deployment file


CATALINA_BASE/webapps/manager.xml.

You must edit this file to ensure that the path specified in the docBase attribute of the
Context element is absolute; that is, the absolute path of
CATALINA_HOME/server/webapps/manager.

If you're using the default UserDatabaseRealm, you'll need to add a user and role to the
CATALINA_BASE/conf/tomcat-users.xml file. For now, just edit this file, and add a role
named "manager" to your users database:

<role name="manager"/>

You must also have a user who is assigned the "manager" role. Add a user line like this
after the existing user entries (changing the password to something a bit more secure):

<user name="manager" password="deep_dark_secret" roles="manager"/>

Then restart Tomcat and visit the URL http://localhost/manager/list to see the plain-text
manager interface, or http://localhost/manager/html/list for the simple HTML manager
interface. Either way, your Manager application should now be working.

85
The Manager application lets you install new web applications on a non-persistent basis,
for testing. If we have a web application in /home/user/hello and want to test it by
installing it under the URI /hello, we put "/hello" in the first text input field (for Path)
and "file:/home/user/hello" in the second text input field (for Config URL).

The Manager also allows you to stop, reload, remove, or undeploy a web application.
Stopping an application makes it unavailable until further notice, but of course it can then
be restarted. Users attempting to access a stopped application will receive an error
message, such as 503 - This application is not currently available.

Removing a web application removes it only from the running copy of Tomcat -- if it was
started from the configuration files, it will reappear the next time you restart Tomcat (i.e.,
removal does not remove the web application's content from disk).

3. Deploying a Web Application

There are two ways of deploying a web application on the filesystem:

1. Copy your WAR file or your web application's directory (including all of its content) to
the $CATALINA_BASE/webapps directory.

2. Create an XML fragment file with just the Context element for your web application,
and place this XML file in $CATALINA_BASE/webapps. The web application itself can
then be stored anywhere on your filesystem.

If you have a WAR file, you can deploy it by simply copying the WAR file into the
directory CATALINA_BASE/webapps. The filename must end with an extension of
".war". Once Tomcat notices the file, it will (by default) unpack it into a subdirectory
with the base name of the WAR file. It will then create a context in memory, just as
though you had created one by editing Tomcat's server.xml file. However, any necessary
defaults will be obtained from the DefaultContext element in Tomcat's server.xml file.

Another way to deploy a web app is by writing a Context XML fragment file and
deploying it into the CATALINA_BASE/webapps directory. A context fragment is not a
complete XML document, but just one Context element and any subelements that are
appropriate for your web application. These files are like Context elements cut out of the
server.xml file, hence the name "context fragment."

For example, if we wanted to deploy the WAR file MyWebApp.war along with a realm for
accessing parts of that web application, we could use this fragment:

<!--
Context fragment for deploying MyWebApp.war
-->
<Context path="/demo" docBase="webapps/MyWebApp.war"
debug="0" privileged="true">

86
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Context>

Put that in a file called "MyWebApp.xml," and copy it into your


CATALINA_BASE/webapps directory.

These context fragments provide a convenient method of deploying web applications;


you do not need to edit the server.xml file and, unless you have turned off the default
liveDeploy feature, you don't have to restart Tomcat to install a new web application.

4. Configuring Virtual Hosts

The Host element normally needs modification only when you are setting up virtual
hosts. Virtual hosting is a mechanism whereby one web server process can serve multiple
domain names, giving each domain the appearance of having its own server. In fact, the
majority of small business web sites are implemented as virtual hosts, due to the expense
of connecting a computer directly to the Internet with sufficient bandwidth to provide
reasonable response times and the stability of a permanent IP address.

Name-based virtual hosting is created on any web server by establishing an aliased IP


address in the Domain Name Service (DNS) data and telling the web server to map all
requests destined for the aliased address to a particular directory of web pages. Since this
article is about Tomcat, we don't try to show all of the ways to set up DNS data on
various operating systems. If you need help with this, please refer to DNS and Bind, by
Paul Albitz and Cricket Liu (O'Reilly). For demonstration purposes, I'll use a static hosts
file, since that's the easiest way to set up aliases for testing purposes.

To use virtual hosts in Tomcat, you just need to set up the DNS or hosts data for the host.
For testing, making an IP alias for localhost is sufficient. You then need to add a few lines
to the server.xml configuration file:

<Server port="8005" shutdown="SHUTDOWN" debug="0">


<Service name="Tomcat-Standalone">
<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8080" minProcessors="5" maxProcessors="75"
enableLookups="true" redirectPort="8443"/>
<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8443" minProcessors="5" maxProcessors="75"
acceptCount="10" debug="0" scheme="https"
secure="true"/>
<Factory
className="org.apache.coyote.tomcat4.CoyoteServerSocketFactory"
clientAuth="false" protocol="TLS" />
</Connector>
<Engine name="Standalone" defaultHost="localhost" debug="0">
<!-- This Host is the default Host -->
<Host name="localhost" debug="0" appBase="webapps"
unpackWARs="true" autoDeploy="true">

87
<Context path="" docBase="ROOT" debug="0"/>
<Context path="/orders" docBase="/home/ian/orders" debug="0"
reloadable="true" crossContext="true">
</Context>
</Host>

<!-- This Host is the first "Virtual Host": www.example.com -->


<Host name="www.example.com" appBase="/home/example/webapp">
<Context path="" docBase="."/>
</Host>

</Engine>
</Service>
</Server>

Tomcat's server.xml file, as distributed, contains only one virtual host, but it is easy to add
support for additional virtual hosts. The simplified version of the server.xml file in the
previous example shows in bold the overall additional structure needed to add one virtual
host. Each Host element must have one or more Context elements within it; one of these
must be the default Context for this host, which is specified by having its relative path
set to the empty string (for example, path="").

5. Configuring Basic Authentication

Container-managed authentication methods control how a user's credentials are verified


when a web app's protected resource is accessed. When a web application uses basic
authentication (BASIC in the web.xml file's auth-method element), Tomcat uses HTTP
basic authentication to ask the web browser for a username and password whenever the
browser requests a resource of that protected web application. With this authentication
method, all passwords are sent across the network in base64-encoded text.

Note: using basic authentication is generally considered insecure because it does not
strongly encrypt passwords, unless the site also uses HTTPS or some other form of
encryption between the client and the server (for instance, a virtual private network).
Without this extra encryption, network monitors can intercept (and misuse) users'
passwords. But, if you're just starting to use Tomcat, or if you just want to test container-
managed security with your web app, basic authentication is easy to set up and test. Just
add <security-constraint> and <login-config> elements to your web app's web.xml
file, and add the appropriate <role> and <user> elements to your
CATALINA_BASE/conf/tomcat-users.xml file, restart Tomcat, and Tomcat takes care of
the rest.

The example below shows a web.xml excerpt from a club membership web site with a
members-only subdirectory that is protected using basic authentication. Note that this
effectively takes the place of the Apache web server's .htaccess files.

<!--
Define the Members-only area, by defining

88
a "Security Constraint" on this Application, and
mapping it to the subdirectory (URL) that we want
to restrict.
-->
<security-constraint>
<web-resource-collection>
<web-resource-name>
Entire Application
</web-resource-name>
<url-pattern>/members/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>member</role-name>
</auth-constraint>
</security-constraint>
<!-- Define the Login Configuration for this Application -->
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>My Club Members-only Area</realm-name>
</login-config>

6. Configuring Single Sign-On

Once you've set up your realm and method of authentication, you'll need to deal with the
actual process of logging the user in. More often than not, logging into an application is a
nuisance to an end user, and you will need to minimize the number of times they must
authenticate. By default, each web application will ask the user to log in the first time the
user requests a protected resource. This can seem like a hassle to your users if you run
multiple web applications and each application asks the user to authenticate. Users cannot
tell how many separate applications make up any single web site, so they won't know
when they're making a request that crosses a context boundary, and will wonder why
they're being repeatedly asked to log in.

The "single sign-on" feature of Tomcat 4 allows a user to authenticate only once to access
all of the web applications loaded under a virtual host. To use this feature, you need only
add a SingleSignOn Valve element at the host level. This looks like the following:

<Valve className="org.apache.catalina.authenticator.SingleSignOn"
debug="0"/>

The Tomcat distribution's default server.xml contains a commented-out single sign-on


Valve configuration example that you can uncomment and use. Then, any user who is
considered valid in a context within the configured virtual host will be considered valid in
all other contexts for that same host.

There are several important restrictions for using the single sign-on valve:

89
• The valve must be configured and nested within the same Host element that the
web applications (represented by Context elements) are nested within.
• The Realm that contains the shared user information must be configured either at
the level of the same Host or in an outer nesting.
• The Realm cannot be overridden at the Context level.
• The web applications that use single sign-on must use one of Tomcat's built-in
authenticators (in the <auth-method> element of web.xml), rather than a custom
authenticator. The built-in methods are basic, digest, form, and client-cert
authentication.
• If you're using single sign-on and wish to integrate another third-party web
application into your web site, and the new web application uses only its own
authentication code that doesn't use container-managed security, you're basically
stuck. Your users will have to log in once for all of the web applications that use
single sign-on, and then once again if they make a request to the new third-party
web application. Of course, if you get the source and you're a developer, you
could fix it, but that's probably not so easy to do.
• The single sign-on valve requires the use of HTTP cookies.

7. Configuring Customized User Directories

Some sites like to allow individual users to publish a directory of web pages on the
server. For example, a university department might want to give each student a public
area, or an ISP might make some web space available on one of its servers to customers
that don't have a virtually hosted web server. In such cases, it is typical to use the tilde
character (~) plus the user's name as the virtual path of that user's web site:

http://www.cs.myuniversity.edu/~username
http://members.mybigisp.com/~username

Tomcat gives you two ways to map this on a per-host basis, using a couple of special
Listener elements. The Listener's className attribute should be
org.apache.catalina.startup.UserConfig, with the userClass attribute specifying
one of several mapping classes. If your system runs Unix, has a standard /etc/passwd file
that is readable by the account running Tomcat, and that file specifies users' home
directories, use the PasswdUserDatabase mapping class:

<Listener className="org.apache.catalina.startup.UserConfig"
directoryName="public_html"
userClass="org.apache.catalina.startup.PasswdUserDatabase"/>

Web files would need to be in directories such as /home/users/ian/public_html or


/users/jbrittain/public_html. Of course, you can change public_html to be whatever
subdirectory into which your users put their personal web pages.

In fact, the directories don't have to be inside of a user's home directory at all. If you don't
have a password file but want to map from a user name to a subdirectory of a common
parent directory such as /home, use the HomesUserDatabase class:

90
<Listener className="org.apache.catalina.startup.UserConfig"
directoryName="public_html" homeBase="/home"
userClass="org.apache.catalina.startup.HomesUserDatabase"/>

In this case, web files would be in directories such as /home/ian/public_html or


/home/jasonb/public_html. This format is more useful on Windows, where you'd likely
use a directory such as C:\home.

These Listener elements, if present, must be inside of a Host element, but not inside of
a Context element, as they apply to the Host itself.

8. Using CGI Scripts with Tomcat

Tomcat is primarily meant to be a servlet/JSP container, but it has many capabilities


rivalling a traditional web server. One of these is support for the Common Gateway
Interface (CGI), which provides a means for running an external program in response to a
browser request, typically to process a web-based form. CGI is called "common" because
it can invoke programs in almost any programming or scripting language: Perl, Python,
awk, Unix shell scripting, and even Java are all supported options. However, you
probably wouldn't run a Java application as a CGI due to the start-up overhead;
elimination of this overhead was what led to the original design of the servlet
specification. Servlets are almost always more efficient than CGIs because you're not
starting up a new operating-system-level process every time somebody clicks on a link or
button.

Tomcat includes an optional CGI servlet that allows you to run legacy CGI scripts; the
assumption is that most new back-end processing will be done by user-defined servlets
and JSPs.

To enable Tomcat's CGI servlet, you must do the following:

1. Rename the file servlets-cgi.renametojar (found in


CATALINA_HOME/server/lib/) to servlets-cgi.jar, so that the servlet that
processes CGI scripts will be on Tomcat's CLASSPATH.
2. In Tomcat's CATALINA_BASE/conf/web.xml file, uncomment the definition of the
servlet named cgi (this is around line 241 in the distribution).
3. Also in Tomcat's web.xml, uncomment the servlet mapping for the cgi servlet
(around line 299 in the distributed file). Remember, this specifies the HTML links
to the CGI script.
4. Either place the CGI scripts under the WEB-INF/cgi directory (remember that
WEB-INF is a safe place to hide things that you don't want the user to be able to
view, for security reasons), or place them in some other directory within your
context and adjust the cgiPathPrefix initialization parameter of the CGIServlet
to identify the directory containing the files. This specifies the actual location of
the CGI scripts, which typically will not be the same as the URL in the previous
step.
5. Restart Tomcat, and your CGI processing should now be operational.

91
The default directory for the servlet to locate the actual scripts is WEB-INF/cgi. As has
been noted, the WEB-INF directory is protected against casual snooping from browsers,
so this is a good place to put CGI scripts, which may contain passwords or other sensitive
information. For compatibility with other servers, though, you may prefer to keep the
scripts in the traditional directory, /cgi-bin, but be aware that files in this directory may be
viewable by the curious web surfer. Also, on Unix, be sure that the CGI script files are
executable by the user under which you are running Tomcat.

9. Changing Tomcat's JSP Compiler

In Tomcat 4.1 (and above, presumably), compilation of JSPs is performed by using the
Ant program controller directly from within Tomcat. This sounds a bit strange, but it's
part of what Ant was intended for; there is a documented API that lets developers use Ant
without starting up a new JVM. This is one advantage of having Ant written in Java. Plus,
it means you can now use any compiler supported by the javac task within Ant; these are
listed in the javac page of the Apache Ant manual. It is easy to use because you need
only an <init-param> with a name of "compiler" and a value of one of the supported
compiler names:

<servlet>
<servlet-name>jsp</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>compiler</param-name>
<param-value>jikes</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>

Of course, the given compiler must be installed on your system, and the CLASSPATH may
need to be set, depending on which compiler you choose.

10. Restricting Access to Specific Hosts

Sometimes you'll only want to restrict access to Tomcat's web app to only specified host
names or IP addresses. This way, only clients at those specified sites will be served
content. Tomcat comes with two Valves that you can configure and use for this purpose:
RemoteHostValve and RemoteAddrValve.

These Valves allow you to filter requests by host name or by IP address, and to allow or
deny hosts that match, similar to the per-directory Allow/Deny directives in Apache

92
httpd. If you run the Admin application, you might want to only allow access to it from
localhost, as follows:

<Context path="/path/to/secret_files" ...>


<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127.0.0.1" deny=""/>
</Context>

If no allow pattern is given, then patterns that match the deny attribute patterns will be
rejected, and all others will be allowed. Similarly, if no deny pattern is given, patterns
that match the allow attribute will be allowed, and all others will be denied.

Jason Brittain is a Senior Software Engineer at Symantec Corporation's Network and


Gateway Security Solutions Team, working on the AntiSpam product. He has contributed
to many Apache Jakarta projects, and has been an active open source software developer
for several years.

Ian F. Darwin has worked in the computer industry for three decades: with Unix since
1980, Java since 1995, and OpenBSD since 1998. He is the author of two O'Reilly books,
Checking C Programs with lint and Java Cookbook, and co-author of Tomcat: The
Definitive Guide with Jason Brittain.

Clustering and Load Balancing in Tomcat 5, Part 1


by Srini Penchikala
03/31/2004

The latest version of the Tomcat servlet container provides clustering and load balancing
capabilities that are essential for deploying scalable and robust web applications. The first
part of this article provides an overview of installation, configuration, usage, and
extension of clustering and load balancing features. The second will introduce a sample
web application to demonstrate the steps involved in configuring Tomcat server instances
to enable clustering, and will study session persistence using in-memory replication in the
cluster environment.

The Tomcat 5 server comes with a rules-based load balancer application. Two simple
custom load balancing rules (extending the rules API) were written based on round-robin
and random algorithms to redirect incoming web requests. Performance benchmarks for
the sample web application running in the cluster environment are presented. The load
testing tool JMeter was used to simulate multiple web users to study the load-balancing
mechanism.

Since this article concentrates mainly on demonstrating the clustering capabilities in the
Tomcat servlet container, J2EE application clustering to replicate EJB, JNDI, and JMS

93
objects is not discussed here. Refer to the articles "J2EE Clustering" and "J2EE
Clustering with JBoss" for EJB and JMS clustering.

Large-Scale System Design

Enterprise web portal applications must provide scalability and high availability (HA) for
web services in order to serve thousands of users hitting a corporate web site. Scalability
is the system's ability to support increasing numbers of users by adding additional servers
to the cluster. High availability is basically providing redundancy in the system. If a
cluster member fails for some reason, another member in the cluster can transparently
take over the web requests. Deploying a web portal application in a cluster environment
gives us the ability to achieve scalability, reliability, and high availability required by the
web portal application. Basically, the main goal of clustering is to prevent any web site
outage problems occurring due to a Single Point of Failure (SPoF) in the system.

Large-scale system design provides mission-critical services to ensure minimal downtime


and maximum scalability in an enterprise application environment. Rather than run a
single server, multiple cooperating servers are run. To scale, you should include
additional machines within the cluster and to minimize downtime, you should make sure
every component of the cluster is redundant. The main ingredient of a large-scale system
is clustering, which includes load balancing, fault tolerance, and session state persistence
features. Usually for web applications, a hardware- or software-based load balancer sits
in front of the application servers within the cluster. These load balancers are used to
distribute the load between the cluster nodes by redirecting web traffic to an appropriate
cluster member, at the same time detecting any server failures.

Clustering

A cluster is defined as a group of application servers that transparently run a J2EE


application as if it were a single entity. There are two methods of clustering: vertical
scaling and horizontal scaling. Vertical scaling is achieved by increasing the number of
servers running on a single machine, whereas horizontal scaling is done by increasing the
number of machines in the cluster. Horizontal scaling is more reliable than vertical
scaling, since there are multiple machines involved in the cluster environment, as
compared to only one machine. With vertical scaling, the machine's processing power,
CPU usage, and JVM heap memory configurations are the main factors in deciding how
many server instances should be run on one machine (also known as the server-to-CPU
ratio).

The servers in a J2EE cluster are usually configured using one of the three options. In the
independent approach, each application server has its own file system with its own copy
of the application files. Another approach is to use a shared file system, where the cluster
uses a single storage device that all application servers use to obtain application files. A
third configuration approach is called the managed approach, where an administrative
server controls access to application content and is responsible for "pushing" appropriate
application content to managed servers. The admin server ensures that all servers in the

94
cluster have the application available. It also updates all servers when an application is
deployed, and removes the application from all servers when the application is
undeployed.

Clustering can be done at various tiers in a J2EE application, including at the database
tier. Some database vendors offer clustered databases that support data replication
between multiple database servers by providing client transparency where the client
(usually a servlet container or an application server) doesn't have to know to which
database server it's connecting to get the data. Examples of JDBC clustering are
Oracle9i's Real Application Clusters (RAC) and Clustered JDBC (C-JDBC). RAC
supports fail over of database connections and transparently reroutes JDBC connections
and database requests to a failed over database node. C-JDBC is an open source database
cluster that allows a web application to transparently access a cluster of databases
through a JDBC driver. This implementation not only load balances JDBC connections
among the database nodes in the cluster, but also fails over to a secondary database
server.

Clustering in Tomcat

Clustering was available in the previous Tomcat version (version 4.1) as a third-party
JAR file; it wasn't very easy to install or configure to make multiple Tomcat instances run
in a cluster. JavaGroups is a popular choice for adding clustering capabilities in open
source servlet containers (Tomcat) and application servers (JBoss). But in the latest
version of Tomcat server, clustering comes as part of the main installation package. This
minimizes all of the extra effort that goes into integrating third-party clustering
implementations into the Tomcat server.

In a typical cluster environment, for servers in the cluster to cooperate and replicate state,
they need to communicate with each other. This group communication is achieved either
by point-to-point RMI (TCP-IP) or via IP multicast. Most of the J2EE application servers
(such as JBoss, Oracle, WebLogic, and Borland) all use IP multicast communication to
send state/updates/heartbeats to one another in the cluster. Here's how the communication
among the cluster members works in Tomcat: all of the cluster members talk to each
other using multicast ping messages. Each Tomcat instance will send out a message in
which it will broadcast its IP address and TCP listen port for session replication. If an
instance has not received the message within a given time frame, it is considered down.

Another popular concept in clustering, called farming, provides cluster-wide hot


deployment of web applications. In a server farm, a web application is deployed by
copying an application's WAR file to only one node in the cluster; farming will take care
of deploying the web application across the entire cluster. Similarly, removing the WAR
file from a single cluster node will result in undeploying the web application from all the
nodes in the cluster. The Tomcat clustering documentation mentions that a future Tomcat
version will support farming capability.

95
Load Balancing

Load balancing (also known as high availability switch over) is a mechanism where the
server load is distributed to different nodes within the server cluster, based on a load
balancing policy. Rather than execute an application on a single server, the system
executes application code on a dynamically selected server. When a client requests a
service, one (or more) of the cooperating servers is chosen to execute the request. Load
balancers act as single points of entry into the cluster and as traffic directors to individual
web or application servers.

Two popular methods of load balancing in a cluster are DNS round robin and hardware
load balancing. DNS round robin provides a single logical name, returning any IP
address of the nodes in the cluster. This option is inexpensive, simple, and easy to set up,
but it doesn't provide any server affinity or high availability. In contrast, hardware load
balancing solves the limitations of DNS round robin through virtual IP addressing. Here,
the load balancer shows a single IP address for the cluster, which maps the addresses of
each machine in the cluster. The load balancer receives each request and rewrites headers
to point to other machines in the cluster. If we remove any machine in the cluster, the
changes take effect immediately. The advantages of hardware load balancing are server
affinity and high availability; the disadvantages are that it's very expensive and complex
to set up.

There are many different algorithms to define the load distribution policy, ranging from a
simple round robin algorithm to more sophisticated algorithms used to perform the load
balancing. Some of the commonly used algorithms are:

• Round-robin
• Random
• Weight-based
• Minimum load
• Last access time
• Programmatic parameter-based (where the load balancer can choose a server
based upon method input arguments)

Load-balancing algorithms affect statistical variance, speed, and simplicity. For example,
the weight-based algorithm has a longer computational time than the other algorithms.
For a more detailed explanation on load balancing, refer to the ONJava article "Load
Balancing Web Applications."

Load Balancing in Tomcat

Load balancing capability was not provided in previous Tomcat versions. The integration
of the Apache web server and the Tomcat servlet container together has been a popular
choice to handle web requests and to balance loads. In an Apache-Tomcat setup, a Tomcat
instance called Tomcat Worker is configured to implement load balancing.

96
Tomcat 5 provides load balancing in three different ways: using the JK native connector,
using Apache 2 with mod_proxy and mod_rewrite, or using the balancer web app. In this
article, we concentrate on the third option, using the balancer web application to redirect
web requests to different nodes in the cluster. The load balancer application is a rules-
based application that uses a servlet filter mechanism to redirect incoming web requests
to the next available member in the cluster. Servlet filters were introduced in the Servlet
2.3 specification. These filters are used for a variety of tasks in a web application, such as
JAAS authentication, encryption, logging and auditing, data compression, XSLT filters
that transform XML content, etc. As mentioned on the Tomcat balancer web site, the
balancer application is not designed as a replacement for other robust load-balancing
mechanisms. Rather, it's a simple and extensible way to direct traffic among multiple
servers. Check out the sample Java classes provided in the balancer application to
understand how load balancing is achieved in different ways using different rules criteria.

The load balancing is enabled by creating a rules configuration file (called rules.xml) that
contains various rules and redirection URLs. The balancer filter checks the RuleChain to
determine where to redirect the request by checking the rules in the same order as they
are defined in the rules.xml file. As soon as a Rule matches the criteria, the filter stops the
evaluation and redirects the request to URL specified for the matching rule.

Fault Tolerance

Fault tolerance is the system's ability to allow a computation to fail over to another
available server if a server in the cluster goes down, as transparently to the end user as
possible. An ideal fail over scenario is that the cluster service should detect when a server
instance is no longer available to take any requests, and stop sending requests to that
instance. It should also periodically check to see if a cluster member is available again
and, if so, automatically add it to the pool of active cluster nodes.

Fault Tolerance in Tomcat

Tomcat 5 does not provide a built-in fail over mechanism to detect when a cluster
member crashes. Hopefully, a future version of Tomcat will provide the fail over feature
that can be used to find the availability of a specific cluster member to make sure it's
ready to service incoming web requests.

There are two levels of fail over capabilities typically provided by clustering solutions:

• Request-level fail over: If one of the servers in the cluster goes down, all
subsequent requests should be redirected to the remaining servers in the cluster.

97
This involves using a heartbeat mechanism to keep track of the server status and
to avoid sending requests to the servers that are not responding. In our cluster
setup, a Tomcat instance acting as a load balancer takes care of request level fail
over by forwarding web requests to another node in the cluster.
• Session-level fail over: A web client can have a session that is maintained by the
HTTP server. In session-level fail over, if one of the servers in the cluster goes
down, another server in the cluster should be able to carry on with the sessions
that were being handled by the first server, with minimal loss of continuity. This
involves replicating the session data across the cluster. A Tomcat cluster with
session replication capability takes care of session-level fail over.

Session State Persistence

Fail over and load balancing require the session state to be replicated at different servers
in a cluster. Session state replication allows a client to seamlessly get session information
from another server in the cluster when the original server, on which the client
established a session, fails. The state can be system state and/or application state
(application state contains the objects and data stored in an HTTP session). The main goal
of session replication is not to lose any session details if a cluster member crashes or is
stopped for application updates or system maintenance.

As far as session persistence is concerned, clustering can be a simple scenario in which a


cluster member doesn't have any knowledge of session state in the other cluster members.
In this scenario, the user session lives entirely on one server, selected by the load
balancer. This is called a sticky session (also known as session affinity), since the session
data stays in the cluster member that received the web request.

On the other hand, the cluster can be implemented in such a way that each cluster
member is completely aware of session state in other cluster members, with the session
state periodically propagated to all (or preferably, one or two) backup cluster members.
This type of session is known as a replicated session.

There are three ways to implement session persistence:

• Memory-to-memory replication.
• File System session persistence, where session information is written to and read
from a centralized file system.
• Database session persistence, where session data is stored in a JDBC data store.

In memory session replication, the individual objects in the HttpSession are serialized
to a backup server as they change, whereas in database session persistence, the objects in
the session are serialized together when any one of them changes.

The main drawback of database/file system session persistence is limited scalability when
storing large or numerous objects in the HttpSession. Every time a user adds an object

98
to the HttpSession, all of the objects in the session are serialized and written to the
database or shared file system.

Session Replication in Tomcat

Session replication in the current version of Tomcat server is an all-to-all replication of


session state, meaning the session attributes are propagated to all cluster members all the
time. This algorithm is efficient when the clusters are small. For large clusters, the next
Tomcat release will support primary-secondary session replication, where the session will
only be stored at one or maybe two backup servers.

There are three types of session replication mechanisms in Tomcat:

• Using in-memory replication, with the SimpleTcpCluster (in the


org.apache.catalina.cluster.tcp package) that ships with Tomcat 5 (in
server/lib/catalina-cluster.jar).
• Using session persistence, and saving the session to a shared database
(org.apache.catalina.session.JDBCStore).
• Saving the session state to a shared file system
(org.apache.catalina.session.FileStore, part of catalina-optional.jar).

Factors to Consider in Implementing a J2EE Cluster

There are many factors to take into account when designing a J2EE cluster. The following
is a list of questions to be considered in a large-scale J2EE system design. (This list is
taken from "Creating Highly Available and Scalable Applications Using J2EE" in the EJB
Essentials Training document.)

Clustering

• What kind of clustering should be implemented: vertical scaling or horizontal


scaling?
• In what tier should clustering be implemented: web server or servlet container for
servlets, JSP, and HTTP session objects; or application server for EJB, JMS, and
JNDI objects or database clustering?

Load Balancing

• When is a server selected (i.e. affinity): every request, every transaction, or every
session?
• How is a server selected (i.e. load balancing policy): randomly, round-robin,
weight-based, least loaded server, or by the application?
• Where is load balancing accomplished: in one place or many, at the client or at the
server?

99
Fault Tolerance

• How are server failures detected?


• When is it right time to fail over and try another server?
• What about system and application state at the failed node?

Session State Persistence

• How is state communicated?


• How often is it communicated?
• How is object state materialized?
• Is the state persistence mechanism efficient?
• Consistency of replicated state?
• Any network constraints in replicating the session state?

Proposed Cluster Setup

Listed below are the main objectives I wanted to accomplish in the proposed cluster
environment:

• The cluster should be highly scalable.


• It should be fault-tolerant.
• It should be dynamically configurable, meaning it should be easy to manage the
cluster declaratively (changing a configuration file) rather than programmatically
(changing Java code).
• It should provide automatic cluster member discovery.
• It should have fail over and load-balancing features for session data with in-
memory session state replication.
• It should have pluggable/configurable load-balancing policies.
• It should perform group membership notification when a member of the cluster
joins or leaves a group.
• There should be no loss of message transmission through multicast.
• Clustering should be seamless to the web application and the server. It should
provide both client and server transparency. Client transparency means that the
client is not aware of clustered services or how the cluster is set up. The cluster is
identified and accessed as a single thing, rather than as individual services. Server
transparency means that the application code in a server is not aware that it's in a
cluster. The application code cannot communicate with the other members of
cluster.

Conclusion

In part two of this article, we'll look at how to deploy a cluster (by running multiple
Tomcat server instances) to achieve these goals. We will discuss the cluster architecture
and configuration details to enable session replication in Tomcat 5.

100
Srini Penchikala is an information systems subject matter expert at Flagstar Bank.

101

Das könnte Ihnen auch gefallen