2003-05-20 03:25:31 +00:00

1293 lines
46 KiB
Java

/*
* 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
{
/*--------------------------------------------------------------------------------
* 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
/*--------------------------------------------------------------------------------
* 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.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);
// 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
/**
* Finds an instance of the {@link com.silverwrist.dynamo.iface.ComponentInitialize ComponentInitialize} interface
* associated with the given object, either through implementation of the interface, or by querying its
* {@link com.silverwrist.dynamo.iface.ServiceProvider ServiceProvider} implementation.
*
* @param obj The object to be queried.
* @return The instance of <CODE>ComponentInitialize</CODE> associated with the object, or <CODE>null</CODE>
* if there is no such instance.
*/
private static final ComponentInitialize getComponentInitialize(Object obj)
{
if (obj instanceof ComponentInitialize)
return (ComponentInitialize)obj;
if (obj instanceof ServiceProvider)
{ // it may be a service provider interface
try
{ // look for ComponentInitialize as a service
ServiceProvider sp = (ServiceProvider)obj;
ComponentInitialize rc = (ComponentInitialize)(sp.queryService(ComponentInitialize.class));
return rc;
} // end try
catch (NoSuchServiceException e)
{ // do nothing here
} // end catch
} // end if
return null; // no dice!
} // end getComponentInitialize
/**
* Finds an instance of the {@link com.silverwrist.dynamo.iface.ComponentShutdown ComponentShutdown} interface
* associated with the given object, either through implementation of the interface, or by querying its
* {@link com.silverwrist.dynamo.iface.ServiceProvider ServiceProvider} implementation.
*
* @param obj The object to be queried.
* @return The instance of <CODE>ComponentShutdown</CODE> associated with the object, or <CODE>null</CODE>
* if there is no such instance.
*/
private static final ComponentShutdown getComponentShutdown(Object obj)
{
if (obj instanceof ComponentShutdown)
return (ComponentShutdown)obj;
if (obj instanceof ServiceProvider)
{ // it may be a service provider interface
try
{ // look for ComponentShutdown as a service
ServiceProvider sp = (ServiceProvider)obj;
ComponentShutdown rc = (ComponentShutdown)(sp.queryService(ComponentShutdown.class));
return rc;
} // end try
catch (NoSuchServiceException e)
{ // do nothing here
} // end catch
} // end if
return null; // no dice!
} // end getComponentShutdown
/**
* 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_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
{
XMLLoader loader = XMLLoader.get();
String klassname = null;
try
{ // get the classname and load it, and create an object
klassname = loader.getAttribute(config_root,"classname");
if (logger.isDebugEnabled())
{ // output a proper debug message
Class desired = (extratest==null) ? NamedObject.class : extratest;
logger.debug("createNamedObject: Creating new NamedObject of class " + klassname
+ ", which should implement " + desired.getName());
} // end if
Class klass = Class.forName(klassname);
if (!(NamedObject.class.isAssignableFrom(klass)))
{ // this object is not a valid NamedObject
logger.error("object " + klassname + " does not implement NamedObject");
ConfigException ce = new ConfigException(ApplicationContainer.class,"ApplicationContainerMessages",
"no.notDynamo");
ce.setParameter(0,klassname);
throw ce;
} // end if
if ((extratest!=null) && !(extratest.isAssignableFrom(klass)))
{ // this object is not a valid (whatever)
logger.error("object " + klassname + " does not implement " + extratest.getName());
ConfigException ce = new ConfigException(ApplicationContainer.class,"ApplicationContainerMessages",
extratest_msg);
ce.setParameter(0,klassname);
throw ce;
} // end if
// instantiate the object, and initialize it as needed
Object rc = klass.newInstance();
ComponentInitialize init = getComponentInitialize(rc);
if (init!=null)
init.initialize(config_root,services);
synchronized (this)
{ // save its shutdown hook for later
ComponentShutdown sd = getComponentShutdown(rc);
if (sd!=null)
m_shutdown_list.addFirst(sd);
} // end synchronized block
if (logger.isDebugEnabled())
logger.debug("Object \"" + ((NamedObject)rc).getName() + "\" (of class " + klassname + ") created");
return (NamedObject)rc;
} // end try
catch (XMLLoadException e)
{ // error getting class name - throw the ConfigException
logger.error("XML loader failure",e);
throw new ConfigException(e);
} // end catch
catch (ClassNotFoundException e)
{ // the class was not found
ConfigException ce = new ConfigException(ApplicationContainer.class,"ApplicationContainerMessages",
"no.classNotFound");
ce.setParameter(0,klassname);
throw ce;
} // end catch
catch (IllegalAccessException e)
{ // unable to create this object
ConfigException ce = new ConfigException(ApplicationContainer.class,"ApplicationContainerMessages",
"no.createError");
ce.setParameter(0,klassname);
throw ce;
} // end catch
catch (InstantiationException e)
{ // unable to create this object
ConfigException ce = new ConfigException(ApplicationContainer.class,"ApplicationContainerMessages",
"no.createError");
ce.setParameter(0,klassname);
throw ce;
} // end catch
} // 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
/*--------------------------------------------------------------------------------
* 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 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