Tomcat 5

This article was originally published in VSJ, which is now part of Developer Fusion.
Once upon a time, application servers were complex software packages that only executed on huge monstrous machines. Server side applications were difficult to create, required great skills and in-depth system knowledge, and were often a nightmare to maintain. Web-centric computing, increasing trends towards componentization and code reuse, together with the popularity of the Java platform (specfically J2EE), have brought us the new generation of utility application servers. These servers allow us to build highly modular applications, using servlets and JSP as building blocks.

Standardizing on servlets and JSP in the application tier enables server developers to improve their own art – creating “leaner and meaner” application servers. The new Tomcat 5 is a direct result of this evolution in the server’s system engineering. This new release of the popular application server sports an embedded mode of operation, allowing you to include the entire application server – complete with all the JSP and servlet processing mechanisms – directly into your own applications!

Now your own applications can include a complete application server inside. You can tap into Tomcat 5’s servlets and JSP processing capabilities to build your own modular application logic. You can leverage Tomcat 5’s flexible protocol handling mechanisms (especially HTTP handling) and save yourself months of tedious programming. Embedded Tomcat can also be used to front-end a legacy application formerly created for fat (or custom proprietary) clients, giving it a new lease of life as a web-based application.

In this article, we will learn about Tomcat 5’s new and exciting embedded mode of operation. The key enabler for this operation is the componentization of Tomcat’s internal architecture. We will peek under the hood and examine these components. Last but not least, our hands-on example will show how to put this new mode to use by creating our own application with Tomcat 5 inside.

Inside Tomcat 5

A high level understanding of the internals of Tomcat 5 is a pre-requisite for anyone who wants to work with Tomcat in the embedded mode. We will start by exploring inside Tomcat 5.

In the history of Tomcat server evolution, Tomcat 4 represents a disjoint point where the engineers got a rare chance of going back to their drawing boards to design a better server. Unlike the Tomcat 3.x version before it, the architecture of Tomcat 4 is highly componentized and pipelined. Tomcat 5 improved on this architecture by continuing this evolution.

Figure 1
Figure 1: Tomcat in Standalone mode

Figure 1 shows the general “blackbox” functionality of the Tomcat 5 server, and one can deduce that the internal operations of Tomcat may be roughly divided up into the following responsibilities:

  1. handling the incoming HTTP protocol request
  2. decoding the request and extracting cookie/session information
  3. forwarding the request to appropriate processors, according to configured mapping
  4. in the case of servlets and JSP processor, providing an environment to execute them
  5. processing the outgoing response from the processor as HTTP protocol response
Figure 1 show Tomcat running standalone, where Tomcat 5 replaces the function of a conventional web server (by serving static web pages and graphics etc. to the user).

However, Tomcat is quite often deployed in conjunction with a high performance web server (such as Apache or IIS). In this case, the protocol handled in step 1 and 5 will be AJP (Apache JServ Protocol for “web server to application server” connections) instead of HTTP. Figure 2 shows Apache acting as the web server with Tomcat 5 servicing servlets and JSPs requests.

Figure 2
Figure 2: Tomcat with an Apache front end

Comparing Figure 1 and Figure 2, certain common functionalities can be factored out. Tomcat 5 divides these functional pieces into components.

Tomcat 5 architectural components

Figure 3 reveals the architectural components that make up a Tomcat 5 server.

Figure 3
Figure 3: Tomcat’s internal architecture

It is interesting to observe the clear separation of responsibilities. Note that most components nest inside one another, with most of the remaining ones being the Server itself. Any component that can contain other component(s) is called a Container in Tomcat 5 architectural nomenclature.

The following description presents the functionality assumed by each component.

Connector for protocol handling

The Connector component is responsible for receiving and handling communication protocol requests, as well as sending protocol response. This alleviates the other internal components from being protocol dependent. Since incoming requests first reach a connector, it is also a natural point to create threads to process the request. The connector handles decoded incoming protocol requests and running processors. This brings us naturally to the next component, the Engine.

Engine for request processing

An Engine represents the complete servlet processor (Catalina). A number of connectors can be associated with a single Engine component. In these cases, the requests from all of the associated connectors are handled by the same Engine. For example, an Engine instance may service an AJP link to an Apache server front-end, and at the same time as servicing HTTP requests directly on port 8080.

In production, a single Engine can field web application requests for a number of virtual hosts. For example, the same physical Tomcat instance may be handling an e-commerce site for www.buyVSJbooks.com and www.buyVSJcds.com. These virtual hosts are represented by Hosts components. Conceptually, an Engine passes the incoming requests to the corresponding Host component (internal to the Engine). Each Engine has a default Host, this is the Host that will be used to process incoming requests for which a corresponding Host component cannot be found. Note that an Engine can also have an associated default Realm and default Logger. Any Host components will inherit the defaults unless they explicitly define their own Logger and/or Realm.

Host for virtual host handling

Virtual hosts handled by the Engine are represented by Host components. There can be any number of Host elements, but only one can be the default Host to the Engine. Hosts are logical component that provides an environment for web applications to run. A single Host can handle request for many different web applications. For example, we may say in production: “the e-commerce web application and the accounting web application are both running on virtual host www.buyVSJbooks.com”. Each application is represented by a Context component inside the Host. Conceptually, a Host forward incoming requests to the associated Context component for processing. Note that a Host component has a name associated with it (i.e. www.buyVSJbooks.com) and also an appBase property that indicates the directory where web applications for this host is stored (typically called webapps).

Context for web application execution

A Context is a component representing a web application executing within a host. There can be as many Contexts within a Host as there are web applications. A Context wraps a web application and provides it with an environment within which to execute.

This quick look inside the componentized Tomcat 5 server will help us make sense of the steps required in embedding an instance of a Tomcat server into our own application.

Working with embedded Tomcat 5

In order to embed Tomcat within an application, we need to:
  1. create an instance of the org.apache.catalina.startup.Embedded class, this class represents an embedded instance of a Tomcat server
  2. use the helper methods of this class to create Engine, Host, Context, and Connector components and set their associated attributes
  3. start the server (and all the components)
These are the steps that we will take in the example. The example, called CatStarter, will start an instance of the Tomcat server using Java coding. The instance started will host the manager application (delivered with the embedded server), enabling the deployment of new web applications and monitoring of the server instance.

The embedded server class

The org.apache.catalina.startup.Embedded class is a class that represents the embedded server. Helper methods of this class can be used to create Tomcat 5 architectural components, set their property values, and start them.

The source code for our example, the uk.co.vsj.tc5mbed.CatStarter class, is reproduced and annotated below.

Some class definitions from org.apache.catalina sub packages need to be included since we will be working with Tomcat 5 components such as Engine, Host, Connector, and nested elements such as Logger and Realm.

package uk.co.vsj.tc5mbed;
import org.apache.catalina.*;
import org.apache.catalina.logger.*;
import org.apache.catalina.users.*;
import org.apache.catalina.realm.*;
import
	org.apache.catalina.startup.
	Embedded;

public class CatStarter {

/** Creates a new instance
	of CatStarter */
	public CatStarter() {
	}

	public static void main(
		String[] args) {

Creating an embedded server instance

Everything evolves around an instance of this embedded class, the constructor is straightforward. You can also call the constructor variant that allows you to supply a default logger and a default realm. Here, we’ll create the instance first, and then use setLogger() and setRelam() to initialize the default logger and realm.
Embedded mbedTC5 = new Embedded();
This logger is a simple file based logger. It will create log files in the directory where CatStarter is launched. Files will have a “.log” extension, datestamp, and a “vsjMbedTC5” prefix.
// set default logger and realm
FileLogger fileLog = new FileLogger();
fileLog.setDirectory(".");
fileLog.setPrefix("vsjMbedTC5");
fileLog.setSuffix(".log");
fileLog.setTimestamp(true);
mbedTC5.setLogger(fileLog);
The default memory realm is easiest to set up. This realm will use the conf/tomcat_users.xml file to obtain user, password and role information for authentication.
MemoryRealm memRealm =
	new MemoryRealm();
mbedTC5.setRealm(memRealm);

Creating an engine and setting properties

After creating the embedded server instance, you will need to create an instance of an engine using the helper createEngine() method. Here, we’ll set the name of the engine to “vsjEngine”, and the default host’s name to “vsjHost”. We will, of course, create this host next.
// create an Engine
Engine baseEngine =
	mbedTC5.createEngine();

// set Engine properties
baseEngine.setName("vsjEngine");
baseEngine.setDefaultHost("vsjHost");

Creating a host

The createHost() helper method can be used to create the one and only host inside our “vsjEngine”. This method allows you to specify the name of the host, and the application directory name. Here, we are using a relative path that points to the webapps subdirectory (a standard location to find web applications).
// create Host
Host baseHost =
	mbedTC5.createHost(
	"vsjHost", "webapps");
Note that the virtual host created is not associated with the engine until you add it as a child to the engine, using the engine’s addChild() method. This is necessary because there can be more than one engine in an embedded server instance.
// add host to Engine
baseEngine.addChild(baseHost);

Creating contexts

Next, we create contexts for running web applications inside the virtual host. This is done using the createContext() method of the server. This method takes two arguments, the first one specifying a context path, and the second specifying the directory under the Host’s appBase (typically “webapps”) that contains the associated web application.

A context with path containing an empty string “” is used as a default. This is the context that a request will be routed to if it does not match other contexts in the virtual host. Typically, this is set to the static content base directory (i.e. it will serve static HTML pages when Tomcat is used in standalone mode). Here, we set the document base of this context to the ROOT subdirectory. This is the convention observed in the default (full) distribution of the Tomcat 5 server. The Tomcat 5 default welcome page is placed under this directory.

// create root Context
Context rootCtx =
	mbedTC5.createContext("", "ROOT");
There can be many hosts inside an engine, therefore you must explicitly add the context to the host that it is associated with. Again, the addChild() method of the host is used for this purpose.
// add context to host
baseHost.addChild(rootCtx);

Creating a context for a web application

The next context we create is a context for a fully-fledged web application. To make things simple, we are creating it for the manager application that is included with the Embedded Tomcat distribution. Using manager, we can observe Tomcat server status, and deploy any web application to the host.

Here, we create the context setting the path to “/manager” and the document base directory to the “manager” sub-directory. This is the only web application included with Embedded Tomcat distribution. If you need other sample applications, such as the admin utility (for Tomcat 5 web based administration), you will need to copy them from a full (non-embedded) Tomcat 5 installation.

Since the manager application makes use of certain privileged Tomcat 5 libraries, we are setting the privileged property to true before adding the context to the host.

// create application Context
Context appCtx =
	mbedTC5.createContext(
		"/manager", "manager");
appCtx.setPrivileged(true);
// add context to host
baseHost.addChild(appCtx);
At this point, we have the engine instantiated and fully configured. However, it is not yet attached to a server, and it is not yet started. Here, we attach the engine to our server instance using the embedded server’s addEngine() method.
// add new Engine to set of
// Engine for embedded server
mbedTC5.addEngine(baseEngine);

Creating an HTTP connector

From our earlier discussion, we know that an engine without one or more associated connectors is quite useless, since no external access is possible. Here we create a Coyote HTTP 1.1 on port 8080. The helper createConnector() method of the embedded server can be used for this purpose. Supplying the “null” address indicates to the connector that it should be listening to requests on all the available IP addresses on this physical machine (some machines may have more than one network adapters).
// create Connector
Connector httpConnector =
		mbedTC5.createConnector(
			(java.net.InetAddress)
				null,8080, false);
Using the embedded server’s addConnector() helper method, we’ll add the new HTTP connector to the server. This method will associate the connector with the most recently added engine via the addEngine() method call. We can add as many connectors as we want to be associated with the engine. For example, you may want to add a connector supporting SSL, or another one supporting AJP for an Apache front-end.
// add new Connector to set of
// Connectors for embedded server,
// associated with Engine
mbedTC5.addConnector(httpConnector);

Starting the embedded server

This concludes the component setup. Finally, we are ready to start the embedded server (and all its components).
// start operation
try {
	mbedTC5.start();
} catch
		(org.apache.catalina.
			LifecycleException ex)
			{
					fileLog.log(
					"Startup failed");
					fileLog.log(
					ex.getMessage());
				}
	}
}

Testing Embedded Tomcat

To try out CatStarter, you will need to download the Embedded Tomcat 5 distribution. At the time of writing, the latest available version is Tomcat 5.0.16. This is also the first stable release of Tomcat 5.

You can find the binaries to download at jakarta.apache.org/site/binindex.cgi. Look for the 5.xx.yyEmbed.zip or the 5.xx.yyEmbed.tar.gz file under the Tomcat 5 heading (where xx and yy are sub-release numbers).

To install this embedded server distribution, simply unarchive it into a directory. Next, you should unzip the code distribution of this article into the same directory. This will create an additional code and src subdirectories, and it will add an Ant script XML file. This Ant script is used to compile and run the sample application.

Adding access privileges for the manager application

We have set up the server to use a default memory realm for authentication. This realm reads the user id, password, and role information from the tomcat_users.xml file under the conf directory. If you need authentication in production scenarios, there are alternate Tomcat realm implementations that can utilize external databases, JNDI/LDAP and JAAS, that can be substantially more robust and secure. The default tomcat_users.xml file does not provide anyone with access to the manager application. We need to edit it to add the ‘manager’ role to the tomcat user. You need to make the highlighted modification below to this file:
<tomcat-users>
	<user name="tomcat"
		password="tomcat"
		roles="tomcat,manager" />
	<user name="role1" password="tomcat"
		roles="role1" />
	<user name="both" password="tomcat"
		roles="tomcat,role1" />
</tomcat-users>
You can now use the user name “tomcat” (with password “tomcat”) to access the manager utility.

Compiling CatStarter

Make sure you have Ant 1.5.1 or later installed and working before proceeding. If you are unfamiliar with Ant, see VSJ March 2003 for an introductory article.

To compile the source code, you can use the supplied Ant script, called runmbed.xml. From the embedded Tomcat install directory, use the target “compile” via the command line:

ant -f runmbed.xml compile
After compilation, the binary class file should be placed underneath the classes directory, you may want to verify this.

Running CatStarter

At this point, you are ready to run the CatStarter application with the embedded Tomcat server. From the embedded Tomcat install directory, use the following command line to start the server:
ant -f startmbed.xml
The server should start up at this point. The console messages should be similar to Figure 4.

Figure 4
Figure 4: Starting Tomcat

Testing Embedded Tomcat

Now, start a browser instance, and point it to:
http://localhost:8080/
You should see the default Tomcat welcome page, served by the default Context that we’ve created from the ROOT sub-directory.

You can then access the manager application using the URL:

http://localhost:8080/manager/html
Tomcat will prompt for authentication (login), the default memory realm we created in CatStarter will use the information in the tomcat_user.xml file. Therefore, you can enter “tomcat” for both user name and password. Upon successful authentication, you should see the primary screen of the manager application as shown in Figure 5.

Figure 5
Figure 5: The manager
Using the manager utility, you can readily deploy more web applications, start and stop them and undeploy them. You can also observe runtime statistics from applications.

Of course, if you are using embedded Tomcat in your own code, you are more likely to include your own web application instead of this sample manager utility. However, in initial testing and debugging phases, having the manager application handy may assist in troubleshooting.

Conclusions

The ability to embed a fully-featured web tier application server into one’s own application opens up a whole new world of application possibilities. Tomcat 5’s new embedded mode empowers the developer by:
  • easing communications programming by supplying standards-based HTTP connectors supporting secured connection via SSL
  • enabling the creation of modular and maintainable code through the support of servlets, JSP, and modern development frameworks
  • providing a ready-to-go web front end for transitioning or re-purposing legacy standalone applications to a web-based ones
It is time to examine your daily Java development tasks and consider how Embedded Tomcat may add value to your own applications.


Sing Li is a consultant, system architect, open source software contributor, and freelance writer specialising in Java technology, embedded and distributed systems design. He has authored or co-authored several books on the topic, including Professional Apache Tomcat (Wrox), Professional JSP 2 (Apress), Early Adopter JXTA (Wrox), and Professional Jini (Wrox).

Nested elements, Realms and Loggers

Nested elements are non-container components in the Tomcat architecture. That is, they are typically contained within container elements. For example, a Realm is a nested element that can be placed inside an Engine (a container). Two common nested elements are Realms and Loggers. A Realm is a component that stores user id, password, and security role information. The information in a Realm is typically used by the container (and some web applications) to perform authentication and authorization. A Logger is a component that logs (stores and/or prints) error and debug messages. Loggers typically store the messages in files or display them on the monitor screens.

Starting a new JVM in the startmbed.xml Ant Script

We are using the startmbed.xml Ant script to run the example. If you examine the source XML file, you may notice that the <java> task is set to be true. This is because we want the embedded server to run in its own JVM and system process. Doing so will ensure that the different requirements for different versions of libraries (such as XML parsing libraries, etc) can be satisfied. By specifying fork=”true”, Ant will start a new system process and JVM to run CatStarter.

You might also like...

Comments

About the author

Sing Li United States

Sing Li has been writing software, and writing about software for twenty plus years. His specialities include scalable distributed computing systems and peer-to-peer technologies. He now spends ...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“We better hurry up and start coding, there are going to be a lot of bugs to fix.”