| /* |
| * Copyright 1999-2005 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.components.treeprocessor; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import org.apache.avalon.framework.activity.Disposable; |
| import org.apache.avalon.framework.activity.Initializable; |
| import org.apache.avalon.framework.configuration.Configurable; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.configuration.ConfigurationException; |
| import org.apache.avalon.framework.configuration.NamespacedSAXConfigurationHandler; |
| import org.apache.avalon.framework.configuration.SAXConfigurationHandler; |
| import org.apache.avalon.framework.container.ContainerUtil; |
| import org.apache.avalon.framework.context.Context; |
| import org.apache.avalon.framework.context.ContextException; |
| import org.apache.avalon.framework.context.Contextualizable; |
| import org.apache.avalon.framework.logger.AbstractLogEnabled; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.avalon.framework.service.Serviceable; |
| import org.apache.avalon.framework.thread.ThreadSafe; |
| import org.apache.cocoon.ProcessingException; |
| import org.apache.cocoon.Processor; |
| import org.apache.cocoon.components.ContextHelper; |
| import org.apache.cocoon.components.classloader.ClassLoaderFactory; |
| import org.apache.cocoon.components.classloader.ReloadingClassLoaderFactory; |
| import org.apache.cocoon.components.fam.SitemapMonitor; |
| import org.apache.cocoon.components.flow.Interpreter; |
| import org.apache.cocoon.components.source.SourceUtil; |
| import org.apache.cocoon.components.source.impl.DelayedRefreshSourceWrapper; |
| import org.apache.cocoon.components.treeprocessor.sitemap.FlowNode; |
| import org.apache.cocoon.core.Core; |
| import org.apache.cocoon.environment.Environment; |
| import org.apache.cocoon.environment.internal.EnvironmentHelper; |
| import org.apache.cocoon.sitemap.SitemapExecutor; |
| import org.apache.cocoon.sitemap.impl.DefaultExecutor; |
| import org.apache.commons.jci.compilers.JavaCompiler; |
| import org.apache.commons.jci.compilers.eclipse.EclipseJavaCompiler; |
| import org.apache.commons.jci.listeners.CompilingListener; |
| import org.apache.commons.jci.listeners.FileChangeListener; |
| import org.apache.commons.jci.listeners.NotifyingListener; |
| import org.apache.commons.jci.listeners.ReloadingListener; |
| import org.apache.commons.jci.listeners.ResourceStoringListener; |
| import org.apache.commons.jci.monitor.FilesystemAlterationListener; |
| import org.apache.commons.jci.stores.ResourceStore; |
| import org.apache.commons.jci.stores.TransactionalResourceStore; |
| import org.apache.excalibur.source.Source; |
| import org.apache.excalibur.source.SourceResolver; |
| import org.apache.regexp.RE; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * Interpreted tree-traversal implementation of a pipeline assembly language. |
| * |
| * @version $Id$ |
| */ |
| public class TreeProcessor extends AbstractLogEnabled |
| implements ThreadSafe, Processor, Serviceable, |
| Configurable, Contextualizable, |
| Disposable, Initializable { |
| |
| private static final String XCONF_URL = |
| "resource://org/apache/cocoon/components/treeprocessor/sitemap-language.xml"; |
| |
| /** The parent TreeProcessor, if any */ |
| protected TreeProcessor parent; |
| |
| /** The context */ |
| protected Context context; |
| |
| /** |
| * The component manager given by the upper level |
| * (root manager or parent concrete processor) |
| */ |
| protected ServiceManager manager; |
| |
| /** The core object. */ |
| protected Core core; |
| |
| /** Last modification time */ |
| protected long lastModified = 0; |
| |
| /** The source of the tree definition */ |
| protected DelayedRefreshSourceWrapper source; |
| |
| /** Delay for <code>sourceLastModified</code>. */ |
| protected long lastModifiedDelay; |
| |
| /** Check for reload? */ |
| protected boolean checkReload; |
| |
| protected SitemapMonitor fam; |
| |
| /** The source resolver */ |
| protected SourceResolver resolver; |
| |
| /** The environment helper */ |
| private EnvironmentHelper environmentHelper; |
| |
| /** The actual sitemap executor */ |
| private SitemapExecutor sitemapExecutor; |
| |
| /** Indicates whether this is our component or not */ |
| private boolean releaseSitemapExecutor; |
| |
| /** The actual processor */ |
| protected ConcreteTreeProcessor concreteProcessor; |
| |
| /** The tree builder configuration */ |
| private Configuration treeBuilderConfiguration; |
| |
| /** |
| * Create a TreeProcessor. |
| */ |
| public TreeProcessor() { |
| this.checkReload = true; |
| this.lastModifiedDelay = 1000; |
| } |
| |
| /** |
| * Create a child processor for a given language |
| */ |
| protected TreeProcessor(TreeProcessor parent, |
| DelayedRefreshSourceWrapper sitemapSource, |
| boolean checkReload, |
| String prefix) |
| throws Exception { |
| this.parent = parent; |
| enableLogging(parent.getLogger()); |
| |
| // Copy all that can be copied from the parent |
| this.context = parent.context; |
| this.source = sitemapSource; |
| this.treeBuilderConfiguration = parent.treeBuilderConfiguration; |
| this.checkReload = checkReload; |
| this.lastModifiedDelay = parent.lastModifiedDelay; |
| |
| this.manager = parent.concreteProcessor.getServiceManager(); |
| |
| this.resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE); |
| this.fam = (SitemapMonitor) this.manager.lookup(SitemapMonitor.ROLE); |
| this.core = (Core) this.manager.lookup(Core.ROLE); |
| this.environmentHelper = new EnvironmentHelper(parent.environmentHelper); |
| // Setup environment helper |
| ContainerUtil.enableLogging(this.environmentHelper, this.getLogger()); |
| ContainerUtil.service(this.environmentHelper, this.manager); |
| this.environmentHelper.changeContext(sitemapSource, prefix); |
| this.sitemapExecutor = parent.sitemapExecutor; |
| } |
| |
| /** |
| * Create a new child of this processor (used for mounting submaps). |
| * |
| * @return a new child processor. |
| */ |
| public TreeProcessor createChildProcessor(String src, |
| boolean checkReload, |
| String prefix) |
| throws Exception { |
| DelayedRefreshSourceWrapper delayedSource = new DelayedRefreshSourceWrapper( |
| this.resolver.resolveURI(src), this.lastModifiedDelay); |
| return new TreeProcessor(this, delayedSource, checkReload, prefix); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) |
| */ |
| public void contextualize(Context context) throws ContextException { |
| this.context = context; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) |
| */ |
| public void service(ServiceManager manager) throws ServiceException { |
| this.manager = manager; |
| this.resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE); |
| this.fam = (SitemapMonitor) this.manager.lookup(SitemapMonitor.ROLE); |
| this.core = (Core) this.manager.lookup(Core.ROLE); |
| } |
| |
| /** |
| * @see org.apache.avalon.framework.activity.Initializable#initialize() |
| */ |
| public void initialize() throws Exception { |
| // setup the environment helper |
| if (this.environmentHelper == null) { |
| this.environmentHelper = new EnvironmentHelper( |
| (URL) this.context.get(ContextHelper.CONTEXT_ROOT_URL)); |
| } |
| ContainerUtil.enableLogging(this.environmentHelper, getLogger()); |
| ContainerUtil.service(this.environmentHelper, this.manager); |
| |
| // Create sitemap executor |
| if (this.parent == null) { |
| if (this.manager.hasService(SitemapExecutor.ROLE)) { |
| this.sitemapExecutor = (SitemapExecutor) this.manager.lookup(SitemapExecutor.ROLE); |
| this.releaseSitemapExecutor = true; |
| } else { |
| this.sitemapExecutor = new DefaultExecutor(); |
| } |
| } else { |
| this.sitemapExecutor = this.parent.sitemapExecutor; |
| } |
| } |
| |
| /** |
| * Configure the tree processor: |
| * <processor file="{Location of the sitemap}" |
| * check-reload="{true|false}" |
| * config="{Location of sitemap tree processor config}> |
| * <reload delay="10"/> |
| * </processor> |
| * |
| * Only the file attribute is required; everything else is optional. |
| * |
| * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) |
| */ |
| public void configure(Configuration config) |
| throws ConfigurationException { |
| |
| this.checkReload = config.getAttributeAsBoolean("check-reload", |
| this.core.getSettings().isReloadingEnabled("sitemap")); |
| |
| // Obtain the configuration file, or use the XCONF_URL if none |
| // is defined |
| String xconfURL = config.getAttribute("config", XCONF_URL); |
| |
| // Reload check delay. Default is 1 second. |
| this.lastModifiedDelay = config.getChild("reload").getAttributeAsLong("delay", this.core.getSettings().getReloadDelay("sitemap")); |
| |
| String fileName = config.getAttribute("file", "sitemap.xmap"); |
| |
| try { |
| this.source = new DelayedRefreshSourceWrapper(this.resolver.resolveURI(fileName), lastModifiedDelay); |
| } catch (Exception e) { |
| throw new ConfigurationException("Cannot resolve " + fileName, e); |
| } |
| |
| // Read the builtin languages definition file |
| try { |
| Source source = this.resolver.resolveURI(xconfURL); |
| try { |
| SAXConfigurationHandler handler = new SAXConfigurationHandler(); |
| SourceUtil.toSAX(this.manager, source, null, handler); |
| this.treeBuilderConfiguration = handler.getConfiguration(); |
| } finally { |
| this.resolver.release(source); |
| } |
| } catch (Exception e) { |
| String msg = "Error while reading " + xconfURL + ": " + e.getMessage(); |
| throw new ConfigurationException(msg, e); |
| } |
| } |
| |
| /** |
| * Process the given <code>Environment</code> producing the output. |
| * @return If the processing is successfull <code>true</code> is returned. |
| * If not match is found in the sitemap <code>false</code> |
| * is returned. |
| * @throws org.apache.cocoon.ResourceNotFoundException If a sitemap component tries |
| * to access a resource which can not |
| * be found, e.g. the generator |
| * ConnectionResetException If the connection was reset |
| */ |
| public boolean process(Environment environment) throws Exception { |
| // Get the concrete processor and delegate it the job |
| setupConcreteProcessor(environment); |
| return this.concreteProcessor.process(environment); |
| } |
| |
| |
| /** |
| * Process the given <code>Environment</code> to assemble |
| * a <code>ProcessingPipeline</code>. |
| * @since 2.1 |
| */ |
| public InternalPipelineDescription buildPipeline(Environment environment) |
| throws Exception { |
| // Get the concrete processor and delegate it the job |
| setupConcreteProcessor(environment); |
| return this.concreteProcessor.buildPipeline(environment); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.Processor#getRootProcessor() |
| */ |
| public Processor getRootProcessor() { |
| TreeProcessor result = this; |
| while (result.parent != null) { |
| result = result.parent; |
| } |
| |
| return result; |
| } |
| |
| // /** |
| // * Set the sitemap component configurations |
| // */ |
| // public void setComponentConfigurations(Configuration componentConfigurations) { |
| // this.concreteProcessor.setComponentConfigurations(componentConfigurations); |
| // } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.Processor#getComponentConfigurations() |
| */ |
| public Configuration[] getComponentConfigurations() { |
| return this.concreteProcessor.getComponentConfigurations(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.Processor#getContext() |
| */ |
| public String getContext() { |
| return this.environmentHelper.getContext(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.Processor#getEnvironmentHelper() |
| */ |
| public org.apache.cocoon.environment.SourceResolver getSourceResolver() { |
| return this.environmentHelper; |
| } |
| |
| /** |
| * The current environment helper used by the MountNode |
| * @return EnvironmentHelper |
| */ |
| public EnvironmentHelper getEnvironmentHelper() { |
| return this.environmentHelper; |
| } |
| |
| /** |
| * Get the tree builder role from the sitemap program (as a configuration object). |
| * This method should report very any problem very clearly, as it is the entry point of any |
| * Cocoon application. |
| * |
| * @param sitemapProgram the sitemap |
| * @return the treebuilder role |
| * @throws ConfigurationException if a suitable role could not be found |
| */ |
| private TreeBuilder getTreeBuilder(Configuration sitemapProgram) throws ConfigurationException { |
| String ns = sitemapProgram.getNamespace(); |
| |
| RE re = new RE("http://apache.org/cocoon/sitemap/(\\d\\.\\d)"); |
| if (!re.match(ns)) { |
| throw new ConfigurationException("Unknown sitemap namespace (" + ns + ") at " + |
| this.source.getURI()); |
| } |
| |
| String version = re.getParen(1); |
| String result = TreeBuilder.ROLE + "/sitemap-" + version; |
| |
| try { |
| return (TreeBuilder) this.manager.lookup(result); |
| } catch (Exception e) { |
| throw new ConfigurationException("This version of Cocoon does not handle sitemap version " + |
| version + " at " + this.source.getURI(), e); |
| } |
| } |
| |
| /** |
| * Sets up the concrete processor, building or rebuilding it if necessary. |
| */ |
| private void setupConcreteProcessor(Environment env) throws Exception { |
| |
| if (this.parent == null) { |
| // Ensure root sitemap uses the correct context, even if not located in the webapp context |
| this.environmentHelper.changeContext(this.source, ""); |
| } |
| |
| if (this.concreteProcessor == null || this.concreteProcessor.isReloadNeeded() || |
| (this.checkReload && this.source.getLastModified() != this.lastModified)) { |
| buildConcreteProcessor(env); |
| } |
| } |
| |
| private JavaCompiler createJavaCompiler(final Configuration config) { |
| // FIXME: extract compiler and compiler configuration from config |
| return new EclipseJavaCompiler(); |
| } |
| |
| private ResourceStore createResourceStore(final Configuration storeConfig) throws Exception { |
| final String className = storeConfig.getAttribute("class","org.apache.commons.jci.stores.MemoryResourceStore"); |
| final ResourceStore store = (ResourceStore) Class.forName(className).newInstance(); |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("storing resources in " + store.getClass().getName()); |
| } |
| return store; |
| } |
| |
| private FilesystemAlterationListener createCompilingListener( |
| final Configuration dirConfig |
| ) throws Exception { |
| Source src = null; |
| |
| try { |
| src = resolver.resolveURI(dirConfig.getAttribute("src")); |
| final File repository = new File(src.getURI().substring(5)); |
| |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("monitoring src dir " + dirConfig.getAttribute("src")); |
| } |
| |
| return new CompilingListener( |
| repository, |
| createJavaCompiler(dirConfig.getChild("compiler")), |
| new TransactionalResourceStore(createResourceStore(dirConfig.getChild("store"))) |
| ); |
| } finally { |
| resolver.release(src); |
| } |
| } |
| |
| private FilesystemAlterationListener createReloadingListener(final Configuration dirConfig) |
| throws Exception { |
| |
| Source src = null; |
| |
| try { |
| src = resolver.resolveURI(dirConfig.getAttribute("src")); |
| final File repository = new File(src.getURI().substring(5)); |
| |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("monitoring class dir " + dirConfig.getAttribute("src")); |
| } |
| |
| return new ReloadingListener( |
| repository, |
| createResourceStore(dirConfig.getChild("store")) |
| ); |
| } finally { |
| resolver.release(src); |
| } |
| } |
| |
| private FilesystemAlterationListener createFileChangeListener( |
| final Configuration dirConfig |
| ) throws Exception { |
| Source src = null; |
| |
| try { |
| src = resolver.resolveURI(dirConfig.getAttribute("src")); |
| final File repository = new File(src.getURI().substring(5)); |
| |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("monitoring lib dir " + dirConfig.getAttribute("src")); |
| } |
| |
| return new FileChangeListener(repository); |
| } finally { |
| resolver.release(src); |
| } |
| } |
| |
| |
| private boolean containsListener(Map map, String src, Class clazz) { |
| FilesystemAlterationListener listener = (FilesystemAlterationListener) map.get(src); |
| if (listener == null) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("no previous listener for " + src); |
| } |
| return false; |
| } |
| if (!listener.getClass().equals(clazz)) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("previous listener was of a different type"); |
| } |
| return false; |
| } |
| |
| return true; |
| } |
| |
| private Map createClasspathListeners(Map oldListeners, Configuration classpathConfig) throws Exception { |
| |
| final Configuration[] classDirConfigs = classpathConfig.getChildren("class-dir"); |
| final Configuration[] srcDirConfigs = classpathConfig.getChildren("src-dir"); |
| final Configuration[] libDirConfigs = classpathConfig.getChildren("lib-dir"); |
| |
| Map newListeners = new HashMap(); |
| |
| for (int i = 0; i < classDirConfigs.length; i++) { |
| final Configuration dirConfig = classDirConfigs[i]; |
| final String src = dirConfig.getAttribute("src"); |
| if (containsListener(oldListeners, src, ReloadingListener.class)) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("keeping ReloadingListener for " + src); |
| } |
| newListeners.put(src, oldListeners.get(src)); |
| oldListeners.remove(src); |
| } else { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("new ReloadingListener for " + src); |
| } |
| newListeners.put(src, createReloadingListener(dirConfig)); |
| } |
| } |
| |
| for (int i = 0; i < srcDirConfigs.length; i++) { |
| final Configuration dirConfig = srcDirConfigs[i]; |
| final String src = dirConfig.getAttribute("src"); |
| if (containsListener(oldListeners, src, CompilingListener.class)) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("keeping CompilingListener for " + src); |
| } |
| newListeners.put(src, oldListeners.get(src)); |
| oldListeners.remove(src); |
| } else { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("new CompilingListener for " + src); |
| } |
| newListeners.put(src, createCompilingListener(dirConfig)); |
| } |
| } |
| |
| for (int i = 0; i < libDirConfigs.length; i++) { |
| final Configuration dirConfig = libDirConfigs[i]; |
| final String src = dirConfig.getAttribute("src"); |
| if (containsListener(oldListeners, src, FileChangeListener.class)) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("keeping FileChangeListener for " + src); |
| } |
| newListeners.put(src, oldListeners.get(src)); |
| oldListeners.remove(src); |
| } else { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("new FileChangeListener for " + src); |
| } |
| newListeners.put(src, createFileChangeListener(dirConfig)); |
| } |
| } |
| |
| return newListeners; |
| } |
| |
| protected ClassLoader createClassLoader(Configuration classpathConfig) |
| throws Exception { |
| String factoryRole = classpathConfig.getAttribute("factory-role", ClassLoaderFactory.ROLE + "/ReloadingClassLoaderFactory"); |
| // Create a new classloader |
| ClassLoaderFactory clFactory = (ClassLoaderFactory)this.manager.lookup(factoryRole); |
| try { |
| return clFactory.createClassLoader( |
| Thread.currentThread().getContextClassLoader(), |
| classpathConfig |
| ); |
| } finally { |
| this.manager.release(clFactory); |
| } |
| } |
| |
| |
| private Configuration createSitemapProgram(Source source) throws ProcessingException, SAXException, IOException { |
| NamespacedSAXConfigurationHandler handler = new NamespacedSAXConfigurationHandler(); |
| AnnotationsFilter annotationsFilter = new AnnotationsFilter(handler); |
| SourceUtil.toSAX(source, annotationsFilter); |
| return handler.getConfiguration(); |
| } |
| |
| private void subscribeListeners(Map listerens, ConcreteTreeProcessor processor) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("setting up listeners " + listerens); |
| } |
| for (final Iterator it = listerens.values().iterator(); it.hasNext();) { |
| final NotifyingListener newListener = (NotifyingListener) it.next(); |
| |
| newListener.setNotificationListener(processor); |
| |
| fam.subscribe(newListener); |
| } |
| } |
| |
| private void unsubscribeListeners(Map listerens) { |
| if (listerens != null && listerens.size() > 0) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("unsubscribing " + listerens + " from fam"); |
| } |
| for (final Iterator it = listerens.values().iterator(); it.hasNext();) { |
| final FilesystemAlterationListener oldListener = (FilesystemAlterationListener) it.next(); |
| fam.unsubscribe(oldListener); |
| } |
| } |
| } |
| |
| private void waitForInitialCompilation(Map listeners) throws Exception { |
| if (listeners.size() > 0) { |
| // wait for the new ones to complete for the first time |
| for (final Iterator it = listeners.values().iterator(); it.hasNext();) { |
| final NotifyingListener newListener = (NotifyingListener) it.next(); |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("waiting for initial compilation"); |
| } |
| newListener.waitForFirstCheck(); |
| } |
| } |
| } |
| |
| private void provideClasses(Map listeners, ClassLoader classloader) { |
| for (final Iterator it = listeners.values().iterator(); it.hasNext();) { |
| final NotifyingListener newListener = (NotifyingListener) it.next(); |
| |
| if (newListener instanceof ResourceStoringListener) { |
| ResourceStoringListener l = (ResourceStoringListener)newListener; |
| if (classloader instanceof ReloadingClassLoaderFactory.DefaultClassLoader) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("adding store " + l.getStore() + " to classloader"); |
| } |
| ReloadingClassLoaderFactory.DefaultClassLoader cl = (ReloadingClassLoaderFactory.DefaultClassLoader) classloader; |
| cl.addResourceStore(l.getStore()); |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Build the concrete processor (i.e. loads the sitemap). Should be called |
| * only by setupProcessor(); |
| */ |
| private synchronized void buildConcreteProcessor(Environment env) throws Exception { |
| |
| // Now that we entered the synchronized area, recheck what's already |
| // been checked in process(). |
| if (this.concreteProcessor != null && source.getLastModified() == this.lastModified && !this.concreteProcessor.isReloadNeeded()) { |
| // Nothing changed |
| return; |
| } |
| |
| long startTime = System.currentTimeMillis(); |
| long newLastModified; |
| ConcreteTreeProcessor newProcessor; |
| ConcreteTreeProcessor oldProcessor = this.concreteProcessor; |
| Map oldListeners = Collections.EMPTY_MAP; |
| Map newListeners = Collections.EMPTY_MAP; |
| |
| if (oldProcessor != null) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("found a previous ConcreteTreeProcessor"); |
| } |
| oldListeners = oldProcessor.getClasspathListeners(); |
| } else { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("first version of the ConcreteTreeProcessor"); |
| } |
| } |
| |
| // Dispose the old processor, if any |
| if (oldProcessor != null) { |
| oldProcessor.markForDisposal(); |
| } |
| |
| |
| // We have to do a call to enterProcessor() here as during building |
| // of the tree, components (e.g. actions) are already instantiated |
| // (ThreadSafe ones mostly). |
| // If these components try to access the current processor or the |
| // current service manager they must get this one - which is currently |
| // in the process of initialization. |
| EnvironmentHelper.enterProcessor(this, this.manager, env); |
| |
| try { |
| |
| Configuration sitemapProgram = createSitemapProgram(this.source); |
| newLastModified = this.source.getLastModified(); |
| |
| newProcessor = createConcreteTreeProcessor(); |
| |
| // setup sitemap specific classloader |
| // (RP) Should we really support sitemap specific classloader when the global |
| // BlocksClassloader is in place? |
| Configuration classpathConfig = sitemapProgram.getChild("components").getChild("classpath", false); |
| if (classpathConfig != null) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("ConcreteTreeProcessor has a special classpath"); |
| } |
| |
| // create a reloading classloader and make it the context classloader |
| ClassLoader classloader = createClassLoader(classpathConfig); |
| Thread.currentThread().setContextClassLoader(classloader); |
| |
| // create the listeners for all classpath entries (lib, classes, src) |
| newListeners = createClasspathListeners(oldListeners, classpathConfig); |
| |
| // store the listeners in the the concreteTreeProcessor instance |
| newProcessor.setClasspathListeners(newListeners); |
| |
| // subscribe all listeners to filesystem altering monitor (FAM) |
| subscribeListeners(newListeners, newProcessor); |
| |
| // use the information about the listeners to create the classpath |
| provideClasses(newListeners, classloader); |
| } |
| |
| unsubscribeListeners(oldListeners); |
| |
| waitForInitialCompilation(newListeners); |
| |
| |
| // Get the treebuilder that can handle this version of the sitemap. |
| TreeBuilder treeBuilder = getTreeBuilder(sitemapProgram); |
| try { |
| treeBuilder.setProcessor(newProcessor); |
| treeBuilder.setParentProcessorManager(this.manager); |
| |
| ProcessingNode root = treeBuilder.build(sitemapProgram); |
| newProcessor.setProcessorData( |
| treeBuilder.getBuiltProcessorManager(), |
| treeBuilder.getBuiltProcessorClassLoader(), |
| root, |
| treeBuilder.getDisposableNodes(), |
| treeBuilder.getComponentLocator(), |
| treeBuilder.getEnterSitemapEventListeners(), |
| treeBuilder.getLeaveSitemapEventListeners()); |
| |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("ConcreteTreeProcessor ready"); |
| } |
| |
| // Get the actual interpreter |
| FlowNode flowNode = (FlowNode)treeBuilder.getRegisteredNode("flow"); |
| if ( flowNode != null ) { |
| final Interpreter interpreter = flowNode.getInterpreter(); |
| newProcessor.setAttribute(Interpreter.ROLE, interpreter); |
| } |
| |
| } finally { |
| this.manager.release(treeBuilder); |
| } |
| } finally { |
| EnvironmentHelper.leaveProcessor(); |
| } |
| |
| if (getLogger().isDebugEnabled()) { |
| double time = (System.currentTimeMillis() - startTime) / 1000.0; |
| getLogger().debug("TreeProcessor built in " + time + " secs from " + source.getURI()); |
| } |
| |
| // Switch to the new processor (ensure it's never temporarily null) |
| this.concreteProcessor = newProcessor; |
| this.lastModified = newLastModified; |
| } |
| |
| private ConcreteTreeProcessor createConcreteTreeProcessor() { |
| ConcreteTreeProcessor newProcessor = new ConcreteTreeProcessor(this, this.sitemapExecutor); |
| setupLogger(newProcessor); |
| return newProcessor; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.activity.Disposable#dispose() |
| */ |
| public void dispose() { |
| // Dispose the concrete processor. No need to check for existing requests, as there |
| // are none when a TreeProcessor is disposed. |
| ContainerUtil.dispose(this.concreteProcessor); |
| this.concreteProcessor = null; |
| |
| if (this.releaseSitemapExecutor) { |
| this.manager.release(this.sitemapExecutor); |
| this.sitemapExecutor = null; |
| } |
| |
| if (this.manager != null) { |
| if (this.source != null) { |
| this.resolver.release(this.source.getSource()); |
| this.source = null; |
| } |
| this.manager.release(this.fam); |
| this.manager.release(this.resolver); |
| this.manager.release(this.core); |
| this.resolver = null; |
| this.manager = null; |
| this.core = null; |
| } |
| } |
| |
| /** |
| * @see org.apache.cocoon.Processor#getAttribute(java.lang.String) |
| */ |
| public Object getAttribute(String name) { |
| return this.concreteProcessor.getAttribute(name); |
| } |
| |
| /** |
| * @see org.apache.cocoon.Processor#removeAttribute(java.lang.String) |
| */ |
| public Object removeAttribute(String name) { |
| return this.concreteProcessor.removeAttribute(name); |
| } |
| |
| /** |
| * @see org.apache.cocoon.Processor#setAttribute(java.lang.String, java.lang.Object) |
| */ |
| public void setAttribute(String name, Object value) { |
| this.concreteProcessor.setAttribute(name, value); |
| } |
| } |