04/03/2002
In 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:
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
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
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.
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
package onjava;
import java.net.URL;
import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Deployer;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.logger.SystemOutLogger;
import org.apache.catalina.startup.Embedded;
import org.apache.catalina.Container;
public class EmbeddedTomcat {
private String path = null;
private Embedded embedded = null;
private Host host = null;
/**
* Default Constructor
*
*/
public EmbeddedTomcat() {
}
/**
* Basic Accessor setting the value of the context path
*
* @param path - the path
*/
public void setPath(String path) {
this.path = path;
}
/**
* Basic Accessor returning the value of the context path
*
* @return - the context path
*/
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
* 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 {
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
1.
The main() method begins by setting the system property to the value of the path attribute:
// Set the home directory
System.setProperty("catalina.home", getPath());
Note:
Make sure you use the value of
2.
The next step performed by this method is to create an instance of the Embedded object and set the debug level and current logger.
// Create an embedded server
embedded = new Embedded();
embedded.setDebug(5);
// print all log statments to standard error
embedded.setLogger(new SystemOutLogger());
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.
3.
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.
// Create an engine
engine = embedded.createEngine();
engine.setDefaultHost("localhost");
4.
After an Engine has been instantiated, we create an org.apache.catalina.Host object, named localhost, with a path pointing to the
// Create a default virtual host
host = embedded.createHost("localhost", getPath() +
"/webapps");
engine.addChild(host);
5.
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.
// Create the ROOT context
Context context = embedded.createContext("",
getPath() + "/webapps/ROOT");
host.addChild(context);
6.
The next step adds the Engine containing the created Host and Context to the Embedded object.
// Install the assembled container hierarchy
embedded.addEngine(engine);
7.
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
// Assemble and install a default HTTP connector
Connector connector = embedded.createConnector(null,
8080, false);
embedded.addConnector(connector);
8.
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.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
4. Make sure that your classpath includes the directory containing the compiled EmbeddedTomcat class.
5. Execute the following command:
java onjava.EmbeddedTomcat
If everything went according to plan, you should see some log statements in the console window:
HttpProcessor[8080][0] Starting background thread
HttpProcessor[8080][0] Background thread has been started
HttpProcessor[8080][1] Starting background thread
HttpProcessor[8080][1] Background thread has been started
HttpProcessor[8080][2] Starting background thread
HttpProcessor[8080][2] Background thread has been started
HttpProcessor[8080][3] Starting background thread
HttpProcessor[8080][3] Background thread has been started
HttpProcessor[8080][4] Starting background thread
HttpProcessor[8080][4] Background thread has been started
Once you see the previous text, you will be able to access the ROOT and /onjava Web applications using the following URLs:
* http://localhost:8080/
* http://localhost:8080/onjava/
Note: The onjava application that we are using throughout this article is the Web application from my previous Tomcat articles.
Up next: in the next Tomcat article, we will continue our embedded discussions by debugging a Web application that is running in our embedded container.
James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software consultancy.
Link