Save This Page
Home » cocoon-2.1.11-src » org.apache » cocoon » components » treeprocessor » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.cocoon.components.treeprocessor;
   18   
   19   import java.util.Map;
   20   
   21   import org.apache.avalon.excalibur.component.RoleManageable;
   22   import org.apache.avalon.excalibur.component.RoleManager;
   23   import org.apache.avalon.framework.activity.Disposable;
   24   import org.apache.avalon.framework.component.Component;
   25   import org.apache.avalon.framework.component.ComponentException;
   26   import org.apache.avalon.framework.component.ComponentManager;
   27   import org.apache.avalon.framework.component.Composable;
   28   import org.apache.avalon.framework.component.Recomposable;
   29   import org.apache.avalon.framework.configuration.Configurable;
   30   import org.apache.avalon.framework.configuration.Configuration;
   31   import org.apache.avalon.framework.configuration.ConfigurationException;
   32   import org.apache.avalon.framework.configuration.SAXConfigurationHandler;
   33   import org.apache.avalon.framework.container.ContainerUtil;
   34   import org.apache.avalon.framework.context.Context;
   35   import org.apache.avalon.framework.context.ContextException;
   36   import org.apache.avalon.framework.context.Contextualizable;
   37   import org.apache.avalon.framework.logger.AbstractLogEnabled;
   38   import org.apache.avalon.framework.thread.ThreadSafe;
   39   import org.apache.cocoon.Processor;
   40   import org.apache.cocoon.util.Settings;
   41   import org.apache.cocoon.util.SettingsHelper;
   42   import org.apache.cocoon.components.CocoonComponentManager;
   43   import org.apache.cocoon.components.ExtendedComponentSelector;
   44   import org.apache.cocoon.components.LifecycleHelper;
   45   import org.apache.cocoon.components.PropertyAwareSAXConfigurationHandler;
   46   import org.apache.cocoon.components.pipeline.ProcessingPipeline;
   47   import org.apache.cocoon.components.source.SourceUtil;
   48   import org.apache.cocoon.components.source.impl.DelayedRefreshSourceWrapper;
   49   import org.apache.cocoon.environment.Environment;
   50   import org.apache.excalibur.source.Source;
   51   import org.apache.excalibur.source.SourceResolver;
   52   
   53   /**
   54    * Interpreted tree-traversal implementation of a pipeline assembly language.
   55    *
   56    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   57    * @version CVS $Id: TreeProcessor.java 540711 2007-05-22 19:36:07Z cziegeler $
   58    */
   59   
   60   public class TreeProcessor
   61       extends AbstractLogEnabled
   62       implements ThreadSafe,
   63                  Processor,
   64                  Composable,
   65                  Configurable,
   66                  RoleManageable,
   67                  Contextualizable,
   68                  Disposable {
   69   
   70       public static final String COCOON_REDIRECT_ATTR = "sitemap:cocoon-redirect";
   71   
   72       private static final String XCONF_URL =
   73           "resource://org/apache/cocoon/components/treeprocessor/treeprocessor-builtins.xml";
   74   
   75       /** The parent TreeProcessor, if any */
   76       protected TreeProcessor parent;
   77   
   78       /** The context */
   79       protected Context context;
   80   
   81       /** The component manager */
   82       protected ComponentManager manager;
   83   
   84       /** The role manager */
   85       protected RoleManager roleManager;
   86   
   87       /** Selector of TreeBuilders, the hint is the language name */
   88       protected ExtendedComponentSelector builderSelector;
   89   
   90       /** Last modification time */
   91       protected long lastModified = 0;
   92   
   93       /** The source of the tree definition */
   94       protected DelayedRefreshSourceWrapper source;
   95   
   96       /** Delay for <code>sourceLastModified</code>. */
   97       protected long lastModifiedDelay;
   98   
   99       /** The current language configuration */
  100       protected Configuration currentLanguage;
  101   
  102       /** Check for reload? */
  103       protected boolean checkReload;
  104   
  105       /** The source resolver */
  106       protected SourceResolver resolver;
  107   
  108       /** The actual processor (package-private as needs to be accessed by ConcreteTreeProcessor) */
  109       ConcreteTreeProcessor concreteProcessor;
  110   
  111       /**
  112        * Create a TreeProcessor.
  113        */
  114       public TreeProcessor() {
  115           this.checkReload = true;
  116           this.lastModifiedDelay = 1000;
  117       }
  118   
  119       /**
  120        * Create a child processor for a given language
  121        */
  122       protected TreeProcessor(TreeProcessor parent, ComponentManager manager) {
  123           this.parent = parent;
  124   
  125           // Copy all that can be copied from the parent
  126           this.enableLogging(parent.getLogger());
  127           this.context = parent.context;
  128           this.roleManager = parent.roleManager;
  129           this.builderSelector = parent.builderSelector;
  130           this.checkReload = parent.checkReload;
  131           this.lastModifiedDelay = parent.lastModifiedDelay;
  132   
  133           // We have our own CM
  134           this.manager = manager;
  135       }
  136   
  137       /**
  138        * Create a new child of this processor (used for mounting submaps).
  139        *
  140        * @param manager the component manager to be used by the child processor.
  141        * @return a new child processor.
  142        */
  143       public TreeProcessor createChildProcessor(ComponentManager manager,
  144                                                 String           actualSource,
  145                                                 boolean          checkReload)
  146       throws Exception {
  147   
  148           // Note: lifecycle methods aren't called, since this constructors copies all
  149           // that can be copied from the parent (see above)
  150           TreeProcessor child = new TreeProcessor(this, manager);
  151           child.checkReload = checkReload;
  152           child.resolver = (SourceResolver)manager.lookup(SourceResolver.ROLE);
  153           child.source = new DelayedRefreshSourceWrapper(child.resolver.resolveURI(actualSource), this.lastModifiedDelay);
  154   
  155           return child;
  156       }
  157   
  158       /* (non-Javadoc)
  159        * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
  160        */
  161       public void contextualize(Context context) throws ContextException {
  162           this.context = context;
  163       }
  164   
  165       /* (non-Javadoc)
  166        * @see org.apache.avalon.framework.component.Composable#compose(org.apache.avalon.framework.component.ComponentManager)
  167        */
  168       public void compose(ComponentManager manager) throws ComponentException {
  169           this.manager = manager;
  170           this.resolver = (SourceResolver)this.manager.lookup(SourceResolver.ROLE);
  171       }
  172   
  173       /* (non-Javadoc)
  174        * @see org.apache.avalon.excalibur.component.RoleManageable#setRoleManager(org.apache.avalon.excalibur.component.RoleManager)
  175        */
  176       public void setRoleManager(RoleManager rm) {
  177           this.roleManager = rm;
  178       }
  179   
  180   
  181   /*
  182     <processor>
  183       <reload delay="10"/>
  184       <language>...</language>
  185     </processor>
  186   */
  187       public void configure(Configuration config)
  188       throws ConfigurationException {
  189   
  190           this.checkReload = config.getAttributeAsBoolean("check-reload", true);
  191   
  192           // Obtain the configuration file, or use the XCONF_URL if none
  193           // is defined
  194           String xconfURL = config.getAttribute("config", XCONF_URL);
  195   
  196           // Reload check delay. Default is 1 second.
  197           this.lastModifiedDelay = config.getChild("reload").getAttributeAsLong("delay", 1000L);
  198   
  199           String fileName = config.getAttribute("file", "sitemap.xmap");
  200   
  201           try {
  202               this.source = new DelayedRefreshSourceWrapper(this.resolver.resolveURI(fileName), this.lastModifiedDelay);
  203           } catch (Exception e) {
  204               throw new ConfigurationException("Cannot resolve " + fileName, e);
  205           }
  206   
  207           // Read the builtin languages definition file
  208           Configuration builtin;
  209           try {
  210               Source source = this.resolver.resolveURI(xconfURL);
  211               try {
  212                   Settings settings = SettingsHelper.getSettings(this.context);
  213                   SAXConfigurationHandler handler = new PropertyAwareSAXConfigurationHandler(settings, this.getLogger());
  214                   SourceUtil.toSAX( this.manager, source, null, handler);
  215                   builtin = handler.getConfiguration();
  216               } finally {
  217                   this.resolver.release(source);
  218               }
  219           } catch(Exception e) {
  220               String msg = "Error while reading " + xconfURL + ": " + e.getMessage();
  221               throw new ConfigurationException(msg, e);
  222           }
  223   
  224           // Create a selector for tree builders of all languages
  225           this.builderSelector = new ExtendedComponentSelector(Thread.currentThread().getContextClassLoader());
  226           try {
  227               LifecycleHelper.setupComponent(this.builderSelector,
  228                                              this.getLogger(),
  229                                              this.context,
  230                                              this.manager,
  231                                              this.roleManager,
  232                                              builtin);
  233           } catch (ConfigurationException e) {
  234               throw e;
  235           } catch (Exception e) {
  236               throw new ConfigurationException("Could not setup builder selector", e);
  237           }
  238       }
  239   
  240       /**
  241        * Process the given <code>Environment</code> producing the output.
  242        * @return If the processing is successfull <code>true</code> is returned.
  243        *         If not match is found in the sitemap <code>false</code>
  244        *         is returned.
  245        * @throws org.apache.cocoon.ResourceNotFoundException If a sitemap component tries
  246        *                                   to access a resource which can not
  247        *                                   be found, e.g. the generator
  248        *         ConnectionResetException  If the connection was reset
  249        */
  250       public boolean process(Environment environment) throws Exception {
  251   
  252           this.setupConcreteProcessor(environment);
  253   
  254           return this.concreteProcessor.process(environment);
  255       }
  256   
  257       /**
  258        * Process the given <code>Environment</code> to assemble
  259        * a <code>ProcessingPipeline</code>.
  260        * @since 2.1
  261        */
  262       public ProcessingPipeline buildPipeline(Environment environment)
  263       throws Exception {
  264   
  265       		this.setupConcreteProcessor(environment);
  266   
  267       		return this.concreteProcessor.buildPipeline(environment);
  268       }
  269   
  270       /* (non-Javadoc)
  271        * @see org.apache.cocoon.Processor#getRootProcessor()
  272        */
  273       public Processor getRootProcessor() {
  274           TreeProcessor result = this;
  275           while(result.parent != null) {
  276               result = result.parent;
  277           }
  278   
  279           return result;
  280       }
  281   
  282       /**
  283        * Set the sitemap component configurations
  284        */
  285       public void setComponentConfigurations(Configuration componentConfigurations) {
  286           this.concreteProcessor.setComponentConfigurations(componentConfigurations);
  287       }
  288   
  289       /* (non-Javadoc)
  290        * @see org.apache.cocoon.Processor#getComponentConfigurations()
  291        */
  292       public Map getComponentConfigurations() {
  293           return this.concreteProcessor.getComponentConfigurations();
  294       }
  295   
  296       private void setupConcreteProcessor(Environment env) throws Exception {
  297   
  298           if (this.parent == null) {
  299               // Ensure root sitemap uses the correct context, even if not located in the webapp context
  300               env.changeContext("", this.source.getURI());
  301           }
  302   
  303           // check for sitemap changes
  304           if (this.concreteProcessor == null ||
  305               (this.checkReload && this.source.getLastModified() != this.lastModified)) {
  306               this.buildConcreteProcessor(env);
  307           }
  308       }
  309   
  310       private synchronized void buildConcreteProcessor(Environment env) throws Exception {
  311   
  312           // Now that we entered the synchronized area, recheck what's already
  313           // been checked in process().
  314           if (this.concreteProcessor != null && this.source.getLastModified() == this.lastModified) {
  315               // Nothing changed
  316               return;
  317           }
  318   
  319           long startTime = System.currentTimeMillis();
  320   
  321           // Dispose the old processor, if any
  322           if (this.concreteProcessor != null) {
  323               this.concreteProcessor.markForDisposal();
  324           }
  325   
  326           // Get a builder
  327           TreeBuilder builder = (TreeBuilder)this.builderSelector.select("sitemap");
  328           ConcreteTreeProcessor newProcessor = new ConcreteTreeProcessor(this);
  329           long newLastModified;
  330           this.setupLogger(newProcessor);
  331           //FIXME (SW): why do we need to enterProcessor here?
  332           CocoonComponentManager.enterEnvironment(env, this.manager, this);
  333           try {
  334               if (builder instanceof Recomposable) {
  335                   ((Recomposable)builder).recompose(this.manager);
  336               }
  337               builder.setProcessor(newProcessor);
  338   
  339               newLastModified = this.source.getLastModified();
  340   
  341               ProcessingNode root = builder.build(this.source);
  342   
  343               newProcessor.setProcessorData(builder.getSitemapComponentManager(), root, builder.getDisposableNodes());
  344           } finally {
  345               CocoonComponentManager.leaveEnvironment();
  346               this.builderSelector.release(builder);
  347           }
  348   
  349           if (this.getLogger().isDebugEnabled()) {
  350               double time = (this.lastModified - startTime) / 1000.0;
  351               this.getLogger().debug("TreeProcessor built in " + time + " secs from " + this.source.getURI());
  352           }
  353   
  354           // Switch to the new processor (ensure it's never temporarily null)
  355           this.concreteProcessor = newProcessor;
  356           this.lastModified = newLastModified;
  357       }
  358   
  359       /* (non-Javadoc)
  360        * @see org.apache.avalon.framework.activity.Disposable#dispose()
  361        */
  362       public void dispose() {
  363           // Dispose the concrete processor. No need to check for existing requests, as there
  364           // are none when a TreeProcessor is disposed.
  365           ContainerUtil.dispose(this.concreteProcessor);
  366           this.concreteProcessor = null;
  367   
  368           if (this.manager != null) {
  369   	        if (this.source != null) {
  370   	            this.resolver.release(this.source.getSource());
  371   	            this.source = null;
  372   	        }
  373   
  374               if (this.parent == null) {
  375                   // root processor : dispose the builder selector
  376                   this.builderSelector.dispose();
  377                   this.builderSelector = null;
  378               }
  379   
  380               // Release resolver looked up in compose()
  381               this.manager.release((Component)this.resolver);
  382               this.resolver = null;
  383   
  384               this.manager = null;
  385   	    }
  386   	}
  387   
  388       public String toString() {
  389           return "TreeProcessor - " + (this.source == null ? "[unknown location]" : this.source.getURI());
  390       }
  391   }

Save This Page
Home » cocoon-2.1.11-src » org.apache » cocoon » components » treeprocessor » [javadoc | source]