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
/conf directory of the Tomcat instance that you will be integrating with IIS. Note: 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 /conf directory. 3. Modifying the Windows Registry
4
In this step, we are going to create the Windows Registry settings that will be used to point the ISAPI Redirector to our newly-created files. Note: To add these new registry settings, you will need to use the Windows REGEDT32.EXE application, which is most likely in your C:\WINDOWS\SYSTEM32 directory. Before you can add any of these settings, you must add the following registry key: HKEY_LOCAL_MACHINE SOFTWARE Apache Software Foundation Jakarta Isapi Redirector 1.0
Note: These keys must be entered exactly as they are listed above. After you have the above keys, select the 1.0 key and add the following name/values pairs -- all of which are string values. Name
Data
extension_uri
/tomcat/isapi_redirector.dll
worker_file
/conf/workers.properties
worker_mount_file /conf/uriworkermap.properties log_file
/logs/jk_iis.log
log_level
debug
These string values are read by the ISAPI Redirector at startup, and are used to initialize IIS. The following table provides a description of each of these variables. Name
Data
extension_uri
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.
worker_file
The worker_file variable tells the ISAPI Redirector the location of the workers.properties file.
worker_mount_file
The worker_mount_file variable tells the ISAPI Redirector the location of the uriworkermap.properties file.
log_file
The log_file variable defines the name and location of the ISAPI Redirector's log file.
log_level
The log_level variable defines the debug level used when writing
5
to the log file. The possible values are debug, info, error, and emerg. Once you have added the appropriate keys and variables, your registry should resemble the following figure.
4. Configuring the ISAPI Redirector We are now at our final step. In this step, we are going to actually tell IIS when to use the ISAPI Redirector. To do this, we need to complete all of the following steps: 1. 2. 3. 4.
Copy the isapi_redirector.dll file to your /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 /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 /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
AJP 13
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.
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 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
elements.
The only two attributes in this 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 /conf directory of the Tomcat instance that you will be integrating with Apache. ( 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 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.
<workername>.type
The type property defines the protocol of this Tomcat worker, which in our case is ajp13.
Modify Apache's httpd.conf Configuration File Now that we have defined a Tomcat worker, we need to tell Apache to talk to that worker. We do this by making several modifications to Apache's <APACHE_HOME>/conf/httpd.conf file. This process is broken down into several steps: 1. Copy the previously downloaded mod_jk module to the <APACHE_HOME>/libexec directory. 2. Tell Apache to load the mod_jk module. We do this by adding the LoadModule and AddModule directives to the bottom of the httpd.conf file, as follows: 3. LoadModule jk_module libexec/mod_jk-1.3.26.dll AddModule mod_jk.c
12
Note: If your OS is a flavor of Unix, then you will most likely be pointing at the file mod_jk-1.3-eapi.so. If you are on a Windows box, which is what I am currently using, then you will most likely be pointing to the file mod_jk-1.3.26.dll. 4. We must now tell mod_jk the location of our workers.properties file. This is done by using the JkWorkersFile property. Make sure you use the appropriate path when defining the location of your properties file. JkWorkersFile C:/Tomcat4_1_12/conf/workers.properties
5. Our next step is an optional but very useful (when you run into problems) step: naming a log file that will record mod_jk's actions. You do this by adding two additional properties to the httpd.conf file. The first of these properties, JkLogFile, identifies the location of the log file. The second, JkLogLevel, defines the logging level, which can be one of three logging levels: debug, error, or info, which decrease in level of verbosity, respectively. Note: If you do not define a log level, then no log file will be generated. JkLogFile C:/Tomcat4_1_12/logs/mod_jk.log JkLogLevel debug
6. The next step is to tell Apache that we want all static content requested from the /examples directory to be served from the /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 element enforces this constraint: 10. 11.AllowOverride None 12.deny from all
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 AllowOverride None deny from all
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"> <Engine name="Standalone" defaultHost="localhost" debug="0">
15
prefix="localhost_examples_log." suffix=".txt" timestamp="true"/>
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 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 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 components. The <Engine> element must be nested immediately after the 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 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: • • • •
The Element The element defines the virtual hosts that are contained in each instance of a Catalina <Engine>. Each can be a parent to one or more Web applications, which are represented by a component, which will be described in the following section. You must define at least one for each Engine element. The possible attributes for the 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 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 element defined for the Standalone <Engine> is listed in the following code snippet:
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 element, which will be described later in this article. The element is configured as a child of the <Engine> element. It can be configured as a parent to the following elements: • • • •
The Element The element is the most commonly used element in the server.xml file. It represents an individual Web application that is running within a defined . There is no limit to the number of contexts that can be defined within a element. Each definition must have a unique context path, which is defined by the path attribute. The possible attributes for the 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 . 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 defined in a given . 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 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 element that defines the /examples application is included in the following code snippet:
The context definition defines a Web application named /examples that will have all of its resources stored in the directory /Webapps/examples. This context also states that this application will be reloaded when class files are changed. The element is configured as a child of the element. It can be configured as a parent to the following elements: • • • • • • • • •
<Manager> <Ejb> <Environment> <Parameter>
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 /conf/ directory.
20
The Element The final element we are going to examine is the element. The element defines the component that does the actual managing of requests and responses to and from a calling client. The element is defined by the org.apache.catalina.Connector interface. The 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 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 defining a HTTP connector is contained in the following code snippet:
This 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> <Engine>
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 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 /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 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 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 /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 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 /bin/bootstrap.jar o /server/lib/catalina.jar o /server/lib/servlet-cgi.jar o /server/lib/servlets-common.jar o /server/lib/servlets-default.jar o /server/lib/servlets-invoker.jar o /server/lib/servlets-manager.jar o /server/lib/servlets-snoop.jar o /server/lib/servlets-ssi.jar o /server/lib/servlets-webdav.jar o /server/lib/jakarta-regexp-1.2.jar o /lib/naming-factory.jar o /common/lib/crimson.jar o /common/lib/jasper-compiler.jar o /common/lib/jasper-runtime.jar o /common/lib/jaxp.jar o /common/lib/jndi.jar o /common/lib/naming-common.jar o /common/lib/naming-resources.jar o /common/lib/servlet.jar o /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 /common/lib/
directory. v1.0.1 This .jar file is packaged with Tomcat in the /common/lib/ directory. xerces.jar v1.4.2 This .jar file is packaged with Tomcat in the /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 /webapps/ directory. You can find this file in the SOAP 2.2 archive. Once you have moved the soap.war file into /webapps/directory, you need to make sure that each of the previously listed .jar files are in the /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 /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 org.apache.soap.server.DOMFaultListener
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 [email protected] 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: /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: <user name="tomcat" password="tomcat" roles="tomcat" /> <user name="role1" password="tomcat" roles="role1" /> <user name="both" password="tomcat" roles="tomcat,role1" />
Note: The default location of the MemoryRealms XML file is the /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 element described in the following section. As you can see, there is nothing terribly complicated about this file. It has a root element of , which contains n-number of the sub-element <user>. The <user> element contains all of the necessary information to validate a user. This information is contained in the attributes of the <user> sub-element. Table 1 contains a description of each of the attributes required in the <user> sub-element. Table 1. The Attributes of the <user> Sub-Element Attribute
Description
name
The name attribute contains a string representing the username that will be used in the login form.
password
The password attribute contains a string representing the password that will be
40
used in the login form. roles
The roles attribute contains the role or roles assigned to the named user. This is the value that must match the 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 /conf/server.xml and uncomment the following line.
By un-commenting this 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 /webapps/onjava/WEB-INF/web.xml and add the following security constraint: <security-constraint> <web-resource-collection> <web-resource-name>OnJava Application /* onjavauser
There are only two sub-elements that you need to focus upon. The first is the 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, , defines the user role that can access the resource protected by the previously defined . 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 sub-element directly following the <security-constraint>.
41
BASIC OnJava Application
The sub-element defines the authentication method for the defined realm. The possible values are BASIC, DIGEST, and FORM. And the sub-element names the Web resource that this maps to. 4. Open /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 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
Description
user_name
The user_name column contains a string representing the username that will be used in the login form. The user_name has a type of varchar(12).
user_pass
The user_pass column contains a string representing the user's password. The user_pass has a type of varchar(12).
43
The second table in the users database is the roles table. The roles table contains all of the possible roles for the users defined in this database. The roles table contains a single column, role_name, that is a varchar(12) string representing each role name. The last table in the users database is the user_roles table. The user_roles table is a mapping table between the roles and users defined in this database. Table 3 contains the table definition for the user_roles table. Table 3. The user_roles Table Definition. Column
Description
user_name
The user_name column contains a string representing the username that will be used in the login form. The user_name has a type of varchar(12).
role_name
The role_name column contains a string representing the user's role. The role_name has a type of varchar(12).
The contents of each of the users database's tables are listed in Tables 4, 5, and 6. Table 4. The Contents of the users Table user_name
user_pass
robert
password
bob
password
tomcat
password
joe
$joe$
Table 5. The Contents of the roles Table user_name
onjava manager tomcat Table 6. The Contents of the user_roles Table
44
user_name
user_pass
bob
onjavauser
joe
onjavauser
joe
manager
tomcat
tomcat
robert
onjavauser
Creating and Configuring a MySQL Users Database Now that you have defined the users database, you can actually create the physical database. Before you can create the users database, you will need to download and install the MySQL server, which can be found at http://www.mysql.com. You should also download the latest JDBC driver for MySQL, which can also be found at the previously mentioned Web site. Note: For this example we are using MySQL. You can use any JDBC-compliant database server of your choosing. After you have MySQL installed, you need to complete the following steps to create and configure a MySQL Users database: 1. Start the mysql client found in the <mysql_home>/bin/ directory. 2. Create the Users database, which will be explicitly named tomcatusers, by executing the following command: create database tomcatusers;
3. Create the users table using the following command: create table users ( user_name varchar(15) not null primary key, user_pass varchar(15) not null );
4. Create the roles table using the following command: create table roles ( role_name varchar(15) not null primary key );
5. Create the user_roles table using the following command:
45
create table users ( user_name varchar(15) not null, role_name varchar(15) not null, primary key(user_name, role_name) );
6. Insert the user data into the users table, by executing the following commands: insert 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 /conf/server.xml and place comment tags around the previously uncommented element.
2. Place the following code snippet directly below the previously referenced element:
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 entry defines a JDBCRealm that leverages our database as its container of users. The attributes used in the element, with additional optional attributes, are described in Table 7. Table 7. The Attributes of the Element Attribute
Description
classname
The fully qualified class name of the Realm implementation.
driverName
The name of the driver used to connect to the database containing the users.
connectionURL
The URL referencing the database containing the users.
connectionName
The username to use when connecting to the database. If you are using MySQL, you can encode the username directly on the connectionURL.
connectionPassword
The password to use when connecting to the database. Again, if you are using MySQL, you can encode the password directly on the connectionURL.
userTable
The database table containing the user's information.
userNameCol
The column in the userTable that references the user's username.
userCredCol
The column in the userTable that references the user's password.
userRoleTable
The database table containing the mapping between the userTable and the table containing the possible user roles.
roleNameCol
The column in the userRoleTable that contains a roles given to a user
3. To complete this configuration change, stop and restart the Tomcat server. That is all there is to it; your Web application is now protected by a JDBCRealm. To test this change, try logging in to the /onjava Web application, entering a username from the
47
users
table that has a role of onjavauser. You should see a dialog similar to Figure 1
above. James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software consultancy.
Deploying Web Applications to Tomcat 04/19/2001 In this article we are going to cover the deployment of web applications using Tomcat. We are performing a manual deployment to fully explain the steps involved when deploying a web application. The best way to describe the deployment process is to create a web application of our own that includes the important components found in most Java web applications; then package it for deployment. The following sections will take you through the steps involved in deploying a web application. The name of our web application will be onjava. . In this article, we • • • • • •
create the web application directory structure, create a web application ServletContext, add JSPs, add Servlets, add Tag Libraries, and create and deploy a WAR file.
Creating the Web Application Directory Structure The first thing you need to do is create the directory structure that will contain the application. We discussed this structure in Part 1, Java Web Applications, and I include the relevant details in Table 1.
Table 1. The Web Application Directory Structure Directory
Contains
/onjava
This is the root directory of the web application. All JSP and XHTML files are stored here.
48
/onjava/WEB-INF
This directory contains all resources related to the application that are not in the document root of the application. This is where your web application deployment descriptor is located. Note that the WEBINF directory is not part of the public document. No files contained in this directory can be served directly to a client.
/onjava/WEB-INF/classes
This directory is where servlet and utility classes are located.
/onjava/WEB-INF/lib
This directory contains Java Archive files that the web application depends upon. For example, this is where you would place a JAR file that contained a JDBC driver.
The name of our web application, onjava, is the root of our directory structure. While in development I suggest creating the directory directly in the Tomcat /webapps directory. When it comes time for deployment, you can package your web application into a WAR file and go though the production deployment process. The last step in creating the web application directory structure is adding a deployment descriptor. At this point you'll be creating a default web.xml file that contains only the DTD, describing the web.xml file, and an empty <webapp/> element. Listing 1 contains the source for a default web.xml file. Listing 1 web.xml <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. 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 OnJava Demo <meta http-equiv="Content-Type" content="text/html; charset=iso-88591">
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 OnJava Demo <meta http-equiv="Content-Type" content="text/html; charset=iso-88591"> |
| Welcome : <%= request.getAttribute("USER") %>
52
|
|