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.io.IOException;
   20   import java.net.MalformedURLException;
   21   import java.util.Collections;
   22   import java.util.HashMap;
   23   import java.util.List;
   24   import java.util.Map;
   25   
   26   import org.apache.avalon.framework.activity.Disposable;
   27   import org.apache.avalon.framework.component.ComponentManager;
   28   import org.apache.avalon.framework.configuration.Configuration;
   29   import org.apache.avalon.framework.logger.AbstractLogEnabled;
   30   import org.apache.avalon.framework.logger.Logger;
   31   import org.apache.cocoon.ProcessingException;
   32   import org.apache.cocoon.Processor;
   33   import org.apache.cocoon.components.ChainedConfiguration;
   34   import org.apache.cocoon.components.CocoonComponentManager;
   35   import org.apache.cocoon.components.pipeline.ProcessingPipeline;
   36   import org.apache.cocoon.environment.Environment;
   37   import org.apache.cocoon.environment.ForwardRedirector;
   38   import org.apache.cocoon.environment.Redirector;
   39   import org.apache.cocoon.environment.wrapper.EnvironmentWrapper;
   40   import org.apache.cocoon.environment.wrapper.MutableEnvironmentFacade;
   41   
   42   /**
   43    * The concrete implementation of {@link Processor}, containing the evaluation tree and associated
   44    * data such as component manager.
   45    *
   46    * @version $Id: ConcreteTreeProcessor.java 602257 2007-12-07 22:43:53Z anathaniel $
   47    */
   48   public class ConcreteTreeProcessor extends AbstractLogEnabled
   49                                      implements Processor, Disposable {
   50   
   51   	/** The processor that wraps us */
   52   	private TreeProcessor wrappingProcessor;
   53   
   54   	/** Component manager defined by the <map:components> of this sitemap */
   55       ComponentManager sitemapComponentManager;
   56   
   57     	/** Processing nodes that need to be disposed with this processor */
   58       private List disposableNodes;
   59   
   60       /** Root node of the processing tree */
   61       private ProcessingNode rootNode;
   62   
   63       private Map sitemapComponentConfigurations;
   64   
   65       private Configuration componentConfigurations;
   66   
   67       /** Number of simultaneous uses of this processor (either by concurrent request or by internal requests) */
   68       private int requestCount;
   69   
   70   	/** Builds a concrete processig, given the wrapping processor */
   71   	public ConcreteTreeProcessor(TreeProcessor wrappingProcessor) {
   72   		this.wrappingProcessor = wrappingProcessor;
   73   	}
   74   
   75   	/** Set the processor data, result of the treebuilder job */
   76   	public void setProcessorData(ComponentManager manager, ProcessingNode rootNode, List disposableNodes) {
   77           if (this.sitemapComponentManager != null) {
   78               throw new IllegalStateException("setProcessorData() can only be called once");
   79           }
   80   
   81           this.sitemapComponentManager = manager;
   82           this.rootNode = rootNode;
   83           this.disposableNodes = disposableNodes;
   84   	}
   85   
   86   	/** Set the sitemap component configurations (called as part of the tree building process) */
   87       public void setComponentConfigurations(Configuration componentConfigurations) {
   88           this.componentConfigurations = componentConfigurations;
   89           this.sitemapComponentConfigurations = null;
   90       }
   91   
   92       /**
   93        * Get the sitemap component configurations
   94        * @since 2.1
   95        */
   96       public Map getComponentConfigurations() {
   97           // do we have the sitemap configurations prepared for this processor?
   98           if ( null == this.sitemapComponentConfigurations ) {
   99   
  100               synchronized (this) {
  101   
  102                   if ( this.sitemapComponentConfigurations == null ) {
  103                       // do we have configurations?
  104                       final Configuration[] childs = (this.componentConfigurations == null
  105                                                        ? null
  106                                                        : this.componentConfigurations.getChildren());
  107   
  108                       if ( null != childs ) {
  109   
  110                           if ( null == this.wrappingProcessor.parent ) {
  111                               this.sitemapComponentConfigurations = new HashMap(12);
  112                           } else {
  113                               // copy all configurations from parent
  114                               this.sitemapComponentConfigurations = new HashMap(
  115                               			this.wrappingProcessor.parent.getComponentConfigurations());
  116                           }
  117   
  118                           // and now check for new configurations
  119                           for(int m = 0; m < childs.length; m++) {
  120   
  121                               final String r = this.wrappingProcessor.roleManager.getRoleForName(childs[m].getName());
  122                               this.sitemapComponentConfigurations.put(r, new ChainedConfiguration(childs[m],
  123                                                                                (ChainedConfiguration)this.sitemapComponentConfigurations.get(r)));
  124                           }
  125                       } else {
  126                           // we don't have configurations
  127                           if ( null == this.wrappingProcessor.parent ) {
  128                               this.sitemapComponentConfigurations = Collections.EMPTY_MAP;
  129                           } else {
  130                               // use configuration from parent
  131                               this.sitemapComponentConfigurations = this.wrappingProcessor.parent.getComponentConfigurations();
  132                           }
  133                       }
  134                   }
  135               }
  136           }
  137           return this.sitemapComponentConfigurations;    }
  138   
  139       /**
  140        * Mark this processor as needing to be disposed. Actual call to {@link #dispose()} will occur when
  141        * all request processings on this processor will be terminated.
  142        */
  143   	public void markForDisposal() {
  144   		// Decrement the request count (negative number means dispose)
  145   		synchronized(this) {
  146   			this.requestCount--;
  147   		}
  148   
  149   		if (this.requestCount < 0) {
  150   			// No more users : dispose right now
  151   			dispose();
  152   		}
  153   	}
  154   
  155   	public TreeProcessor getWrappingProcessor() {
  156   		return this.wrappingProcessor;
  157   	}
  158   
  159   	public Processor getRootProcessor() {
  160   		return this.wrappingProcessor.getRootProcessor();
  161   	}
  162   
  163       /**
  164        * Process the given <code>Environment</code> producing the output.
  165        * @return If the processing is successfull <code>true</code> is returned.
  166        *         If not match is found in the sitemap <code>false</code>
  167        *         is returned.
  168        * @throws org.apache.cocoon.ResourceNotFoundException If a sitemap component tries
  169        *                                   to access a resource which can not
  170        *                                   be found, e.g. the generator
  171        *         ConnectionResetException  If the connection was reset
  172        */
  173       public boolean process(Environment environment) throws Exception {
  174           InvokeContext context = new InvokeContext();
  175           context.enableLogging(getLogger());
  176           try {
  177               return process(environment, context);
  178           } finally {
  179               context.dispose();
  180           }
  181       }
  182   
  183       /**
  184        * Process the given <code>Environment</code> to assemble
  185        * a <code>ProcessingPipeline</code>.
  186        * @since 2.1
  187        */
  188       public ProcessingPipeline buildPipeline(Environment environment)
  189       throws Exception {
  190           InvokeContext context = new InvokeContext(true);
  191           context.enableLogging(getLogger());
  192           try {
  193               if (process(environment, context)) {
  194                   return context.getProcessingPipeline();
  195               } else {
  196                   return null;
  197               }
  198           } finally {
  199               context.dispose();
  200           }
  201       }
  202   
  203       /**
  204        * Do the actual processing, be it producing the response or just building the pipeline
  205        * @param environment
  206        * @param context
  207        * @return true if the pipeline was successfully built, false otherwise.
  208        * @throws Exception
  209        */
  210       protected boolean process(Environment environment, InvokeContext context)
  211       throws Exception {
  212   
  213       		// Increment the concurrent requests count
  214       		synchronized(this) {
  215       			requestCount++;
  216       		}
  217   
  218       		try {
  219       	        // and now process
  220               CocoonComponentManager.enterEnvironment(environment, this.sitemapComponentManager, this);
  221   
  222               Map objectModel = environment.getObjectModel();
  223   
  224               Object oldResolver = objectModel.get(ProcessingNode.OBJECT_SOURCE_RESOLVER);
  225       	        final Redirector oldRedirector = context.getRedirector();
  226   
  227       	        // Build a redirector
  228       	        TreeProcessorRedirector redirector = new TreeProcessorRedirector(environment, context);
  229       	        setupLogger(redirector);
  230       	        context.setRedirector(redirector);
  231   
  232               objectModel.put(ProcessingNode.OBJECT_SOURCE_RESOLVER, environment);
  233               boolean success = false;
  234       	        try {
  235       	            success = this.rootNode.invoke(environment, context);
  236   
  237       	            return success;
  238   
  239       	        } finally {
  240                       CocoonComponentManager.leaveEnvironment(success);
  241                       // Restore old redirector and resolver
  242        	            context.setRedirector(oldRedirector);
  243                       objectModel.put(ProcessingNode.OBJECT_SOURCE_RESOLVER, oldResolver);
  244       	        }
  245   
  246       		} finally {
  247   
  248       			// Decrement the concurrent request count
  249       			synchronized(this) {
  250       				requestCount--;
  251       			}
  252   
  253       			if(requestCount < 0) {
  254       				// Marked for disposal and no more concurrent requests.
  255       				dispose();
  256       			}
  257       		}
  258       }
  259   
  260       private boolean handleCocoonRedirect(String uri, Environment environment, InvokeContext context) throws Exception {
  261   
  262           // Build an environment wrapper
  263           // If the current env is a facade, change the delegate and continue processing the facade, since
  264           // we may have other redirects that will in turn also change the facade delegate
  265   
  266           MutableEnvironmentFacade facade = environment instanceof MutableEnvironmentFacade ?
  267               ((MutableEnvironmentFacade)environment) : null;
  268   
  269           if (facade != null) {
  270               // Consider the facade delegate (the real environment)
  271               environment = facade.getDelegate();
  272           }
  273   
  274           // test if this is a call from flow
  275           boolean isRedirect = (environment.getObjectModel().remove("cocoon:forward") == null);
  276           Environment newEnv = new ForwardEnvironmentWrapper(environment, this.sitemapComponentManager, uri, getLogger());
  277           if ( isRedirect ) {
  278               ((ForwardEnvironmentWrapper)newEnv).setInternalRedirect(true);
  279           }
  280   
  281           if (facade != null) {
  282               // Change the facade delegate
  283               facade.setDelegate((EnvironmentWrapper)newEnv);
  284               newEnv = facade;
  285           }
  286   
  287           // Get the processor that should process this request
  288           // (see https://issues.apache.org/jira/browse/COCOON-1990).
  289           ConcreteTreeProcessor processor = this;
  290           if (uri.startsWith("cocoon://"))
  291               processor = ((TreeProcessor)getRootProcessor()).concreteProcessor;
  292   
  293           // Process the redirect
  294           // No more reset since with TreeProcessorRedirector, we need to pop values from the redirect location
  295           // context.reset();
  296           // The following is a fix for bug #26854 and #26571
  297           final boolean result = processor.process(newEnv, context);
  298           if ( facade != null ) {
  299               newEnv = facade.getDelegate();
  300           }
  301           if ( ((ForwardEnvironmentWrapper)newEnv).getRedirectURL() != null ) {
  302               environment.redirect( false, ((ForwardEnvironmentWrapper)newEnv).getRedirectURL() );
  303           }
  304           return result;
  305       }
  306   
  307   	/* (non-Javadoc)
  308   	 * @see org.apache.avalon.framework.activity.Disposable#dispose()
  309   	 */
  310   	public void dispose() {
  311           if (this.disposableNodes != null) {
  312               // we must dispose the nodes in reverse order
  313               // otherwise selector nodes are freed before the components node
  314               for(int i=this.disposableNodes.size()-1; i>-1; i--) {
  315                   ((Disposable)disposableNodes.get(i)).dispose();
  316               }
  317               this.disposableNodes = null;
  318           }
  319   
  320           // Ensure it won't be used anymore
  321           this.rootNode = null;
  322   	}
  323   
  324       public String toString() {
  325           return "ConcreteTreeProcessor - " + wrappingProcessor.source.getURI();
  326       }
  327   
  328       private class TreeProcessorRedirector extends ForwardRedirector {
  329   
  330           private InvokeContext context;
  331           public TreeProcessorRedirector(Environment env, InvokeContext context) {
  332               super(env);
  333               this.context = context;
  334           }
  335   
  336           protected void cocoonRedirect(String uri) throws IOException, ProcessingException {
  337               try {
  338                   ConcreteTreeProcessor.this.handleCocoonRedirect(uri, this.env, this.context);
  339               } catch(IOException ioe) {
  340                   throw ioe;
  341               } catch(ProcessingException pe) {
  342                   throw pe;
  343               } catch(RuntimeException re) {
  344                   throw re;
  345               } catch(Exception ex) {
  346                   throw new ProcessingException(ex);
  347               }
  348           }
  349       }
  350   
  351       /**
  352        * Local extension of EnvironmentWrapper to propagate otherwise blocked
  353        * methods to the actual environment.
  354        */
  355       private static final class ForwardEnvironmentWrapper extends EnvironmentWrapper {
  356   
  357           public ForwardEnvironmentWrapper(Environment env,
  358               ComponentManager manager, String uri, Logger logger)
  359           throws MalformedURLException {
  360               super(env, manager, uri, logger, false);
  361           }
  362   
  363           public void setStatus(int statusCode) {
  364               environment.setStatus(statusCode);
  365           }
  366   
  367           public void setContentLength(int length) {
  368               environment.setContentLength(length);
  369           }
  370   
  371           public void setContentType(String contentType) {
  372               environment.setContentType(contentType);
  373           }
  374   
  375           public String getContentType() {
  376               return environment.getContentType();
  377           }
  378   
  379           public boolean isResponseModified(long lastModified) {
  380               return environment.isResponseModified(lastModified);
  381           }
  382   
  383           public void setResponseIsNotModified() {
  384               environment.setResponseIsNotModified();
  385           }
  386       }
  387   }

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