alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*   
 *  Copyright 1999-2004 The Apache Sofware Foundation.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.tomcat.core;

import org.apache.tomcat.util.log.Log;

import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;


/**
  ContextManager controls requests processing and server configuration.
  It maintains a list of Contexts ( web applications )  and a list of
  global modules that deal with server configuration and request processing,
  and global properties ( directories, general settings, etc ).

  The request processing is similar with Apache and other servers, with the
  addition of a "contextMap" chain that will select a tomcat-specific.
 
  

Configuration and startup

Starting tomcat involves a number of actions and states. In order to start tomcat:
  1. Create ContextManager. The server state is STATE_NEW
  2. Set properties for ContextManager ( home, debug, etc).
  3. Add the initial set of modules ( addInterceptor() ). ContextManager will call setContextManager() and then the addInterceptor() hook.
  4. Add the initial set of web applications ( Contexts ). Configuration modules can also add web applications - but no "addContext" hook will be called ( since the server is not initialized ).
  5. Call init().
    1. Init will notify all modules using the engineInit() hook. At this point the ContextManager will be in STATE_CONFIG.
    2. It'll then call addContext() hooks for each context that were added by config modules. Contexts will be in STATE_ADDED.
    3. It'll then call context.init() hooks for each context that were added by config modules. Contexts will be in STATE_READY.
    4. After all contexts are added and initialized, server will be in STATE_INIT.
    XXX Do we need finer control ? ( like initModules(), initContexts() ? )
  6. At this point the server is fully configured, but not started. The user can add/remove modules and applications - the rules are defined in "run-time configuration".
  7. Call start(). The engineStart() hook will be called, the connector modules should accept and serve requests.
  8. Call stop() to stop the server ( engineStop() hook will be called, no requests more will be accepted )
  9. Call shutdown() to clean up all resources in use by web applications and modules. The server will revert to STATE_CONFIG.
    1. contextShutdown() will be called for each application. Modules must clean any resources allocated for that context.
    2. removeContext() will be called for each application.
    3. engineShutdown() will be called for each module

Runtime configuration

XXX The only "supported" feature is adding/removing web applications. Since each module can have a set of local modules, you can change the configuration or modules for each context. Changing "global" modules may work, but it's not finalized or tested. While tomcat is running, you can temporarily disable web applications, remove and add new applications. @author James Duncan Davidson [duncan@eng.sun.com] @author James Todd [gonzo@eng.sun.com] @author Harish Prabandham @author Costin Manolache @author Hans Bergsten [hans@gefionsoftware.com] */ public class ContextManager { /** Official name and version */ public static final String TOMCAT_VERSION = "3.3.2 Final"; public static final String TOMCAT_NAME = "Tomcat Web Server"; /** System property used to set the base directory ( tomcat home ). * use -DTOMCAT_HOME= in java command line or as a System.setProperty. */ public static final String TOMCAT_HOME="tomcat.home"; /** System property used to set the install directory ( tomcat install ). * use -DTOMCAT_INSTALL= in java command line or as a System.setProperty. */ public static final String TOMCAT_INSTALL="tomcat.install"; // State /** * Server is beeing configured - modules are added. */ public static final int STATE_NEW=0; /** * Server and global modules are initialized and stable. */ public static final int STATE_CONFIG=1; /** * Web applications are configured and initialized. */ public static final int STATE_INIT=2; /** * Server is started and may process requests. */ public static final int STATE_START=3; // -------------------- local variables -------------------- private int state=STATE_NEW; // All contexts managed by the server ( can belong to different // virtual hosts ) private Vector contextsV=new Vector(); private Hashtable contexts=new Hashtable(); private int debug=0; /** Private workspace for this server */ private String workDir="work"; /** The base directory where this instance runs. * It can be different from the install directory to * allow one install per system and multiple users */ private String home; /** The directory where tomcat is installed */ private String installDir; // Server properties ( interceptors, etc ) - it's one level above "/" private Container defaultContainer; // the embedding application loader. @see getParentLoader private ClassLoader parentLoader; // the common class loader, shared by container and apps private ClassLoader commonLoader; // the container class loader, used to load all container modules private ClassLoader containerLoader; // the webapp loader, with classes shared by all webapps. private ClassLoader appsLoader; private Hashtable properties=new Hashtable(); /** * Construct a new ContextManager instance with default values. */ public ContextManager() { defaultContainer=createContainer(); defaultContainer.setContext( null ); defaultContainer.setContextManager( this ); defaultContainer.setPath( null ); // default container } // -------------------- setable properties -------------------- /** * The home of the tomcat instance - you can have multiple * users running tomcat, with a shared install directory. * * Home is used as a base for logs, webapps, local config. * Install dir ( if different ) is used to find lib ( jar * files ). * * The "tomcat.home" system property is used if no explicit * value is set. XXX */ public void setHome(String home) { this.home=home; if( home != null ) System.getProperties().put(TOMCAT_HOME, home ); } public String getHome() { return home; } /** * Get installation directory. This is used to locate * jar files ( lib ). If tomcat instance is shared, * home is used for webapps, logs, config. * If either home or install is not set, the other * is used as default. * */ public String getInstallDir() { return installDir; } public void setInstallDir( String tH ) { installDir=tH; } /** * WorkDir property - where all working files will be created */ public void setWorkDir( String wd ) { if(debug>0) log("set work dir " + wd); this.workDir=wd; } public String getWorkDir() { return workDir; } /** Debug level */ public void setDebug( int level ) { if( level != debug ) log( "Setting debug level to " + level); debug=level; } public final int getDebug() { return debug; } // XmlMapper will call setProperty(name,value) if no explicit setter // is found - it's better to use this mechanism for special // properties ( that are not generic enough and used by few specific // module ) /** Generic properties support. You can get properties like * "showDebugInfo", "randomClass", etc. */ public String getProperty( String name ) { return (String)properties.get( name ); } public void setProperty( String name, String value ) { if( name!=null && value!=null ) properties.put( name, value ); } public Hashtable getProperties() { return properties; } // -------------------- Other properties -------------------- /** Return the current state of the tomcat server. */ public final int getState() { return state; } /** Change the state, after notifying all modules about the change * Any error will be propagated - the server will not change the * state and should fail if any module can't handle that. */ public void setState( int state ) throws TomcatException { BaseInterceptor existingI[]=defaultContainer.getInterceptors(); for( int i=0; i * parentLoader -> tomcat.core.loader [ -> trusted.webapp.loader ] * -> webapp.loaders *
*/ public void setParentLoader( ClassLoader cl ) { parentLoader=cl; } public ClassLoader getParentLoader() { return parentLoader; } public void setCommonLoader( ClassLoader cl ) { commonLoader=cl; } public ClassLoader getCommonLoader() { return commonLoader; } public void setContainerLoader( ClassLoader cl ) { containerLoader=cl; } public ClassLoader getContainerLoader() { return containerLoader; } public void setAppsLoader( ClassLoader cl ) { appsLoader=cl; } public ClassLoader getAppsLoader() { return appsLoader; } /** Default container. The interceptors for this container will be called for all requests, and it will be associated with invalid requests ( where context can't be found ). */ public Container getContainer() { return defaultContainer; } public void setContainer(Container newDefaultContainer) { defaultContainer = newDefaultContainer; } /** Add a global interceptor. The addInterceptor() hook will be called. * If the module is added after STATE_CONFIG, the engineInit() hook will * be called ( otherwise we wait for init() ). * If the module is added after STATE_INIT, the addContext and * initContext hooks will be called. * If the module is added after STATE_START, the engineStart hooks will * be called. */ public void addInterceptor( BaseInterceptor ri ) throws TomcatException { ri.setContextManager( this ); // first, add the module ( addInterceptor may change the ordering ) defaultContainer.addInterceptor(ri); // second, let the module know it's added. It may look at // other module and even choose to remove himself and throw exception. ri.addInterceptor( this, null, ri ); // should know about itself // let other modules know about the new friend. BaseInterceptor existingI[]=defaultContainer.getInterceptors(); for( int i=0; i= STATE_INIT ) { Enumeration enum = getContexts(); while (enum.hasMoreElements()) { Context ctx = (Context)enum.nextElement(); try { ri.contextShutdown( ctx ); } catch( TomcatException ex ) { log( "Error shuting down context " +ctx + " to " + ri ); } } enum = getContexts(); while (enum.hasMoreElements()) { Context ctx = (Context)enum.nextElement(); try { ri.removeContext( this, ctx ); } catch( TomcatException ex ) { log( "Error removing context " +ctx + " to " + ri ); // ignore it } } } } // -------------------- Server functions -------------------- /** * Init() is called after the context manager is set up (properties) * and configured ( modules ). * * All engineInit() hooks will be called and the server will * move to state= INIT * */ public void init() throws TomcatException { if( state >= STATE_CONFIG ) // already initialized return; if(debug>0 ) log( "Tomcat init"); BaseInterceptor existingI[]=defaultContainer.getInterceptors(); for( int i=0; i= 400 ) { if( debug > 0) log( "Error reading request " + req + " " + status); handleStatus( req, res, status ); return; } status= processRequest( req ); if( status != 0 ) { if( debug > 0) log("Error mapping the request " + req + " " + status); handleStatus( req, res, status ); return; } if( req.getHandler() == null ) { status=404; if( debug > 0) log("No handler for request " + req + " " + status); handleStatus( req, res, status ); return; } Container sct=req.getSecurityContext(); if(sct != null ) { status=0; BaseInterceptor reqI[]; // assert( req.getContext()!=null ) - checked in processRequest reqI = req.getContext().getContainer(). getInterceptors(Container.H_authorize); // Call all authorization callbacks. for( int i=0; i< reqI.length; i++ ) { status = reqI[i].authorize( req, res, sct.getRoles() ); if ( status != BaseInterceptor.DECLINED ) { break; } } } if( status != BaseInterceptor.OK ) { if( debug > 0) log("Unauthorized " + req + " " + status); if( status==BaseInterceptor.DECLINED ) status=401; // unauthorized handleStatus( req, res, status ); return; } req.getHandler().service(req, res); } catch (Throwable t) { handleError( req, res, t ); } } /** Will find the Handler for a servlet, assuming we already have * the Context. This is also used by Dispatcher and getResource - * where the Context is already known. * * This method will only map the request, it'll not do authorization * or authentication. */ public int processRequest( Request req ) { if(debug>9) log("Before processRequest(): "+req.toString()); int status=0; BaseInterceptor ri[]; ri=defaultContainer.getInterceptors(Container.H_postReadRequest); for( int i=0; i< ri.length; i++ ) { status=ri[i].postReadRequest( req ); if( status!=0 ) return status; } ri=defaultContainer.getInterceptors(Container.H_contextMap); for( int i=0; i< ri.length; i++ ) { status=ri[i].contextMap( req ); if( status!=0 ) return status; } req.setState(Request.STATE_CONTEXT_MAPPED ); if( req.getContext() == null) { req.setAttribute("javax.servlet.error.message", "No context found"); } if( req.getContext().getState() != Context.STATE_READY ) { // the context is not fully initialized. req.setAttribute("javax.servlet.error.message", "Context " + req.getContext() + " not ready"); // return error code - the caller will handle it return 503; } ri=req.getContext().getContainer(). getInterceptors(Container.H_requestMap); for( int i=0; i< ri.length; i++ ) { if( debug > 1 ) log( "RequestMap " + ri[i] ); status=ri[i].requestMap( req ); if( status!=0 ) return status; } req.setState(Request.STATE_MAPPED ); if(debug>9) log("After processRequest(): "+req.toString()); return 0; } // -------------------- Sub-Request mechanism -------------------- /** Create a new sub-request in a given context, set the context "hint" * This is a particular case of sub-request that can't get out of * a context ( and we know the context before - so no need to compute it * again) * * Note that session and all stuff will still be computed. */ public Request createRequest( Context ctx, String urlPath ) { // assert urlPath!=null // deal with paths with parameters in it String contextPath=ctx.getPath(); String origPath=urlPath; // append context path if( !"".equals(contextPath) && !"/".equals(contextPath)) { if( urlPath.startsWith("/" ) ) urlPath=contextPath + urlPath; else urlPath=contextPath + "/" + urlPath; } else { // root context if( !urlPath.startsWith("/" ) ) urlPath= "/" + urlPath; } if( debug >4 ) log("createRequest " + origPath + " " + urlPath ); Request req= createSubRequest( ctx.getHost(), urlPath ); req.setContext( ctx ); return req; } /** Create a new sub-request, deal with query string */ private Request createSubRequest( String host, String urlPath ) { String queryString=null; int i = urlPath.indexOf("?"); int len=urlPath.length(); if (i>-1) { if(i= MAX_NOTES ) throw new TomcatException( "Too many notes "); // make sure the note id is > RESERVED if( noteId[noteType] < RESERVED ) noteId[noteType]=RESERVED; noteName[noteType][ noteId[noteType] ]=name; return noteId[noteType]++; } public String getNoteName( int noteType, int noteId ) { return noteName[noteType][noteId]; } // -------------------- Per-server notes -------------------- private Object notes[]=new Object[MAX_NOTES]; public final void setNote( int pos, Object value ) { notes[pos]=value; } public final Object getNote( int pos ) { return notes[pos]; } public Object getNote( String name ) throws TomcatException { int id=getNoteId( SERVER_NOTE, name ); return getNote( id ); } public void setNote( String name, Object value ) throws TomcatException { int id=getNoteId( SERVER_NOTE, name ); setNote( id, value ); } // -------------------- Logging and debug -------------------- // default, is going to console until replaced (unless aleady configured) private Log loghelper = Log.getLog("org/apache/tomcat/core", "ContextManager"); /** * So other classes can piggyback on the context manager's log * stream. **/ public Log getLog() { return loghelper; } public void setLog(Log log) { loghelper=log; } public void log(String msg) { loghelper.log(msg); } public void log(String msg, Throwable t) { loghelper.log(msg, t); } public void log(String msg, int level) { loghelper.log(msg, level); } public void log(String msg, Throwable t, int level) { loghelper.log(msg, t, level); } // -------------------- Factories -------------------- public Context createContext() { return new Context(); } public Request createRequest() { Request req=new Request(); //Response res=new Response(); //initRequest( req, res ); return req; } public Response createResponse(Request req) { //return req.getResponse(); return new Response(); } public Container createContainer() { return new Container(); } public OutputBuffer createOutputBuffer() { return new OutputBuffer(); } public OutputBuffer createOutputBuffer(int size) { return new OutputBuffer(size); } public ServerSession createServerSession() { ServerSession ss=new ServerSession(); ss.setContextManager( this ); return ss; } }
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.