/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.core;
import org.apache.tomcat.util.hooks.Hooks;
import java.util.Hashtable;
import java.util.Enumeration;
// XXX better names: Location, URLPattern,
/**
* A group of resources, with some common properties.
* Container is similar with Apache "dir_conf" structue.
*
* Each Context has a default Container and one container for
* each URL property ( mapping handlers and security constraints ).
*
* The ContextManager has a defaultContainer containing global
* properties.
*
* Each time a container is added to a Context, addContainer() hook is
* called to notify all modules of a new URL property.
*
* Modules that implement contextMap/requestMap and security constraints
* ( authenticate/authorize hooks ) will construct specialized data
* structures.
* You can associate trees, hashtables or other data types with the
* context using notes - no application/module should assume any
* particular structure is in used, the user can choose any mapper.
* See SimpleMapper1 for an example of such structures.
*
* A container will be selected by best-matching a request using the
* alghoritms described in the servlet API.
*/
public class Container implements Cloneable{
/* It is not yet finalized - it is possible to use more
* "rules" for matching ( if future APIs will define that ).
* You can use notes or attributes to extend the model -
* the attributes that are defined and have get/set methods
* are the one defined in the API and with wide use.
*/
// The "controler"
private ContextManager contextM;
// The webapp including this container, if any
private Context context;
// The type of the mapping
public static final int UNKNOWN_MAP=0;
public static final int PATH_MAP=1;
public static final int PREFIX_MAP=2;
public static final int EXTENSION_MAP=3;
public static final int DEFAULT_MAP=4;
private int mapType=0;
// Common map parameters ( path prefix, ext, etc)
private String transport;
private String path;
private String proto;
private String vhosts[];
// Container attributes - it's better to use
// notes, as the access time is much smaller
private Hashtable attributes = new Hashtable();
/** The handler associated with this container.
*/
private Handler handler;
private String handlerName;
/** Security constraints associated with this Container
*/
private String roles[]=null;
private String methods[]=null;
private boolean special=false;
public Container() {
initHooks();
}
/** Get the context manager
*/
public ContextManager getContextManager() {
if( contextM==null && context==null ) {
/* assert */
throw new RuntimeException( "Assert: container.contextM==null" );
}
if( contextM==null )
contextM=context.getContextManager();
return contextM;
}
public void setContextManager(ContextManager cm) {
contextM=cm;
}
/** Set the context, if this container is part of a web application.
* Right now all container in use have a context.
*/
public void setContext( Context ctx ) {
this.context=ctx;
}
/** The parent web application, if any.
*/
public Context getContext() {
return context;
}
public void setSpecial( boolean b ) {
special=b;
}
public boolean isSpecial() {
return special;
}
// -------------------- Mapping LHS --------------------
/** Return the type of the mapping ( extension, prefix, default, etc)
*/
public int getMapType() {
if( mapType!=0) return mapType;
// What happens with "" or null ?
// XXX Which one is default servlet ? API doesn't say,
// but people expect it to work.
if( path==null ||
path.equals("" ) ||
path.equals( "/")) {
mapType=DEFAULT_MAP;
} else if (path.startsWith("/") &&
path.endsWith("/*")) {
mapType=PREFIX_MAP;
} else if (path.startsWith("*.")) {
mapType=EXTENSION_MAP;
} else {
mapType=PATH_MAP;
}
return mapType;
}
/** The mapping string that creates this Container.
* Not that this is an un-parsed string, like a regexp.
*/
public void setPath( String path ) {
// XXX use a better name - setMapping for example
if( path==null)
this.path=""; // default mapping
else
this.path=path.trim();
}
/** Return the path
*/
public String getPath() {
return path;
}
/** Set the protocol - if it's set it will be used
* in mapping
*/
public void setProtocol( String protocol ) {
this.proto=protocol;
}
/** Protocol matching. With Servlet 2.2 the protocol
* can be used only with security mappings, not with
* handler ( servlet ) maps
*/
public String getProtocol() {
return proto;
}
/** The transport - another component of the matching.
* Defined only for security mappings.
*/
public void setTransport(String transport ) {
this.transport=transport;
}
/** The transport - another component of the matching.
* Defined only for security mappings.
*/
public String getTransport() {
return transport;
}
/** Any alias that can match a particular vhost
*/
public String[] getVhosts() {
return vhosts;
}
/** Any alias that can match a particular vhost
*/
public void setVhosts(String vhosts[]) {
this.vhosts=vhosts;
}
/** If not null, this container can only be accessed by users
* in roles.
*/
public String []getMethods() {
return methods;
}
/** If not null, this container can only be accessed by users
in roles.
*/
public void setMethods( String m[] ) {
this.methods=m;
}
// -------------------- Mapping RHS --------------------
public Handler getHandler() {
return handler;
}
/** The handler ( servlet ) for this container
*/
public void setHandler(Handler h) {
handler=h;
}
public void setHandlerName(String hn) {
handlerName=hn;
}
/** The handler name for this container.
* @return null if no handler is defined for this
* container ( this container defines only
* security or other type of properties, but
* not a handler )
*/
public String getHandlerName() {
if( handlerName != null )
return handlerName;
if( handler != null )
return handler.getName();
return null;
}
/** If not null, this container can only be accessed by users
* in roles.
*/
public String []getRoles() {
return roles;
}
/** If not null, this container can only be accessed by users
in roles.
*/
public void setRoles( String roles[] ) {
this.roles=roles;
}
/** Per container attributes. Not used - can be removed
* ( it's here for analogy with the other components )
*/
public Object getAttribute(String name) {
return attributes.get(name);
}
/** Per container attributes. Not used - can be removed
* ( it's here for analogy with the other components )
*/
public Enumeration getAttributeNames() {
return attributes.keys();
}
/** Per container attributes. Not used - can be removed
* ( it's here for analogy with the other components )
*/
public void setAttribute(String name, Object object) {
attributes.put(name, object);
}
/** Per container attributes. Not used - can be removed
* ( it's here for analogy with the other components )
*/
public void removeAttribute(String name) {
attributes.remove(name);
}
// -------------------- Utils --------------------
/** Print a short string describing the mapping
*/
public String toString() {
StringBuffer sb=new StringBuffer();
sb.append( "Ct (" );
sb.append(path ).append( " " );
if( handler!= null) sb.append( handler.toString() );
if( roles!=null) {
sb.append(" Roles: ");
for( int i=0; i< roles.length; i++ )
sb.append(" ").append( roles[i]);
}
sb.append( " )");
return sb.toString();
}
public Container getClone() {
try {
return (Container)this.clone();
} catch( CloneNotSupportedException ex ) {
return this;
}
}
// -------------------- Per-Container "notes"
Object notes[]=new Object[ContextManager.MAX_NOTES];
/** See ContextManager comments.
*/
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=getContextManager().getNoteId( ContextManager.CONTAINER_NOTE,
name );
return getNote( id );
}
public void setNote( String name, Object value ) throws TomcatException {
int id=getContextManager().getNoteId( ContextManager.CONTAINER_NOTE,
name );
setNote( id, value );
}
// -------------------- Interceptors --------------------
public static final int H_postReadRequest=0;
public static final int H_requestMap=1;
public static final int H_contextMap=2;
public static final int H_authenticate=3;
public static final int H_authorize=4;
public static final int H_preService=5;
public static final int H_beforeBody=6;
public static final int H_findSession=7;
public static final int H_sessionState=8;
public static final int H_beforeCommit=9;
public static final int H_afterBody=10;
public static final int H_postService=11;
public static final int H_postRequest=12;
public static final int H_handleError=13;
public static final int H_getInfo=14;
public static final int H_setInfo=15;
public static final int H_engineInit=16;
public static final int H_COUNT=17;
private Hooks hooks=new Hooks();
private BaseInterceptor hooksCache[][]=null;
private BaseInterceptor allHooksCache[]=null;
private void initHooks() {
hooks.registerHook( "postReadRequest", H_postReadRequest );
hooks.registerHook( "requestMap", H_requestMap );
hooks.registerHook( "contextMap", H_contextMap );
hooks.registerHook( "authenticate", H_authenticate );
hooks.registerHook( "authorize", H_authorize );
hooks.registerHook( "preService", H_preService );
hooks.registerHook( "beforeBody", H_beforeBody );
hooks.registerHook( "findSession", H_findSession );
hooks.registerHook( "sessionState", H_sessionState );
hooks.registerHook( "beforeCommit", H_beforeCommit );
hooks.registerHook( "afterBody", H_afterBody );
hooks.registerHook( "postService", H_postService );
hooks.registerHook( "postRequest", H_postRequest );
hooks.registerHook( "handleError", H_handleError );
hooks.registerHook( "getInfo", H_getInfo );
hooks.registerHook( "setInfo", H_setInfo );
hooks.registerHook( "engineInit", H_engineInit );
}
public Hooks getHooks() {
return hooks;
}
/** Add the interceptor to all the hook chains it's interested
* in
*/
public void addInterceptor( BaseInterceptor bi ) {
bi.setContext( getContext() );
int status=bi.registerHooks( hooks, contextM, context );
if( status!=BaseInterceptor.OK ) {
hooks.addModule( bi );
}
hooksCache=null;
allHooksCache=null;
}
public void removeInterceptor( BaseInterceptor bi ) {
hooks.removeModule( bi );
hooksCache=null;
allHooksCache=null;
}
public BaseInterceptor[] getInterceptors( int type )
{
if( hooksCache != null ) {
return hooksCache[type];
}
// load the cache with all the hooks
Container globalIntContainer=getContextManager().getContainer();
Hooks globals=globalIntContainer.getHooks();
hooksCache=new BaseInterceptor[H_COUNT][];
for( int i=0; i<H_COUNT; i++ ) {
Hooks locals=null;
if( this != globalIntContainer ) {
hooksCache[i]=mergeHooks( globals.getModules(i),
getHooks().getModules(i));
} else {
hooksCache[i]=mergeHooks( globals.getModules(i), null);
}
}
return hooksCache[type];
}
/** Get all interceptors
*/
public BaseInterceptor[] getInterceptors()
{
if( allHooksCache != null ) {
return allHooksCache;
}
// load the cache with all the hooks
Container globalIntContainer=getContextManager().getContainer();
Hooks globals=globalIntContainer.getHooks();
if( this == globalIntContainer ) {
allHooksCache=mergeHooks( globals.getModules(), null );
} else {
allHooksCache=mergeHooks( globals.getModules(),
this.getHooks().getModules());
}
return allHooksCache;
}
private BaseInterceptor[] mergeHooks( Object globalM[], Object localM[] ) {
BaseInterceptor hA[]=null;
if( localM==null ) {
hA=new BaseInterceptor[ globalM.length ];
for( int j=0; j<globalM.length; j++ ) {
hA[j]=(BaseInterceptor)globalM[j];
}
} else {
hA=new BaseInterceptor[ globalM.length +
localM.length ];
int gsize=globalM.length;
for( int j=0; j<globalM.length; j++ ) {
hA[j]=(BaseInterceptor)globalM[j];
}
for( int j=0; j<localM.length; j++ ) {
hA[gsize+j]=(BaseInterceptor)localM[j];
}
}
return hA;
}
public void resetInterceptorCache( int id ) {
allHooksCache=null;
hooksCache=null;
}
// debug
private static final int dL=0;
private void debug( String s ) {
System.out.println("Container: " + s );
}
}