com.silverwrist.dynamo.app.ApplicationContainer.java Source code

Java tutorial

Introduction

Here is the source code for com.silverwrist.dynamo.app.ApplicationContainer.java

Source

/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (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.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2002-03 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.app;

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.collections.*;
import org.apache.log4j.Logger;
import org.w3c.dom.*;
import com.silverwrist.util.*;
import com.silverwrist.util.xml.*;
import com.silverwrist.dynamo.DynamoVersion;
import com.silverwrist.dynamo.Namespaces;
import com.silverwrist.dynamo.event.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.script.ScriptController;
import com.silverwrist.dynamo.util.*;

/**
 * The main "engine" class of the Dynamo framework, which holds most of the service providers and other
 * classes registered (usually by the defined {@link com.silverwrist.dynamo.iface.Application Application} class).
 * Exactly one of these is created per Dynamo instance; for Dynamo Web applications, a reference to it is saved
 * in the servlet context attributes.  It is also responsible for reading and parsing the Dynamo XML configuration
 * file and instantiating components defined therein.
 *
 * @author Eric J. Bowersox &lt;erbo@silcom.com&gt;
 * @version X
 */
public class ApplicationContainer implements ResourceProvider, ResourceProviderManager, RendererRegistration,
        ObjectProvider, EventListenerRegistration, OutputObjectFilterRegistration, QueryRenderer, PostDynamicUpdate,
        RenderImmediate, RequestPreprocessorRegistration, ExceptionTranslatorRegistration, FinalStageRegistration {
    /*--------------------------------------------------------------------------------
     * Internal class recording renderer registrations and providing a
     * "shutdown hook"
     *--------------------------------------------------------------------------------
     */

    private class RegisteredRenderer implements ComponentShutdown {
        /*====================================================================
         * Attributes
         *====================================================================
         */

        private Renderer m_renderer;
        private HashSet m_known_classes;

        /*====================================================================
         * Constructors
         *====================================================================
         */

        RegisteredRenderer(Class klass, Renderer renderer) {
            m_renderer = renderer;
            m_known_classes = new HashSet();
            m_known_classes.add(klass);

        } // end constructor

        RegisteredRenderer(DynamicClass dclass, Renderer renderer) {
            m_renderer = renderer;
            m_known_classes = new HashSet();
            m_known_classes.add(dclass);

        } // end constructor

        /*====================================================================
         * Implementations from interface ComponentShutdown
         *====================================================================
         */

        public synchronized void shutdown() {
            Iterator it = m_known_classes.iterator();
            while (it.hasNext()) { // remove all class rendering entries registered with this "event"
                Object obj = it.next();
                if (obj instanceof Class)
                    m_class_renderers.remove(obj);
                else if (obj instanceof DynamicClass)
                    m_class_renderers.remove(obj);

            } // end while

            m_known_classes.clear();

        } // end shutdown

        /*====================================================================
         * External operations
         *====================================================================
         */

        public Renderer getRenderer() {
            return m_renderer;

        } // end getRenderer

        public synchronized void add(Class klass) {
            m_known_classes.add(klass);

        } // end add

        public synchronized void add(DynamicClass dclass) {
            m_known_classes.add(dclass);

        } // end add

    } // end class RegisteredRenderer

    /*--------------------------------------------------------------------------------
     * Static data members
     *--------------------------------------------------------------------------------
     */

    private static Logger logger = Logger.getLogger(ApplicationContainer.class);

    private static final String[] SUBSTRATE_MAP_KEYS = { ApplicationSubstrate.OBJ_BASE_PATH,
            ApplicationSubstrate.OBJ_CODE_PATH, ApplicationSubstrate.OBJ_CLASSES_PATH,
            ApplicationSubstrate.OBJ_LIBS_PATH };

    private static final ApplicationListener[] APP_LISTENER_TEMPLATE = new ApplicationListener[0];
    private static final SessionInfoListener[] SESSION_LISTENER_TEMPLATE = new SessionInfoListener[0];

    private static final String TEMPLATE_CLASSNAME = "$$$BLORT$$$";

    /*--------------------------------------------------------------------------------
     * Attributes
     *--------------------------------------------------------------------------------
     */

    private int m_refs; // reference count
    private ApplicationSubstrate m_substrate; // application substrate object
    private File m_resource_base; // base directory for resources
    private Vector m_resource_providers = new Vector(); // resource providers
    private LinkedList m_shutdown_list = new LinkedList(); // list of objects to shut down
    private HashMap m_connections = new HashMap(); // list of connection pools
    private HashMap m_objects = new HashMap(); // list of base objects
    private Application m_application = null; // the application object
    private Hashtable m_class_renderers = new Hashtable(); // renderers for static classes
    private Hashtable m_dclass_renderers = new Hashtable(); // renderers for dynamic classes
    private Vector m_application_listeners = new Vector(); // application event listeners
    private Vector m_session_listeners = new Vector(); // session event listeners
    private BackgroundProcessor m_background; // background processor
    private ScriptController m_script_ctrl; // script controller
    private ApplicationServiceManager m_app_sm; // application service manager
    private PropertySerializationSupport m_pss; // property serialization support
    private Set m_known_sessions; // known sessions
    private String m_identity; // server identity
    private Map m_rewrite_rules; // URL rewriting rules
    private Vector m_output_filters = new Vector(); // output filters
    private HashMap m_update_listeners = new HashMap(); // update listeners
    private Vector m_request_preprocessors = new Vector(); // request preprocessors
    private Vector m_exception_xlators = new Vector(); // exception translators
    private LinkedList m_final_stage_inits = new LinkedList(); // final-stage initialization hooks
    private LinkedList m_prestage_shutdown = new LinkedList(); // pre-stage shutdown hooks

    /*--------------------------------------------------------------------------------
     * Constructor
     *--------------------------------------------------------------------------------
     */

    /**
     * Creates the application container.
     *
     * @param config_file A reference to the Dynamo XML configuration file.
     * @param substrate A reference to the {@link com.silverwrist.dynamo.iface.ApplicationSubstrate ApplicationSubstrate}
     *                  object, which provides certain services and object references to the
     *                  <CODE>ApplicationContainer</CODE>.  (Usually, this will be specific to the Dynamo application
     *                  type, for example, Web application.)
     * @exception com.silverwrist.dynamo.except.ConfigException If there is an error in the configuration which will
     *            not allow Dynamo to be initialized.
     */
    public ApplicationContainer(File config_file, ApplicationSubstrate substrate) throws ConfigException {
        if (logger.isDebugEnabled())
            logger.debug("new ApplicationContainer - config file is " + config_file.getAbsolutePath());
        substrate.initialize();
        m_substrate = substrate;
        m_refs = 1;
        m_app_sm = new ApplicationServiceManager();
        m_pss = new PropertySerializationSupport();
        m_known_sessions = Collections.synchronizedSet(new HashSet());
        XMLLoader loader = XMLLoader.get();

        // Initialize the init services and runtime services with defaults.
        m_app_sm.addInitService(ResourceProvider.class, (ResourceProvider) this);
        m_app_sm.addInitService(ResourceProviderManager.class, (ResourceProviderManager) this);
        m_app_sm.addInitService(RendererRegistration.class, (RendererRegistration) this);
        m_app_sm.addInitService(ObjectProvider.class, (ObjectProvider) this);
        m_app_sm.addInitService(EventListenerRegistration.class, (EventListenerRegistration) this);
        m_app_sm.addInitService(OutputObjectFilterRegistration.class, (OutputObjectFilterRegistration) this);
        m_app_sm.addInitService(PropertySerializer.class, (PropertySerializer) m_pss);
        m_app_sm.addInitService(PropertySerializerRegistration.class, (PropertySerializerRegistration) m_pss);
        m_app_sm.addInitService(PostDynamicUpdate.class, (PostDynamicUpdate) this);
        m_app_sm.addInitService(RequestPreprocessorRegistration.class, (RequestPreprocessorRegistration) this);
        m_app_sm.addInitService(ExceptionTranslatorRegistration.class, (ExceptionTranslatorRegistration) this);
        m_app_sm.addInitService(FinalStageRegistration.class, (FinalStageRegistration) this);

        m_app_sm.addRuntimeService(ResourceProvider.class, (ResourceProvider) this);
        m_app_sm.addRuntimeService(ObjectProvider.class, (ObjectProvider) this);
        m_app_sm.addRuntimeService(EventListenerRegistration.class, (EventListenerRegistration) this);
        m_app_sm.addRuntimeService(PropertySerializer.class, (PropertySerializer) m_pss);
        m_app_sm.addRuntimeService(PostDynamicUpdate.class, (PostDynamicUpdate) this);
        m_app_sm.addRuntimeService(RenderImmediate.class, (RenderImmediate) this);

        m_app_sm.addOutputService(ResourceProvider.class, (ResourceProvider) this);
        m_app_sm.addOutputService(ObjectProvider.class, (ObjectProvider) this);
        m_app_sm.addOutputService(QueryRenderer.class, (QueryRenderer) this);

        // Create initialization services interface object.
        ServiceProvider init_svcs = m_app_sm.createInitServices();

        try { // load the configuration file
            Document config_doc = loader.load(config_file, false);
            Element root = loader.getRootElement(config_doc, "configuration");

            // get the <control/> element and process it
            Element control = loader.getSubElement(root, "control");
            processControlSection(control);
            m_shutdown_list.addFirst(m_background);
            m_app_sm.addInitService(BackgroundScheduler.class, m_background);
            m_app_sm.addRuntimeService(BackgroundScheduler.class, m_background);

            // initialize some default renderers
            m_shutdown_list.addFirst(registerRenderer(DataItem.class, new DataItemRenderer()));
            m_shutdown_list.addFirst(registerRenderer(java.util.List.class, new ListRenderer()));

            // initialize the scripting engine
            m_script_ctrl = new ScriptController();
            m_script_ctrl.initialize(control, init_svcs);
            m_shutdown_list.addFirst(m_script_ctrl);

            // add the scripting engine's services so they can be used by external components
            m_app_sm.addInitService(ScriptEngineConfig.class, m_script_ctrl);
            m_app_sm.addRuntimeService(ScriptExecute.class, m_script_ctrl);

            // get all database connection configurations
            List l = loader.getMatchingSubElements(root, "dbconnection");
            Iterator it = l.iterator();
            Element elt;
            while (it.hasNext()) { // get each element in turn
                elt = (Element) (it.next());
                DBConnectionPool pool = (DBConnectionPool) createNamedObject(elt, init_svcs, DBConnectionPool.class,
                        "no.notDBPool");
                m_connections.put(pool.getName(), pool);

            } // end while

            m_app_sm.addInitService(HookServiceProviders.class, m_app_sm);

            // Sort the "object" definitions by priority order to determine in what order to instantiate them.
            l = loader.getMatchingSubElements(root, "object");
            if (!(l.isEmpty())) { // copy elements into binary heap 
                it = l.iterator();
                BinaryHeap prioheap = new BinaryHeap(l.size());
                while (it.hasNext()) { // sort elements by priority 
                    elt = (Element) (it.next());
                    prioheap.insert(new HeapContents(elt));

                } // end while

                while (prioheap.size() > 0) { // now remove and instantiate the elements
                    HeapContents hc = (HeapContents) (prioheap.remove());
                    NamedObject nobj = createNamedObject(hc.getElement(), init_svcs, null, null);
                    m_objects.put(nobj.getName(), nobj);

                } // end while

            } // end if

            // Find the application definition and initialize the application.
            elt = loader.getSubElement(root, "application");
            m_application = (Application) createNamedObject(elt, init_svcs, Application.class, "no.notApp");

        } // end try
        catch (IOException e) { // unable to read config file - send back a ConfigException
            logger.fatal("ApplicationContainer config read failed", e);
            ConfigException ce = new ConfigException(ApplicationContainer.class, "ApplicationContainerMessages",
                    "creation.ioError", e);
            ce.setParameter(0, config_file.getAbsolutePath());
            throw ce;

        } // end catch
        catch (XMLLoadException e) { // XML loader failed - send back a ConfigException
            logger.fatal("ApplicationContainer config load failed", e);
            throw new ConfigException(e);

        } // end catch

        // Create the "server identity" string.
        StringBuffer buf = new StringBuffer();
        String app_id = m_application.getIdentity();
        if (app_id != null)
            buf.append(app_id).append(' ');
        buf.append("Dynamo/").append(DynamoVersion.VERSION);
        m_identity = buf.toString();
        logger.info("Server: " + m_identity);

        try { // Call the "final stage" initialization hooks.
            logger.info(m_final_stage_inits.size() + " final-stage init hook(s) to call");
            while (!(m_final_stage_inits.isEmpty())) { // call the hooks in FIFO order
                FinalStageInitHook hook = (FinalStageInitHook) (m_final_stage_inits.removeFirst());
                hook.initialize(m_application, init_svcs);

            } // end while

            m_final_stage_inits = null; // done with this list

        } // end try
        catch (DynamoException e) { // final-stage initialization failed - post an error
            ConfigException ce = new ConfigException(ApplicationContainer.class, "ApplicationContainerMessages",
                    "finalinit.fail", e);
            ce.setParameter(0, e.getMessage());
            throw ce;

        } // end catch

        // Fire the "application initialized" events.
        ApplicationListener[] listeners = (ApplicationListener[]) (m_application_listeners
                .toArray(APP_LISTENER_TEMPLATE));
        if (listeners.length > 0) { // call the event handlers...
            if (logger.isDebugEnabled())
                logger.debug("ApplicationContainer: " + listeners.length + " init handler(s) to call");
            ApplicationEventRequest req = new ApplicationEventRequest(m_app_sm.createRuntimeServices(), true);
            ApplicationEvent evt = new ApplicationEvent(req, m_application);
            for (int i = 0; i < listeners.length; i++)
                listeners[i].applicationInitialized(evt);

        } // end if

        if (logger.isDebugEnabled())
            logger.debug("ApplicationContainer initialization done");

    } // end constructor

    /*--------------------------------------------------------------------------------
     * Internal operations
     *--------------------------------------------------------------------------------
     */

    private static final Class getUltimateComponent(Class klass) {
        while (klass.isArray())
            klass = klass.getComponentType();
        return klass;

    } // end getUltimateComponent

    /**
     * Destroys all data associated with this <CODE>ApplicationContainer</CODE>.
     */
    private final synchronized void destroy() {
        if (logger.isDebugEnabled())
            logger.debug("ApplicationContainer.destroy(): " + m_known_sessions.size() + " sessions to shutdown");
        Iterator it = m_known_sessions.iterator();
        while (it.hasNext()) { // shut down each session
            ComponentShutdown cs = (ComponentShutdown) (it.next());
            cs.shutdown();

        } // end while

        m_known_sessions.clear();

        // Fire the "application exiting" events.
        ApplicationListener[] listeners = (ApplicationListener[]) (m_application_listeners
                .toArray(APP_LISTENER_TEMPLATE));
        if (logger.isDebugEnabled())
            logger.debug("ApplicationContainer.destroy(): " + listeners.length + " exit handlers to call");
        if (listeners.length > 0) { // call the event handlers...
            ApplicationEventRequest req = new ApplicationEventRequest(m_app_sm.createRuntimeServices(), false);
            ApplicationEvent evt = new ApplicationEvent(req, m_application);
            for (int i = 0; i < listeners.length; i++)
                listeners[i].applicationExiting(evt);

        } // end if

        if (logger.isDebugEnabled())
            logger.debug("ApplicationContainer.destroy(): " + m_prestage_shutdown.size()
                    + " pre-stage shutdown hooks to call");
        while (m_prestage_shutdown.size() > 0) { // call the appropriate pre-stage shutdown hooks
            ComponentShutdown sd = (ComponentShutdown) (m_prestage_shutdown.removeFirst());
            sd.shutdown();

        } // end while

        if (logger.isDebugEnabled())
            logger.debug("ApplicationContainer.destroy(): " + m_shutdown_list.size() + " objects to blow away");

        while (m_shutdown_list.size() > 0) { // shut down all components in reverse order of creation
            ComponentShutdown sd = (ComponentShutdown) (m_shutdown_list.removeFirst());
            sd.shutdown();

        } // end while

        if (logger.isDebugEnabled())
            logger.debug("ApplicationContainer.destroy(): clearing internal data structures");
        m_connections.clear();
        m_objects.clear();
        m_class_renderers.clear();
        m_dclass_renderers.clear();
        m_application = null;
        m_resource_providers.clear();
        m_application_listeners.clear();
        m_session_listeners.clear();

        if (logger.isDebugEnabled())
            logger.debug("ApplicationContainer.destroy(): terminating substrate");
        m_substrate.terminate();
        m_substrate = null;

    } // end destroy

    private final Map getSubstrateReplaceMap() {
        HashMap rc = new HashMap();
        for (int i = 0; i < SUBSTRATE_MAP_KEYS.length; i++) { // load the map with return values
            try { // get each object in turn from the substrate and add it to the map
                Object tmp = m_substrate.getObject(ApplicationSubstrate.NAMESPACE, SUBSTRATE_MAP_KEYS[i]);
                rc.put(SUBSTRATE_MAP_KEYS[i], tmp.toString());

            } // end try
            catch (NoSuchObjectException e) { // skip this object and try the next one
            } // end catch

        } // end for

        return rc;

    } // end getSubstrateReplaceMap

    private final void processControlSection(Element control) throws ConfigException, XMLLoadException {
        Map replace_map = getSubstrateReplaceMap();
        XMLLoader loader = XMLLoader.get();
        DOMElementHelper control_h = new DOMElementHelper(control);

        // Get the thread counts and create the background processor.
        int num_norm = 2;
        int num_low = 2;
        Element elt = control_h.getSubElement("background-threads");
        if (elt != null) { // read in the values
            num_norm = loader.getAttributeInt(elt, "normal", 2);
            num_low = loader.getAttributeInt(elt, "low", 2);

        } // end if

        // Create the background services and the background processor.
        SimpleServiceProvider bg_svcs = new SimpleServiceProvider("Background Services");
        bg_svcs.addService(ResourceProvider.class, this);
        bg_svcs.addService(ObjectProvider.class, this);
        m_background = new BackgroundProcessor(num_norm, num_low, bg_svcs);

        // Get the resource root directory.
        String res_root = loader.getSubElementText(control_h, "resource-root");
        m_resource_base = new File(StringUtils.replaceAllVariables(res_root, replace_map));
        if (!(m_resource_base.isDirectory())) { // the resource base MUST be a directory!
            ConfigException ce = new ConfigException(ApplicationContainer.class, "ApplicationContainerMessages",
                    "resource.rootErr");
            ce.setParameter(0, m_resource_base.getAbsolutePath());
            throw ce;

        } // end if

        // Get the list of rewrite rules.
        Element rewrite_sect = loader.getSubElement(control_h, "url-rewrite-rules");
        List rewrite_list = loader.getMatchingSubElements(rewrite_sect, "rule");
        HashMap tmp_map = new HashMap();
        Iterator it = rewrite_list.iterator();
        while (it.hasNext()) { // get each rule and create corresponding object
            elt = (Element) (it.next());
            RewriteRule rule = new RewriteRule(elt);
            tmp_map.put(rule.getName(), rule);

        } // end while

        m_rewrite_rules = Collections.unmodifiableMap(tmp_map);

    } // end processControlSection

    private final NamedObject createNamedObject(Element config_root, ServiceProvider services, Class extratest,
            String extratest_msg) throws ConfigException {
        NamedObject nobj = LoaderUtils.loadObject(config_root, services, extratest);
        synchronized (this) { // save its shutdown hook for later
            ComponentShutdown sd = (ComponentShutdown) (LoaderUtils.query(ComponentShutdown.class, nobj));
            if (sd != null)
                m_shutdown_list.addFirst(sd);

        } // end synchronized block

        return nobj;

    } // end createNamedObject

    private final WrappedResourceProvider locateResourceProvider(ComponentResName crn) {
        WrappedResourceProvider wrp = null;
        synchronized (m_resource_providers) { // perform the "maximum munch" algorithm to try and find a provider
            int max_munch = 0;
            Iterator it = m_resource_providers.iterator();
            while (it.hasNext()) { // look at each mounted provider and try to find a better possibility
                WrappedResourceProvider tmp = (WrappedResourceProvider) (it.next());
                int test = tmp.getMatchCount(crn);
                if (test > max_munch) { // found one that munches more...save it
                    wrp = tmp;
                    max_munch = test;

                } // end if

            } // end while

        } // end synchronized block

        return wrp;

    } // end locateResourceProvider

    private final InputStream getResourceInternal(ComponentResName crn) throws IOException {
        File res_file = new File(m_resource_base, crn.getStringValue(0, false));
        if (!(res_file.exists()) || res_file.isDirectory() || !(res_file.canRead()))
            throw new NoSuchResourceException(crn.getStringValue(0, true));
        return new FileInputStream(res_file);

    } // end getResourceInternal

    private final long getResourceModTimeInternal(ComponentResName crn) {
        File res_file = new File(m_resource_base, crn.getStringValue(0, false));
        if (!(res_file.exists()) || res_file.isDirectory() || !(res_file.canRead()))
            return 0;
        return res_file.lastModified();

    } // end getResourceModTimeInternal

    private final RegisteredRenderer searchDClassRenderers(DynamicClass dclass) {
        return (RegisteredRenderer) (m_dclass_renderers.get(dclass));

    } // end searchDClassRenderer

    private final RegisteredRenderer searchArrayRenderers(Class klass, String template) {
        if (klass.isPrimitive() || (klass == Object.class))
            return null; // should have been picked up already

        // look at this level for the class member
        RegisteredRenderer rc = null;
        try { // load the array class corresponding to the right depth, then check the renderer map
            Class tmp = Class.forName(StringUtils.replace(template, TEMPLATE_CLASSNAME, klass.getName()));
            rc = (RegisteredRenderer) (m_class_renderers.get(tmp));
            if (rc != null)
                return rc;

        } // end try
        catch (ClassNotFoundException e) { // this class was not found, so it can't be present
            rc = null;

        } // end catch

        // Try all interfaces implemented by the object.
        Class[] ifaces = klass.getInterfaces();
        for (int i = 0; i < ifaces.length; i++) { // look for interfaces implemented by the object
            rc = searchArrayRenderers(ifaces[i], template);
            if (rc != null)
                return rc;

        } // end for

        Class superclass = klass.getSuperclass();
        if (superclass != null) { // try the superclass now
            rc = searchArrayRenderers(superclass, template);
            if (rc != null)
                return rc;

        } // end if

        return null; // give up

    } // end searchArrayRenderers

    private final RegisteredRenderer searchClassRenderers(Class klass) {
        if (klass.isPrimitive() || (klass == Object.class))
            return null; // should have been picked up already

        // look at this level for the class member
        RegisteredRenderer rc = (RegisteredRenderer) (m_class_renderers.get(klass));
        if (rc != null)
            return rc;

        if (klass.isArray()) { // for arrays, use the parallel function to search back over the component
                               // class's hierarchy
            Class component = getUltimateComponent(klass);
            if (component.isPrimitive() || (component == Object.class))
                return null; // no chance this should have been picked up
            String template = StringUtils.replace(klass.getName(), component.getName(), TEMPLATE_CLASSNAME);
            return searchArrayRenderers(component, template);

        } // end if

        // Try all interfaces implemented by the object.
        Class[] ifaces = klass.getInterfaces();
        for (int i = 0; i < ifaces.length; i++) { // look for interfaces implemented by the object
            rc = searchClassRenderers(ifaces[i]);
            if (rc != null)
                return rc;

        } // end for

        Class superclass = klass.getSuperclass();
        if (superclass != null) { // try the superclass now
            rc = searchClassRenderers(superclass);
            if (rc != null)
                return rc;

        } // end if

        return null; // give up

    } // end searchClassRenderers

    /*--------------------------------------------------------------------------------
     * Implementations from interface ResourceProvider
     *--------------------------------------------------------------------------------
     */

    public InputStream getResource(String resource_path) throws IOException {
        ComponentResName crn = new ComponentResName(resource_path);
        WrappedResourceProvider wrp = locateResourceProvider(crn);
        if (wrp == null)
            return getResourceInternal(crn);
        else
            return wrp.getResource(crn);

    } // end getResource

    public long getResourceModTime(String resource_path) {
        try { // build the resource name and return it
            ComponentResName crn = new ComponentResName(resource_path);
            WrappedResourceProvider wrp = locateResourceProvider(crn);
            if (wrp == null)
                return getResourceModTimeInternal(crn);
            else
                return wrp.getResourceModTime(crn);

        } // end try
        catch (NoSuchResourceException e) { // just deal with exceptions in here
            return 0;

        } // end catch

    } // end getResourceModTime

    /*--------------------------------------------------------------------------------
     * Implementations from interface ResourceProviderManager
     *--------------------------------------------------------------------------------
     */

    public ComponentShutdown mountResourceProvider(String mount_path, ResourceProvider provider)
            throws ConfigException {
        ComponentResName crn = null;
        try { // create component name for mount path
            crn = new ComponentResName(mount_path);

        } // end try
        catch (NoSuchResourceException e) { // this is not a valid name - throw ConfigException
            ConfigException ce = new ConfigException(ApplicationContainer.class, "ApplicationContainerMessages",
                    "mountRP.badName");
            ce.setParameter(0, mount_path);
            throw ce;

        } // end catch

        WrappedResourceProvider wrp = new WrappedResourceProvider(crn, provider);
        if (m_resource_providers.contains(wrp)) { // resource provider already mounted on this path - bogus!
            ConfigException ce = new ConfigException(ApplicationContainer.class, "ApplicationContainerMessages",
                    "mountRP.already");
            ce.setParameter(0, mount_path);
            throw ce;

        } // end if

        m_resource_providers.add(wrp);
        return new ShutdownVectorRemove(m_resource_providers, wrp);

    } // end mountResourceProvider

    /*--------------------------------------------------------------------------------
     * Implementations from interface RendererRegistration
     *--------------------------------------------------------------------------------
     */

    public ComponentShutdown registerRenderer(Class klass, Renderer renderer) throws ConfigException {
        if (m_class_renderers.containsKey(klass)) { // renderer already registered - bogus!
            ConfigException ce = new ConfigException(ApplicationContainer.class, "ApplicationContainerMessages",
                    "registerRenderer.already");
            ce.setParameter(0, klass.getName());
            throw ce;

        } // end if

        if (logger.isDebugEnabled())
            logger.debug("Registering a new renderer for class " + klass.getName());
        RegisteredRenderer rr = new RegisteredRenderer(klass, renderer);
        m_class_renderers.put(klass, rr);
        return rr;

    } // end registerRenderer

    public ComponentShutdown registerRenderer(DynamicClass dclass, Renderer renderer) throws ConfigException {
        if (m_dclass_renderers.containsKey(dclass)) { // renderer already registered - bogus!
            ConfigException ce = new ConfigException(ApplicationContainer.class, "ApplicationContainerMessages",
                    "registerRenderer.already");
            ce.setParameter(0, dclass.getName());
            throw ce;

        } // end if

        if (logger.isDebugEnabled())
            logger.debug("Registering a new renderer for dynamic class " + dclass.getName());
        RegisteredRenderer rr = new RegisteredRenderer(dclass, renderer);
        m_class_renderers.put(dclass, rr);
        return rr;

    } // end if

    /*--------------------------------------------------------------------------------
     * Implementations from interface ObjectProvider
     *--------------------------------------------------------------------------------
     */

    /**
     * Retrieves an object from this <CODE>ObjectProvider</CODE>.
     *
     * @param namespace The namespace to interpret the name relative to.
     * @param name The name of the object to be retrieved.
     * @return The object reference specified.
     */
    public Object getObject(String namespace, String name) {
        if (namespace.equals(Namespaces.SUBSTRATE_NAMESPACE))
            return m_substrate.getObject(namespace, name);

        if (namespace.equals(Namespaces.DATABASE_CONNECTIONS_NAMESPACE)) { // get a database connection
            Object rc = m_connections.get(name);
            if (rc != null)
                return rc;

        } // end if

        if (namespace.equals(Namespaces.DYNAMO_OBJECT_NAMESPACE)) { // get an object
            Object rc = m_objects.get(name);
            if (rc != null)
                return rc;

        } // end if

        if (namespace.equals(Namespaces.DYNAMO_APPLICATION_NAMESPACE)) { // return one of the application data elements
            if (name.equals("application"))
                return m_application;
            else if (name.equals("identity"))
                return m_identity;
            else if (name.equals("__container__"))
                return this;

        } // end if

        throw new NoSuchObjectException("ApplicationContainer", namespace, name);

    } // end getObject

    /*--------------------------------------------------------------------------------
     * Implementations from interface EventListenerRegistration
     *--------------------------------------------------------------------------------
     */

    public ComponentShutdown registerApplicationListener(ApplicationListener listener) {
        m_application_listeners.add(listener);
        return new ShutdownVectorRemove(m_application_listeners, listener);

    } // end registerApplicationListener

    public ComponentShutdown registerSessionInfoListener(SessionInfoListener listener) {
        m_session_listeners.add(listener);
        return new ShutdownVectorRemove(m_session_listeners, listener);

    } // end registerSessionInfoListener

    public synchronized ComponentShutdown registerDynamicUpdateListener(Class event_type,
            DynamicUpdateListener listener) {
        if (!(DynamicUpdateEvent.class.isAssignableFrom(event_type)))
            throw new IllegalArgumentException("event type is not valid");
        Vector vec = (Vector) (m_update_listeners.get(event_type));
        if (vec == null) { // creatr vector for this event type
            vec = new Vector();
            m_update_listeners.put(event_type, vec);

        } // end if

        vec.add(listener);
        return new ShutdownVectorRemove(vec, listener);

    } // end registerDynamicUpdateListener

    /*--------------------------------------------------------------------------------
     * Implementations from interface OutputObjectFilterRegistration
     *--------------------------------------------------------------------------------
     */

    public ComponentShutdown registerOutputObjectFilter(OutputObjectFilter filter) {
        if (logger.isDebugEnabled())
            logger.debug("Registering new OutputObjectFilter: " + filter);
        m_output_filters.add(filter);
        return new ShutdownVectorRemove(m_output_filters, filter);

    } // end registerOutputObjectFilter

    /*--------------------------------------------------------------------------------
     * Implementations from interface QueryRenderer
     *--------------------------------------------------------------------------------
     */

    public Renderer getRendererForObject(Object obj) {
        RegisteredRenderer rc = null;
        if (obj instanceof DynamicObject) { // this is a dynamic object - search by its dynamic class first
            DynamicClass dclass = ((DynamicObject) obj).getDClass();
            rc = (RegisteredRenderer) (m_dclass_renderers.get(dclass));
            if (rc == null) { // search for ancestor class, "snap" reference if possible
                rc = searchDClassRenderers(dclass);
                if (rc != null) { // add this class to the mapping
                    m_dclass_renderers.put(dclass, rc);
                    rc.add(dclass);

                } // end if

            } // end if

            if (rc != null)
                return rc.getRenderer();

        } // end if

        Class klass = obj.getClass();
        rc = (RegisteredRenderer) (m_class_renderers.get(klass));
        if (rc == null) { // search for ancestor class, "snap" reference if possible
            rc = searchClassRenderers(klass);
            if (rc != null) { // found it - add this class to the mapping
                m_class_renderers.put(klass, rc);
                rc.add(klass);

            } // end if

        } // end if

        return ((rc == null) ? null : rc.getRenderer());

    } // end getRendererForObject

    /*--------------------------------------------------------------------------------
     * Implementations from interface PostDynamicUpdate
     *--------------------------------------------------------------------------------
     */

    public void postUpdate(DynamicUpdateEvent event) {
        if (logger.isDebugEnabled())
            logger.debug("PostDynamicUpdate: posting an event of type " + event.getClass().getName());
        Class klass = event.getClass();
        for (;;) { // get the event listeners
            Vector vec = (Vector) (m_update_listeners.get(klass));
            if (vec != null) { // call the event handlers
                Iterator it = vec.iterator();
                while (it.hasNext())
                    ((DynamicUpdateListener) (it.next())).updateReceived(event);

            } // end if

            if (klass == DynamicUpdateEvent.class)
                break;

            klass = klass.getSuperclass();

        } // end for (ever)

    } // end postUpdate

    /*--------------------------------------------------------------------------------
     * Implementations from interface RenderImmediate
     *--------------------------------------------------------------------------------
     */

    public String renderTextObject(Object obj) throws IOException, RenderingException {
        if (logger.isDebugEnabled())
            logger.debug("RenderImmediate rendering an object of type " + obj.getClass().getName());
        BufferTextRenderControl control = new BufferTextRenderControl(wrapOutputServices(null));
        control.renderSubObject(obj);
        return control.getData();

    } // end renderTextObject

    /*--------------------------------------------------------------------------------
     * Implementations from interface RequestPreprocessorRegistration
     *--------------------------------------------------------------------------------
     */

    public ComponentShutdown registerRequestPreprocessor(RequestPreprocessor rp) {
        if (logger.isDebugEnabled())
            logger.debug("Registering new RequestPreprocessor: " + rp);
        m_request_preprocessors.add(rp);
        return new ShutdownVectorRemove(m_request_preprocessors, rp);

    } // end registerRequestPreprocessor

    /*--------------------------------------------------------------------------------
     * Implementations from interface ExceptionTranslatorRegistration
     *--------------------------------------------------------------------------------
     */

    public ComponentShutdown registerExceptionTranslator(ExceptionTranslator xlat) {
        if (logger.isDebugEnabled())
            logger.debug("Registering new ExceptionTranslator: " + xlat);
        m_exception_xlators.add(xlat);
        return new ShutdownVectorRemove(m_exception_xlators, xlat);

    } // end registerExceptionTranslator

    /*--------------------------------------------------------------------------------
     * Implementations from interface FinalStageRegistration
     *--------------------------------------------------------------------------------
     */

    public synchronized boolean registerFinalStageInitHook(FinalStageInitHook hook) {
        if ((m_final_stage_inits == null) || (m_application != null))
            return false; // too late to register
        m_final_stage_inits.addLast(hook);
        return true;

    } // end registerFinalStageInitHook

    public synchronized void registerPreStageShutdown(ComponentShutdown cs) {
        m_prestage_shutdown.addFirst(cs);

    } // end registerPreStageShutdown

    /*--------------------------------------------------------------------------------
     * External operations
     *--------------------------------------------------------------------------------
     */

    /**
     * Adds a reference to the reference count of the <CODE>ApplicationContainer</CODE>.  Each servlet
     * or other initializing component that attempts to initialize the <CODE>ApplicationContainer</CODE>
     * increases its reference count by 1.
     */
    public synchronized void addRef() {
        ++m_refs;
        if (logger.isDebugEnabled())
            logger.debug("ApplicationContainer.addRef(): refcount now " + m_refs);

    } // end addRef

    /**
     * Removes a reference from the reference count of the <CODE>ApplicationContainer</CODE>.  Each servlet that
     * references the <CODE>ApplicationContainer</CODE> and is destroyed decreases the reference count by 1.
     * When the reference count reaches 0, the <CODE>ApplicationContainer</CODE> is destroyed.
     *
     * @return <CODE>true</CODE> if the <CODE>ApplicationContainer</CODE> was destroyed, <CODE>false</CODE> if not.
     */
    public synchronized boolean release() {
        if (--m_refs == 0) { // clean up this stuff
            if (logger.isDebugEnabled())
                logger.debug("ApplicationContainer.release(): refcount now 0 - destroying!");
            destroy();
            return true;

        } // end if

        if (logger.isDebugEnabled())
            logger.debug("ApplicationContainer.release(): refcount now " + m_refs);

        return false;

    } // end release

    /**
     * Returns the instance of the {@link com.silverwrist.dynamo.iface.Application Application} object defined in
     * the Dynamo XML configuration file and instantiated when the <CODE>ApplicationContainer</CODE> was created.
     *
     * @return The <CODE>Application</CODE> object.
     */
    public Application getApplication() {
        return m_application;

    } // end getApplication

    public ServiceProvider getInitServices() {
        return m_app_sm.createInitServices();

    } // end getInitServices

    public ServiceProvider wrapServices(ServiceProvider sp) {
        return m_app_sm.createRuntimeServices(sp);

    } // end wrapServices

    public ServiceProvider wrapOutputServices(ServiceProvider sp) {
        return m_app_sm.createOutputServices(sp);

    } // end wrapOutputServices

    /**
     * Returns a list of all registered {@link com.silverwrist.dynamo.event.SessionInfoListener SessionInfoListener}
     * objects.
     *
     * @return A list of all registered <CODE>SessionInfoListener</CODE> objects.
     */
    public SessionInfoListener[] getSessionListeners() {
        return (SessionInfoListener[]) (m_session_listeners.toArray(SESSION_LISTENER_TEMPLATE));

    } // end getSessionListeners

    /**
     * Adds a session to our list of known sessions, which will be shut down when the application container
     * is itself destroyed.
     *
     * @param link The link to the session.
     */
    public void addSessionLink(ComponentShutdown link) {
        m_known_sessions.add(link);

    } // end addSessionLink

    /**
     * Removes a session from our list of known sessions, so it will no longer be automatically shut down
     * when the application container is itself destroyed.
     *
     * @param link The link to the session.
     */
    public void removeSessionLink(ComponentShutdown link) {
        m_known_sessions.remove(link);

    } // end removeSessionLink

    public String getServerIdentity() {
        return m_identity;

    } // end getServerIdentity

    public void setServerHeader(HttpServletResponse resp) {
        resp.setHeader("Server", m_identity);

    } // end setServerHeader

    public Map getRewriteRuleMap() {
        return m_rewrite_rules;

    } // end getRewriteRuleMap

    public Object filterOutput(Object out, Request r) throws RenderingException {
        Object rc = out;
        for (int i = (m_output_filters.size() - 1); i >= 0; i--) { // look for new objects that filter this one
            OutputObjectFilter filt = (OutputObjectFilter) (m_output_filters.get(i));
            Object tmp_rc = filt.filterObject(rc, r);
            if (tmp_rc != null)
                rc = tmp_rc;

        } // end for

        return rc;

    } // end filterOutput

    public void preprocessRequest(Request r) {
        for (int i = (m_request_preprocessors.size() - 1); i >= 0; i--) { // preprocess the request, if we have any preprocessors installed
            RequestPreprocessor rp = (RequestPreprocessor) (m_request_preprocessors.get(i));
            rp.preprocessRequest(r);

        } // end for

    } // end preprocessRequest

    public Object translateException(Request r, Exception e) {
        for (int i = (m_exception_xlators.size() - 1); i >= 0; i--) { // try to translate exceptions
            ExceptionTranslator xlat = (ExceptionTranslator) (m_exception_xlators.get(i));
            Object o = xlat.translateException(r, e);
            if (o != null)
                return o; // found a translation

        } // end for

        return e; // return the exception itself as a last resort

    } // end translateException

    public String translateSubstratePathName(String input) {
        return StringUtils.replaceAllVariables(input, getSubstrateReplaceMap());

    } // end translateSubstratePathName

} // end class ApplicationContainer