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
extension_uri worker_file

Data
/tomcat/isapi_redirector.dll <CATALINA_HOME>/conf/workers.properties

worker_mount_file <CATALINA_HOME>/conf/uriworkermap.properties log_file log_level <CATALINA_HOME>/logs/jk_iis.log 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
extension_uri

Data The extension_uri variable represents the IIS virtual director, which will be created in the next step, plus the name of the ISAPI redirector file. The worker_file variable tells the ISAPI Redirector the location of the workers.properties file. The worker_mount_file variable tells the ISAPI Redirector the location of the uriworkermap.properties file. The log_file variable defines the name and location of the ISAPI Redirector's log file. The log_level variable defines the debug level used when writing

worker_file worker_mount_file log_file log_level

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. 2. 3. 4. Copy the isapi_redirector.dll file to your <CATALINA_HOME>/conf/ directory. Open up your Windows Control Panel. Open the Administrative Tools Application. 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. 2. 3. 4. 5. Configuring Tomcat and Apache using JK v1.2 Configuring Tomcat and IIS using JK v1.2 Configuring Tomcat and Apache using JK v2 Configuring Tomcat and IIS using JK v2 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
Before we can continue with our discussions, you must get all of the components required to configure Tomcat and Apache. The following lists these components and their current locations.
• • •

AJP 13 The AJP 13 protocol is a packet-based protocol that allows a Web server to communicate with the Tomcat JSP/servlet container over a TCP connection. For our purposes, all that we need to know is that AJP13 is a more efficient protocol and includes better support for SSL than its predecessors. Further information on AJP13 is available at http://jakarta.apache.org/tomcat/tomcat4.1-doc/jk2/common/AJPv13.html.

Apache 1.3.27 Jakarta-Tomcat 4.1.12 mod_jk v1.2

Make sure that you download the 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.
<workername>.host

The host property defines the hostname of this Tomcat worker. Because we are configuring both Apache and Tomcat on the same host, this value is currently set to localhost. The type property defines the protocol of this Tomcat worker, which in our case is ajp13.

<workername>.type

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 WEBINF/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 import import import import import import import import java.net.URL; org.apache.catalina.Connector; org.apache.catalina.Context; org.apache.catalina.Deployer; org.apache.catalina.Engine; org.apache.catalina.Host; org.apache.catalina.logger.SystemOutLogger; org.apache.catalina.startup.Embedded; 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. 5. 6. 7. // Create an embedded server embedded = new Embedded(); embedded.setDebug(5); // 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. 10. // Create an engine 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. 13. 14. 15. // Create a default virtual host host = embedded.createHost("localhost", getPath() + "/webapps"); 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. 18. 19. // Create the ROOT context Context context = embedded.createContext("", 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. 24. 25. // Assemble and install a default HTTP connector Connector connector = embedded.createConnector(null, 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] HttpProcessor[8080][0] HttpProcessor[8080][1] HttpProcessor[8080][1] HttpProcessor[8080][2] HttpProcessor[8080][2] HttpProcessor[8080][3] HttpProcessor[8080][3] HttpProcessor[8080][4] HttpProcessor[8080][4] Starting background thread Background thread has been Starting background thread Background thread has been Starting background thread Background thread has been Starting background thread Background thread has been Starting background thread Background thread has been started started started started 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. 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.
activation.jar

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 previouslydeployed 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

scope

The type attribute defines the implementation type of the SOAP service. We defined our service as a Java service. 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 import import import import java.io.*; java.net.*; java.util.*; org.apache.soap.*; 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
name password

Description The name attribute contains a string representing the username that will be used in the login form. 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 <securitycontstraint>. 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
user_name

Description 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). The user_pass column contains a string representing the user's password. The user_pass has a type of varchar(12).

user_pass

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
user_name

Description 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). The role_name column contains a string representing the user's role. The role_name has a type of varchar(12).

role_name

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 bob tomcat joe

password password password $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 joe joe tomcat robert

onjavauser onjavauser manager tomcat 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 insert insert insert into into into into users users users users values("bob", "password"); values("joe", "$joe$"); values("robert", "password"); 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 insert insert insert insert into into into into into user_roles user_roles user_roles user_roles user_roles values("bob", "onjavauser"); values("joe", "onjavauser"); values("joe", "manager"); values("robert", "onjavauser"); 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
classname

Description The fully qualified class name of the Realm implementation. The name of the driver used to connect to the database containing the users. The URL referencing the database containing the users. The username to use when connecting to the database. If you are using MySQL, you can encode the username directly on the connectionURL. The password to use when connecting to the database. Again, if you are using MySQL, you can encode the password directly on the connectionURL. The database table containing the user's information. The column in the userTable that references the user's username. The column in the userTable that references the user's password. The database table containing the mapping between the userTable and the table containing the possible user roles. The column in the userRoleTable that contains a roles given to a user

driverName connectionURL connectionName

connectionPassword

userTable userNameCol

userCredCol

userRoleTable

roleNameCol

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
/onjava

Contains 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 WEBINF directory is not part of the public document. No files contained in this directory can be served directly to a client. This directory is where servlet and utility classes are located. 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.

/onjava/WEB-INF/classes /onjava/WEB-INF/lib

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

per web application. We will discuss the relationship between a and its web application in much more detail in Part 4, "Web Applications and the ServletContext."
ServletContext 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-88591"> </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-88591"> </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 import import import javax.servlet.*; javax.servlet.http.*; java.io.*; 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 String // Get String the username from the request username = request.getParameter("username"); the password from the request 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, <tagliburi>, 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-88591"> </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 Tomcat 4.0 beta 1 JDK 1.3 Standard Edition For this article we will be using the latest versions of the tools listed above. Location http://jakarta.apache.org/

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 bash export TOMCAT_HOME tcsh
TOMCAT_HOME=/bob/jakarta-tomcat-4.0-b1 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 Windows NT/2000 Linux Startup Shutdown

TOMCAT_HOME\bin\startup.bat TOMCAT_HOME\bin\shutdown.bat 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.

/conf /server

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

66

/lib /logs /src

This directory contains Java Archive files that Tomcat is dependent upon. This directory contains Tomcat's log files. This directory contains the source code used by the Tomcat server. Once 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. /work This is the directory in which Tomcat will place all servlets that are generated 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 domainname 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 domainname 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 somedomain.com. Nevertheless, you are entitled to point somehost.your-domain.com to some-otherhost.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. 2. 3. 4. 5. I manage my weblogs. I manage documentation for Aspire/J2EE. I support Aspire/J2EE using feedback. I manage documentation for Knowledge Folders. 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</listenerclass> </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. Precompilation 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 try {if try {if (SQLException }

(rs != null) rs.close();} catch (SQLException e) {} (pstmt != null) pstmt.close();} catch (SQLException e) {} (connection != null) connection.close();} catch 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 containermanaged 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 rulesbased 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 inmemory 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