|
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.http.MimeMap;
import org.apache.tomcat.util.log.Log;
import java.io.File;
import java.net.FileNameMap;
import java.net.URL;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
/**
* Context represent a Web Application as specified by Servlet Specs.
* The implementation is a repository for all the properties
* defined in web.xml and tomcat specific properties.
*
* This object has many properties, but doesn't do anything special
* except simple cashing.
*
* You need to set at least "path" and "base" before adding a
* context to a server. You can also set any other properties.
*
* At addContext() stage log and paths will be "fixed" based on
* context manager settings.
*
* At initContext() stage, web.xml will be read and all other
* properties will be set. WebXmlReader must be the first
* module in initContext() chain.
*
* @author James Duncan Davidson [duncan@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
* @author Jason Hunter [jch@eng.sun.com]
* @author Harish Prabandham
* @author costin@dnt.ro
* @author Gal Shachor shachor@il.ibm.com
*/
public class Context {
// -------------------- Constants --------------------
// Proprietary attribute names for contexts - defined
// here so we can document them ( will show in javadoc )
/** Private tomcat attribute names
*/
public static final String ATTRIB_PREFIX="org.apache.tomcat";
/** Protection domain to be used to create new classes in this context.
This is used only by JspServlet, and should be avoided -
the preferred mechanism is to use the default policy file
and URLClassLoader.
*/
public static final String ATTRIB_PROTECTION_DOMAIN=
"org.apache.tomcat.protection_domain";
/** This attribute will return the real context (
* org.apache.tomcat.core.Context).
* Only "trusted" applications will get the value. Null if the application
* is not trusted.
*/
public static final String ATTRIB_REAL_CONTEXT="org.apache.tomcat.context";
/** Context is new, possibly not even added to server.
ContextManager is not set, and most of the paths are not fixed
*/
public static final int STATE_NEW=0;
/** Context was added to the server, but contextInit() is not
called. Paths are not set yet, the only valid information is
the contextURI.
*/
public static final int STATE_ADDED=1;
/**
Relative paths are fixed, based
on server base, and CM is set.
If a request arives for this context, an error message should be
displayed ( "application is temporary disabled" )
*/
public static final int STATE_DISABLED=2;
/** Context is initialized and ready to serve. We have all mappings
and configs from web.xml.
*/
public static final int STATE_READY=3;
// -------------------- internal properties
private String name;
// context "id"
private String path = "";
// directory where the context files are located.
private String docBase;
// Absolute path to docBase if file-system based
private String absPath;
private Hashtable properties=new Hashtable();
private int state=STATE_NEW;
// internal state / related objects
private ContextManager contextM;
private Object contextFacade;
// print debugging information
private int debug=0;
// enable reloading
private boolean reloadable=true;
// XXX Use a better repository
private Hashtable attributes = new Hashtable();
// directory with write-permissions for servlets
private File workDir;
// Servlets loaded by this context( String->ServletWrapper )
private Hashtable servlets = new Hashtable();
// Initial properties for the context
private Hashtable initializationParameters = new Hashtable();
// WelcomeFiles
private Vector welcomeFilesV=new Vector();
// cached for faster access
private String welcomeFiles[] = null;
// Defined error pages.
private Hashtable errorPages = new Hashtable();
// mime mappings
private MimeMap mimeTypes = new MimeMap();
// Default session time out
private int sessionTimeOut = -1;
private boolean isDistributable = false;
// Maps specified in web.xml ( String url -> Handler )
private Hashtable mappings = new Hashtable();
// Security constraints ( String url -> Container )
private Hashtable constraints=new Hashtable();
// All url patterns ( url_pattern -> properties )
private Hashtable containers=new Hashtable();
// Container used if no match is found
// Also contains the special properties for
// this context.
private Container defaultContainer = null;
// Authentication properties
private String authMethod;
private String realmName;
private String formLoginPage;
private String formErrorPage;
// Servlet-Engine header ( default set by Servlet facade)
private String engineHeader = null;
// Virtual host name ( null if default )
private String vhost=null;
// Virtual host ip address (if vhost isn't an address)
private String vhostip=null;
// vhost aliases
private Vector vhostAliases=new Vector();
// are servlets allowed to access internal objects?
private boolean trusted=false;
private static Log defaultContextLog=Log.getLog("org/apache/tomcat/core", "Context");
// log channels for context and servlets
private Log loghelper = defaultContextLog;
private Log loghelperServlet;
// servlet API implemented by this Context
private String apiLevel="2.2";
// class loader for this context
private ClassLoader classLoader;
// Vector, using URLClassLoader conventions
private Vector classPath=new Vector();
// true if a change was detected and this context
// needs reload
private boolean reload;
// -------------------- from web.xml --------------------
// Those properties are not directly used in context
// operation, we just store them.
private String description = null;
private String icon=null;
// taglibs
private Hashtable tagLibs=new Hashtable();
// Env entries
private Hashtable envEntryTypes=new Hashtable();
private Hashtable envEntryValues=new Hashtable();
private int attributeInfo;
// -------------------- Constructor --------------------
public Context() {
defaultContainer=new Container();
defaultContainer.setContext( this );
defaultContainer.setPath( null ); // default container
}
// -------------------- Active methods --------------------
// The main role of Context is to store the many properties
// that a web application have.
// There are only few methods here that actually do something
// ( and we try to keep the object "passive" - it is already
// full of properties, no need to make it to complicated.
/** Add a new container. Container define special properties for
a set of urls.
*/
public void addContainer( Container ct )
throws TomcatException
{
// Notify interceptors that a new container is added
BaseInterceptor cI[]=defaultContainer.getInterceptors();
for( int i=0; i< cI.length; i++ ) {
cI[i].addContainer( ct );
}
}
/**
* Maps a named servlet to a particular path or extension.
*
* If the named servlet is unregistered, it will be added
* and subsequently mapped. The servlet can be set by intereceptors
* during addContainer() hook.
*
* If the mapping already exists it will be replaced by the new
* mapping.
* @deprecated Use addContainer
*/
public void addServletMapping(String path, String servletName)
throws TomcatException
{
if( mappings.get( path )!= null) {
log( "Removing duplicate " + path + " -> " + mappings.get(path) );
mappings.remove( path );
Container ct=(Container)containers.get( path );
removeContainer( ct );
}
// sw may be null - in wich case interceptors may
// set it
Handler sw = getServletByName(servletName);
Container map=contextM.createContainer();
map.setContext( this );
map.setHandlerName( servletName );
map.setHandler( sw );
map.setPath( path );
// Notify interceptors that a new container is added
BaseInterceptor cI[]=defaultContainer.getInterceptors();
for( int i=0; i< cI.length; i++ ) {
cI[i].addContainer( map );
}
sw = getServletByName(servletName);
if (sw == null) {
// web.xml validation - a mapping with no servlet rollback
removeContainer( map );
throw new TomcatException( "Mapping with invalid servlet " +
path + " " + servletName );
}
containers.put( path, map );
mappings.put( path, sw );
if( debug > 4 )
log( "Map " + path + " -> " + mappings.get(path));
}
/** Will add a new security constraint:
For all paths:
if( match(path) && match(method) && match( transport ) )
then require("roles")
This is equivalent with adding a Container with the path,
method and transport. If the container will be matched,
the request will have to pass the security constraints.
@deprecated Use addContainer
*/
public void addSecurityConstraint( String path[], String methods[],
String roles[], String transport)
throws TomcatException
{
for( int i=0; i< path.length; i++ ) {
Container ct=contextM.createContainer();
ct.setContext( this );
ct.setTransport( transport );
ct.setRoles( roles );
ct.setPath( path[i] );
ct.setMethods( methods );
// XXX check if exists, merge if true.
constraints.put( path[i], ct );
//contextM.addSecurityConstraint( this, path[i], ct);
// Notify interceptors that a new container is added
BaseInterceptor cI[]=ct.getInterceptors();
for( int j=0; j< cI.length; j++ ) {
cI[j].addContainer( ct );
}
}
}
/** Check if "special" attributes can be used by
* user application. Only trusted apps can get
* access to the implementation object.
*/
public boolean allowAttribute( String name ) {
// check if we can access this attribute.
if( isTrusted() ) return true;
log( "Attempt to access internal attribute in untrusted app",
null, Log.ERROR);
return false;
}
// -------------------- Passive properties --------------------
// Everything bellow is just get/set
// for web application properties
// --------------------
// -------------------- Facade --------------------
/** Every context is associated with a facade. We don't know the exact
type of the facade, as a Context can be associated with a 2.2 ...
ServletContext.
*/
public Object getFacade() {
return contextFacade;
}
public void setFacade(Object obj) {
if(contextFacade!=null ) {
log( "Changing facade " + contextFacade + " " +obj);
}
contextFacade=obj;
}
// -------------------- Settable context properties --------------------
/** Returned the main server ( servlet container )
*/
public final ContextManager getContextManager() {
return contextM;
}
/** This method is called when the Context is added
to a server. Some of the Context properties
depend on the ContextManager, and will be adjusted
by interceptors ( DefaultCMSetter )
*/
public void setContextManager(ContextManager cm) {
if( contextM != null ) return;
contextM=cm;
if( defaultContainer==null ) {
defaultContainer=contextM.createContainer();
defaultContainer.setContext( this );
defaultContainer.setPath( null ); // default container
}
try {
attributeInfo=cm.getNoteId(ContextManager.REQUEST_NOTE,
"req.attribute");
} catch( TomcatException ex ) {
ex.printStackTrace();
}
}
protected void setContextManager1(ContextManager cm) {
contextM=cm;
}
/** Default container for this context.
*/
public Container getContainer() {
return defaultContainer;
}
public final int getState() {
return state;
}
/** Move the context in a different state.
Can be called only from tomcat.core.ContextManager.
( package access )
*/
public void setState( int state )
throws TomcatException
{
// call state callback
BaseInterceptor csI[]=getContainer().getInterceptors();
for( int i=0; i< csI.length; i++ ) {
csI[i].contextState( this, state );
}
// transition from NEW to ADDED. The system is stable, we
// can init our own local modules
if(this.state==STATE_NEW && state==STATE_ADDED ) {
// we are just beeing added
BaseInterceptor cI[]=getContainer().getInterceptors();
for( int i=0; i< cI.length; i++ ) {
if( cI[i].getContext() != this )
continue; // not ours, don't have to initialize it.
try {
cI[i].addInterceptor( contextM, this , cI[i] );
BaseInterceptor existingI[]=defaultContainer.
getInterceptors();
for( int j=0; jThis method must be called
* before any requests are handled by this context. It will be called
* after the context was added, typically when the engine starts
* or after the admin adds a new context.
*
* After this call, the context will be in READY state and will
* be able to server requests.
*
* @exception if any interceptor throws an exception the error
* will prevent the context from becoming READY
*/
public void init() throws TomcatException {
if( state==STATE_READY ) {
log( "Already initialized " );
return;
}
// make sure we see all interceptors added so far
getContainer().resetInterceptorCache(Container.H_engineInit);
// no action if ContextManager is not initialized
if( contextM==null ||
contextM.getState() == ContextManager.STATE_NEW ) {
log( "ERROR: ContextManager is not yet initialized ");
return;
}
BaseInterceptor cI[]=getContainer().getInterceptors();
for( int i=0; i< cI.length; i++ ) {
cI[i].contextInit( this );
}
// Only if all init methods succeed an no ex is thrown
setState( Context.STATE_READY );
}
/** Stop the context. After the call the context will be disabled,
( DISABLED state ) and it'll not be able to serve requests.
The context will still be available and can be enabled later
by calling initContext(). Requests mapped to this context
should report a "temporary unavailable" message.
All servlets will be destroyed, and resources held by the
context will be freed.
The contextShutdown callbacks can wait until the running serlvets
are completed - there is no way to force the shutdown.
*/
public void shutdown() throws TomcatException {
setState( Context.STATE_DISABLED ); // called before
// the hook, no more request should be allowed in unstable state
BaseInterceptor cI[]= getContainer().getInterceptors();
for( int i=0; i< cI.length; i++ ) {
cI[i].contextShutdown( this );
}
}
// -------------------- Basic properties --------------------
/** Return a name ( id ) for this context. Currently it's composed
from the virtual host and path, or set explicitely by the app.
*/
public String getName() {
if(name!=null ) return name;
name=(vhost==null ? "DEFAULT:" : vhost + ":" ) +
("".equals(path) ? "/ROOT" : path);
return name;
}
public void setName( String s ) {
name=s;
}
/** Base URL for this context
*/
public String getPath() {
return path;
}
/** Base URL for this context
*/
public void setPath(String path) {
// config believes that the root path is called "/",
//
if( "/".equals(path) )
path="";
this.path = path;
loghelper=Log.getLog("org/apache/tomcat/core",
"Ctx("+ getId() +") ");
name=null;
}
/**
* Make this context visible as part of a virtual host.
* The host is the "default" name, it may also have aliases.
*/
public void setHost( String h ) {
vhost=h;
name=null;
}
/**
* Return the virtual host name, or null if we are in the
* default context
*/
public String getHost() {
return vhost;
}
/**
* Set the virtual host ip address.
*/
public final void setHostAddress( String ip ) {
vhostip=ip;
}
/**
* Return the virtual host ip address.
*/
public final String getHostAddress() {
return vhostip;
}
/** DocBase points to the web application files.
*
* There is no restriction on the syntax and content of DocBase,
* it's up to the various modules to interpret this and use it.
* For example, to serve from a war file you can use war: protocol,
* and set up War interceptors.
*
* "Basic" tomcat treats it as a file ( either absolute or relative to
* the CM home ).
*/
public void setDocBase( String docB ) {
this.docBase=docB;
}
public String getDocBase() {
return docBase;
}
/** Return the absolute path for the docBase, if we are file-system
* based, null otherwise.
*/
public String getAbsolutePath() {
return absPath;
}
/** Set the absolute path to this context.
* If not set explicitely, it'll be docBase ( if absolute )
* or relative to "home" ( cm.getHome() ).
* DefaultCMSetter will "fix" the path.
*/
public void setAbsolutePath(String s) {
absPath=s;
}
public String getProperty( String n ) {
return (String)properties.get( n );
}
public void setProperty( String n, String v ) {
properties.put( n, v );
}
// -------------------- Tomcat specific properties --------------------
public void setReloadable( boolean b ) {
reloadable=b;
}
/** Should we reload servlets ?
*/
public boolean getReloadable() {
return reloadable;
}
// -------------------- API level --------------------
/** The servlet API variant that will be used for requests in this
* context
*/
public void setServletAPI( String s ) {
if( s==null ) return;
if( s.endsWith("23") || s.endsWith("2.3")) {
apiLevel="2.3";
} else if( ( s.endsWith("22") || s.endsWith("2.2")) ) {
apiLevel="2.2";
} else {
log( "Unknown API " + s );
}
}
public String getServletAPI() {
return apiLevel;
}
// -------------------- Welcome files --------------------
/** Return welcome files defined in web.xml or the
* defaults, if user doesn't define any
*/
public String[] getWelcomeFiles() {
if( welcomeFiles==null ) {
welcomeFiles=new String[ welcomeFilesV.size() ];
for( int i=0; i< welcomeFiles.length; i++ ) {
welcomeFiles[i]=(String)welcomeFilesV.elementAt( i );
}
}
return welcomeFiles;
}
/** Add an welcome file.
*/
public void addWelcomeFile( String s) {
if (s == null ) return;
s=s.trim();
if(s.length() == 0)
return;
welcomeFiles=null; // invalidate the cache
welcomeFilesV.addElement( s );
}
// -------------------- Init parameters --------------------
public String getInitParameter(String name) {
return (String)initializationParameters.get(name);
}
public void addInitParameter( String name, String value ) {
initializationParameters.put(name, value );
}
public Enumeration getInitParameterNames() {
return initializationParameters.keys();
}
// -------------------- Attributes --------------------
/** Return an attribute value.
* "Special" attributes ( defined org.apache.tomcat )
* are computed
*
*/
public Object getAttribute(String name) {
Object o=attributes.get( name );
if( o!=null ) return o;
if(name.equals(ATTRIB_REAL_CONTEXT)) {
if( ! allowAttribute(name) ) {
return null;
}
return this;
}
// interceptors may return special attributes
BaseInterceptor reqI[]= this.getContainer().
getInterceptors(Container.H_getInfo);
for( int i=0; i< reqI.length; i++ ) {
o=reqI[i].getInfo( this, null, attributeInfo, name );
if ( o != null ) {
break;
}
}
return o;
}
public Enumeration getAttributeNames() {
return attributes.keys();
}
/**
* XXX Use callbacks !!
*/
public void setAttribute(String name, Object object) {
attributes.put(name, object);
}
public void removeAttribute(String name) {
attributes.remove(name);
}
// -------------------- Web.xml properties --------------------
/** Add a taglib declaration for this context
*/
public void addTaglib( String uri, String location ) {
tagLibs.put( uri, location );
}
public String getTaglibLocation( String uri ) {
return (String)tagLibs.get(uri );
}
public Enumeration getTaglibs() {
return tagLibs.keys();
}
/** Add Env-entry to this context
*/
public void addEnvEntry( String name,String type, String value, String description ) {
log("Add env-entry " + name + " " + type + " " + value + " " +description );
if( name==null || type==null) throw new IllegalArgumentException();
envEntryTypes.put( name, type );
if( value!=null)
envEntryValues.put( name, value );
}
public String getEnvEntryType(String name) {
return (String)envEntryTypes.get(name);
}
public String getEnvEntryValue(String name) {
return (String)envEntryValues.get(name);
}
public Enumeration getEnvEntries() {
return envEntryTypes.keys();
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public void setIcon( String icon ) {
this.icon=icon;
}
public boolean isDistributable() {
return this.isDistributable;
}
public void setDistributable(boolean isDistributable) {
this.isDistributable = isDistributable;
}
public int getSessionTimeOut() {
return this.sessionTimeOut;
}
public void setSessionTimeOut(int sessionTimeOut) {
this.sessionTimeOut = sessionTimeOut;
}
// -------------------- Mime types --------------------
public FileNameMap getMimeMap() {
return mimeTypes;
}
public void addContentType( String ext, String type) {
mimeTypes.addContentType( ext, type );
}
// -------------------- Error pages --------------------
public String getErrorPage(int errorCode) {
return getErrorPage(String.valueOf(errorCode));
}
public void addErrorPage( String errorType, String value ) {
this.errorPages.put( errorType, value );
}
public String getErrorPage(String errorCode) {
return (String)errorPages.get(errorCode);
}
// -------------------- Auth --------------------
/** Authentication method, if any specified
*/
public String getAuthMethod() {
return authMethod;
}
/** Realm to be used
*/
public String getRealmName() {
return realmName;
}
public String getFormLoginPage() {
return formLoginPage;
}
public String getFormErrorPage() {
return formErrorPage;
}
public void setFormLoginPage( String page ) {
formLoginPage=page;
}
public void setFormErrorPage( String page ) {
formErrorPage=page;
}
public void setLoginConfig( String authMethod, String realmName,
String formLoginPage, String formErrorPage)
{
this.authMethod=authMethod;
this.realmName=realmName;
this.formLoginPage=formLoginPage;
this.formErrorPage=formErrorPage;
}
// -------------------- Mappings --------------------
public Enumeration getContainers() {
return containers.elements();
}
/** Return an enumeration of Strings, representing
* all URLs ( relative to this context ) having
* associated properties ( handlers, security, etc)
*/
public Enumeration getContainerLocations() {
return containers.keys();
}
/** Return the container ( properties ) associated
* with a path ( relative to this context )
*/
public Container getContainer( String path ) {
return (Container)containers.get(path);
}
/** Remove a container
*/
public void removeContainer( Container ct )
throws TomcatException
{
containers.remove(ct.getPath());
// notify modules that a container was removed
BaseInterceptor cI[]=ct.getInterceptors();
for( int i=0; i< cI.length; i++ ) {
cI[i].removeContainer( ct );
}
}
// -------------------- Servlets management --------------------
/**
* Add a servlet. Servlets are mapped by name.
* This method is used to maintain the list of declared
* servlets, that can be used for mappings.
* @deprecated. Use addHandler()
*/
public void addServlet(Handler wrapper)
throws TomcatException
{
addHandler( wrapper );
}
/**
* Add a servlet. Servlets are mapped by name.
* This method is used to maintain the list of declared
* servlets, that can be used for mappings.
*/
public void addHandler(Handler wrapper)
throws TomcatException
{
// wrapper.setContext( this );
wrapper.setState( Handler.STATE_ADDED );
String name=wrapper.getName();
wrapper.setContextManager( contextM );
// check for duplicates
if (getServletByName(name) != null) {
log("Removing duplicate servlet " + name + " " + wrapper);
removeServletByName(name);
}
if( debug>5 ) log( "Adding servlet=" + name + "-> "
+ wrapper);
BaseInterceptor cI[]=defaultContainer.getInterceptors();
for( int i=0; i< cI.length; i++ ) {
cI[i].addHandler( wrapper );
}
servlets.put(name, wrapper);
}
public void removeHandler( Handler handler )
throws TomcatException
{
if( handler==null ) return;
BaseInterceptor cI[]=defaultContainer.getInterceptors();
for( int i=0; i< cI.length; i++ ) {
cI[i].removeHandler( handler );
}
servlets.remove( handler.getName());
handler.setState( Handler.STATE_NEW );
}
/** Remove the servlet with a specific name
*/
public void removeServletByName(String servletName)
throws TomcatException
{
Handler h=getServletByName( servletName );
removeHandler( h );
}
/**
*
*/
public Handler getServletByName(String servletName) {
return (Handler)servlets.get(servletName);
}
/** Return all servlets registered with this Context
* The elements will be of type Handler ( or sub-types )
*/
public Enumeration getServletNames() {
return servlets.keys();
}
// -------------------- Loading and sessions --------------------
/** The current class loader. This value may change if reload
* is used, you shouldn't cache the result
*/
public ClassLoader getClassLoader() {
return classLoader;
}
public void setClassLoader(ClassLoader cl ) {
classLoader=cl;
}
// -------------------- ClassPath --------------------
public void addClassPath( URL url ) {
classPath.addElement( url);
}
/** Returns the full classpath - concatenation
of ContextManager classpath and locally specified
class path
*/
public URL[] getClassPath() {
if( classPath==null ) return new URL[0];
URL serverCP[]=new URL[0]; //contextM.getServerClassPath();
URL urls[]=new URL[classPath.size() + serverCP.length];
int pos=0;
for( int i=0; i
|