Save This Page
Home » cocoon-2.1.11-src » org.apache » cocoon » components » [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;
   18   
   19   import java.io.IOException;
   20   import java.net.MalformedURLException;
   21   import java.util.ArrayList;
   22   import java.util.HashMap;
   23   import java.util.Iterator;
   24   import java.util.List;
   25   import java.util.Map;
   26   
   27   import org.apache.avalon.excalibur.component.ExcaliburComponentManager;
   28   import org.apache.avalon.framework.component.Component;
   29   import org.apache.avalon.framework.component.ComponentException;
   30   import org.apache.avalon.framework.component.ComponentManager;
   31   import org.apache.avalon.framework.component.ComponentSelector;
   32   import org.apache.avalon.framework.component.Recomposable;
   33   import org.apache.avalon.framework.configuration.Configuration;
   34   import org.apache.avalon.framework.configuration.ConfigurationException;
   35   import org.apache.avalon.framework.logger.Logger;
   36   import org.apache.cocoon.ProcessingException;
   37   import org.apache.cocoon.Processor;
   38   import org.apache.cocoon.environment.Environment;
   39   import org.apache.cocoon.xml.XMLConsumer;
   40   import org.apache.excalibur.instrument.InstrumentManager;
   41   import org.apache.excalibur.source.Source;
   42   import org.apache.excalibur.source.SourceException;
   43   import org.apache.excalibur.source.SourceResolver;
   44   
   45   /**
   46    * Cocoon Component Manager.
   47    * This manager extends the {@link ExcaliburComponentManager}
   48    * by a special lifecycle handling for a {@link RequestLifecycleComponent}
   49    * and by handling the lookup of the {@link SourceResolver}.
   50    * WARNING: This is a "private" Cocoon core class - do NOT use this class
   51    * directly - and do not assume that a {@link ComponentManager} you get
   52    * via the compose() method is an instance of CocoonComponentManager.
   53    *
   54    * @author <a href="mailto:bluetkemeier@s-und-n.de">Bj&ouml;rn L&uuml;tkemeier</a>
   55    * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   56    * @version CVS $Id: CocoonComponentManager.java 540711 2007-05-22 19:36:07Z cziegeler $
   57    */
   58   public final class CocoonComponentManager extends ExcaliburComponentManager
   59                                             implements SourceResolver, Component {
   60   
   61       /** The key used to store the current process environment */
   62       private static final String PROCESS_KEY = CocoonComponentManager.class.getName();
   63   
   64       /** The environment attribute used to keep track of the actual environment in which the pipeline was built. */
   65       private static final String PROCESSOR_ATTR = "CocoonComponentManager.processor";
   66   
   67       /** The environment information */
   68       protected static final ThreadLocal environmentStack = new ThreadLocal();
   69   
   70       /** The configured {@link SourceResolver} */
   71       private SourceResolver sourceResolver;
   72   
   73       /** The {@link SitemapConfigurationHolder}s */
   74       private Map sitemapConfigurationHolders = new HashMap(15);
   75   
   76       /** The parent component manager for implementing parent aware components */
   77       private ComponentManager parentManager;
   78   
   79       /** Temporary list of parent-aware components.  Will be null for most of
   80        * our lifecycle. */
   81       private ArrayList parentAwareComponents = new ArrayList();
   82   
   83       /** has this been disposed? */
   84       private boolean wasDisposed;
   85   
   86       /** The instrument manager (if any). */
   87       private InstrumentManager instrumentManager;
   88   
   89       /** Create the ComponentManager */
   90       public CocoonComponentManager() {
   91           super(null, Thread.currentThread().getContextClassLoader());
   92       }
   93   
   94       /** Create the ComponentManager with a Classloader */
   95       public CocoonComponentManager(final ClassLoader loader) {
   96           super(null, loader);
   97       }
   98   
   99       /** Create the ComponentManager with a Classloader and parent ComponentManager */
  100       public CocoonComponentManager(final ComponentManager manager, final ClassLoader loader) {
  101           super(manager, loader);
  102           this.setParentManager(manager);
  103       }
  104   
  105       /** Create the ComponentManager with a parent ComponentManager */
  106       public CocoonComponentManager(final ComponentManager manager) {
  107           super(manager);
  108           this.setParentManager(manager);
  109       }
  110   
  111       protected void setParentManager(final ComponentManager manager) {
  112           this.parentManager = manager;
  113           if ( manager instanceof CocoonComponentManager ) {
  114               this.setInstrumentManager(((CocoonComponentManager)manager).instrumentManager);
  115           }
  116       }
  117   
  118       /**
  119        * @see org.apache.avalon.excalibur.component.ExcaliburComponentManager#setInstrumentManager(org.apache.excalibur.instrument.InstrumentManager)
  120        */
  121       public void setInstrumentManager(InstrumentManager iManager) {
  122           this.instrumentManager = iManager;
  123           super.setInstrumentManager(iManager);
  124       }
  125   
  126       /**
  127        * This hook must be called by the sitemap each time a sitemap is entered
  128        * This method should never raise an exception, except when the
  129        * parameters are not set!
  130        */
  131       public static void enterEnvironment(Environment      env,
  132                                           ComponentManager manager,
  133                                           Processor        processor) {
  134           if (null == env || null == manager || null == processor) {
  135               throw new RuntimeException("CocoonComponentManager.enterEnvironment: " +
  136                                          "All parameters must be set: " + env + " - " + manager + " - " + processor);
  137           }
  138   
  139           EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  140           if (stack == null) {
  141               stack = new EnvironmentStack();
  142               environmentStack.set(stack);
  143           }
  144           stack.push(new EnvironmentStack.Item(env, processor, manager, stack.getOffset()));
  145           stack.setOffset(stack.size()-1);
  146   
  147           env.setAttribute(PROCESSOR_ATTR, processor);
  148       }
  149   
  150       /**
  151        * This hook must be called by the sitemap each time a sitemap is left.
  152        * It's the counterpart to {@link #enterEnvironment(Environment, ComponentManager, Processor)}.
  153        */
  154       public static void leaveEnvironment() {
  155           // Calling with true will avoid any change on the active processor
  156           leaveEnvironment(true);
  157       }
  158   
  159       /**
  160        * This hook must be called by the sitemap each time a sitemap is left.
  161        * It's the counterpart to {@link #enterEnvironment(Environment, ComponentManager, Processor)}.
  162        *
  163        * @param success indicates if the request was successfully handled by the environment that's being left
  164        */
  165       public static void leaveEnvironment(boolean success) {
  166           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  167           final EnvironmentStack.Item objs = (EnvironmentStack.Item)stack.pop();
  168           stack.setOffset(objs.offset);
  169   
  170           if (stack.isEmpty()) {
  171               final Environment env = objs.env;
  172               final Map globalComponents = (Map)env.getAttribute(GlobalRequestLifecycleComponent.class.getName());
  173               if (globalComponents != null) {
  174   
  175                   final Iterator iter = globalComponents.values().iterator();
  176                   while (iter.hasNext()) {
  177                       final Object[] o = (Object[])iter.next();
  178                       final Component c = (Component)o[0];
  179                       ((CocoonComponentManager)o[1]).releaseRLComponent( c );
  180                   }
  181               }
  182               env.removeAttribute(GlobalRequestLifecycleComponent.class.getName());
  183   
  184               // Setting this ThreadLocal to null allows it to be garbage collected
  185               CocoonComponentManager.environmentStack.set(null);
  186           } else {
  187               if (!success) {
  188                   // Restore the current processor as being the active one
  189                   getCurrentEnvironment().setAttribute(PROCESSOR_ATTR, getCurrentProcessor());
  190               }
  191           }
  192       }
  193   
  194       /**
  195        * INTERNAL METHOD. Do not use, can be removed without warning or deprecation cycle.
  196        */
  197       public static int markEnvironment() {
  198           // TODO (CZ): This is only for testing - remove it later on. See also Cocoon.java.
  199           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  200           if (stack != null) {
  201               return stack.size();
  202           }
  203   
  204           return 0;
  205       }
  206   
  207       /**
  208        * INTERNAL METHOD. Do not use, can be removed without warning or deprecation cycle.
  209        */
  210       public static void checkEnvironment(int depth, Logger logger)
  211       throws Exception {
  212           // TODO (CZ): This is only for testing - remove it later on. See also Cocoon.java.
  213           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  214           int currentDepth = stack != null? stack.size() : 0;
  215           if (currentDepth != depth) {
  216               logger.error("ENVIRONMENT STACK HAS NOT BEEN CLEANED PROPERLY!");
  217               throw new ProcessingException("Environment stack has not been cleaned up properly. " +
  218                                             "Please report this (and if possible, together with a test case) " +
  219                                             "to the Cocoon developers.");
  220           }
  221       }
  222   
  223       /**
  224        * Create an environment aware xml consumer for the cocoon
  225        * protocol
  226        */
  227       public static XMLConsumer createEnvironmentAwareConsumer(XMLConsumer consumer) {
  228           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  229           final EnvironmentStack.Item objs = stack.getCurrent();
  230           return stack.getEnvironmentAwareConsumerWrapper(consumer, objs.offset);
  231       }
  232   
  233       /**
  234        * This hook has to be called before a request is processed.
  235        * The hook is called by the Cocoon component and by the
  236        * cocoon protocol implementation.
  237        * This method should never raise an exception, except when
  238        * the environment is not set.
  239        *
  240        * @return A unique key within this thread.
  241        */
  242       public static Object startProcessing(Environment env) {
  243           if (null == env) {
  244               throw new RuntimeException("CocoonComponentManager.startProcessing: environment must be set.");
  245           }
  246           final EnvironmentDescription desc = new EnvironmentDescription(env);
  247           env.getObjectModel().put(PROCESS_KEY, desc);
  248           env.startingProcessing();
  249           return desc;
  250       }
  251   
  252       /**
  253        * This hook has to be called before a request is processed.
  254        * The hook is called by the Cocoon component and by the
  255        * cocoon protocol implementation.
  256        * @param key A unique key within this thread return by
  257        *         {@link #startProcessing(Environment)}.
  258        */
  259       public static void endProcessing(Environment env, Object key) {
  260           env.finishingProcessing();
  261           final EnvironmentDescription desc = (EnvironmentDescription)key;
  262           desc.release();
  263           env.getObjectModel().remove(PROCESS_KEY);
  264       }
  265   
  266       /**
  267        * Return the current environment (for the cocoon: protocol)
  268        */
  269       public static Environment getCurrentEnvironment() {
  270           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  271           if (null != stack && !stack.isEmpty()) {
  272               return stack.getCurrent().env;
  273           }
  274           return null;
  275       }
  276   
  277       /**
  278        * Return the current processor (for the cocoon: protocol)
  279        */
  280       public static Processor getCurrentProcessor() {
  281           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  282           if (null != stack && !stack.isEmpty()) {
  283               return stack.getCurrent().processor;
  284           }
  285           return null;
  286       }
  287   
  288       /**
  289        * Return the processor that has actually processed the request
  290        */
  291       public static Processor getActiveProcessor(Environment env) {
  292           return (Processor) env.getAttribute(PROCESSOR_ATTR);
  293       }
  294   
  295       /**
  296        * Get the current sitemap component manager.
  297        * This method return the current sitemap component manager. This
  298        * is the manager that holds all the components of the currently
  299        * processed (sub)sitemap.
  300        */
  301       static public ComponentManager getSitemapComponentManager() {
  302           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  303           if (null != stack && !stack.isEmpty()) {
  304               EnvironmentStack.Item o = (EnvironmentStack.Item) stack.peek();
  305               return o.manager;
  306           }
  307   
  308           // If we don't have an environment yet, just return null
  309           return null;
  310       }
  311   
  312       /**
  313        * Return an instance of a component based on a Role.  The Role is usually the Interface's
  314        * Fully Qualified Name(FQN)--unless there are multiple Components for the same Role.  In that
  315        * case, the Role's FQN is appended with "Selector", and we return a ComponentSelector.
  316        */
  317       public Component lookup(final String role)
  318       throws ComponentException {
  319           if (null == role) {
  320               final String message =
  321                   "ComponentLocator Attempted to retrieve component with null role.";
  322               throw new ComponentException(role, message);
  323           }
  324   
  325           if (role.equals(SourceResolver.ROLE)) {
  326               if (null == this.sourceResolver) {
  327                   if(this.wasDisposed) {
  328                       // (BD) working on bug 27249: I think we could throw an Exception here, as
  329                       // the following call fails anyway, but I'm not sure enough ;-)
  330                       this.getLogger().warn("Trying to lookup SourceResolver on disposed CocoonComponentManager");
  331                   }
  332                   this.sourceResolver = (SourceResolver) super.lookup( role );
  333               }
  334               return this;
  335           }
  336   
  337           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  338           if ( null != stack && !stack.isEmpty()) {
  339               final EnvironmentStack.Item objects = stack.getCurrent();
  340               final Map objectModel = objects.env.getObjectModel();
  341               EnvironmentDescription desc = (EnvironmentDescription)objectModel.get(PROCESS_KEY);
  342               if ( null != desc ) {
  343                   Component component = desc.getRequestLifecycleComponent(role);
  344                   if (null != component) {
  345                       return component;
  346                   }
  347                   component = desc.getGlobalRequestLifecycleComponent(role);
  348                   if (null != component) {
  349                       return component;
  350                   }
  351               }
  352           }
  353   
  354           final Component component = super.lookup(role);
  355   
  356           if (component != null && component instanceof RequestLifecycleComponent) {
  357               if (stack == null || stack.isEmpty()) {
  358                   throw new ComponentException(role, "ComponentManager has no Environment Stack.");
  359               }
  360   
  361               final EnvironmentStack.Item objects = stack.getCurrent();
  362               final Map objectModel = objects.env.getObjectModel();
  363               EnvironmentDescription desc = (EnvironmentDescription) objectModel.get(PROCESS_KEY);
  364               if (null != desc) {
  365                   // first test if the parent CM has already initialized this component
  366                   if (!desc.containsRequestLifecycleComponent(role)) {
  367                       try {
  368                           if (component instanceof Recomposable) {
  369                               ((Recomposable) component).recompose(this);
  370                           }
  371                           ((RequestLifecycleComponent) component).setup(objects.env, objectModel);
  372                       } catch (Exception local) {
  373                           throw new ComponentException(role, "Exception during setup of RequestLifecycleComponent.", local);
  374                       }
  375                       desc.addRequestLifecycleComponent(role, component, this);
  376                   }
  377               }
  378           }
  379   
  380           if (component != null && component instanceof GlobalRequestLifecycleComponent) {
  381               if (stack == null || stack.isEmpty()) {
  382                   throw new ComponentException(role, "ComponentManager has no Environment Stack.");
  383               }
  384   
  385               final EnvironmentStack.Item objects = stack.getCurrent();
  386               final Map objectModel = objects.env.getObjectModel();
  387               EnvironmentDescription desc = (EnvironmentDescription) objectModel.get(PROCESS_KEY);
  388               if (null != desc) {
  389                   // first test if the parent CM has already initialized this component
  390                   if ( !desc.containsGlobalRequestLifecycleComponent( role ) ) {
  391                       try {
  392                           if (component instanceof Recomposable) {
  393                               ((Recomposable) component).recompose(this);
  394                           }
  395                           ((GlobalRequestLifecycleComponent) component).setup(objects.env, objectModel);
  396                       } catch (Exception local) {
  397                           throw new ComponentException(role, "Exception during setup of RequestLifecycleComponent.", local);
  398                       }
  399                       desc.addGlobalRequestLifecycleComponent(role, component, this);
  400                   }
  401               }
  402           }
  403   
  404           if (component != null && component instanceof SitemapConfigurable) {
  405               // FIXME: how can we prevent that this is called over and over again?
  406               SitemapConfigurationHolder holder;
  407   
  408               holder = (SitemapConfigurationHolder) this.sitemapConfigurationHolders.get(role);
  409               if (null == holder) {
  410                   // create new holder
  411                   holder = new DefaultSitemapConfigurationHolder(role);
  412                   this.sitemapConfigurationHolders.put(role, holder);
  413               }
  414   
  415               try {
  416                   ((SitemapConfigurable)component).configure(holder);
  417               } catch (ConfigurationException ce) {
  418                   throw new ComponentException(role, "Exception during setup of SitemapConfigurable.", ce);
  419               }
  420           }
  421   
  422           return component;
  423       }
  424   
  425       /**
  426        * Release a Component.  This implementation makes sure it has a handle on the propper
  427        * ComponentHandler, and let's the ComponentHandler take care of the actual work.
  428        */
  429       public void release(final Component component) {
  430           if (null == component) {
  431               return;
  432           }
  433   
  434           if (component instanceof RequestLifecycleComponent
  435                   || component instanceof GlobalRequestLifecycleComponent) {
  436               return;
  437           }
  438   
  439           if (component == this) {
  440               return;
  441           }
  442   
  443           super.release(component);
  444       }
  445   
  446       /**
  447        * Release a RequestLifecycleComponent
  448        */
  449       protected void releaseRLComponent(final Component component) {
  450           super.release(component);
  451       }
  452   
  453       /**
  454        * Add an automatically released component
  455        */
  456       public static void addComponentForAutomaticRelease(final ComponentSelector selector,
  457                                                          final Component         component,
  458                                                          final ComponentManager  manager)
  459       throws ProcessingException {
  460           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  461           if ( null != stack && !stack.isEmpty()) {
  462               final EnvironmentStack.Item objects = (EnvironmentStack.Item)stack.get(0);
  463               final Map objectModel = objects.env.getObjectModel();
  464               EnvironmentDescription desc = (EnvironmentDescription)objectModel.get(PROCESS_KEY);
  465               if ( null != desc ) {
  466                   desc.addToAutoRelease(selector, component, manager);
  467               }
  468           } else {
  469               throw new ProcessingException("Unable to add component for automatic release: no environment available.");
  470           }
  471       }
  472   
  473       /**
  474        * Add an automatically released component
  475        */
  476       public static void addComponentForAutomaticRelease(final ComponentManager manager,
  477                                                          final Component        component)
  478       throws ProcessingException {
  479           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  480           if ( null != stack && !stack.isEmpty()) {
  481               final EnvironmentStack.Item objects = (EnvironmentStack.Item)stack.get(0);
  482               final Map objectModel = objects.env.getObjectModel();
  483               EnvironmentDescription desc = (EnvironmentDescription)objectModel.get(PROCESS_KEY);
  484               if ( null != desc ) {
  485                   desc.addToAutoRelease(manager, component);
  486               }
  487           } else {
  488               throw new ProcessingException("Unable to add component for automatic release: no environment available.");
  489           }
  490       }
  491   
  492       /**
  493        * Remove from automatically released components
  494        */
  495       public static void removeFromAutomaticRelease(final Component component)
  496       throws ProcessingException {
  497           final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  498           if ( null != stack && !stack.isEmpty()) {
  499               final EnvironmentStack.Item objects = (EnvironmentStack.Item)stack.get(0);
  500               final Map objectModel = objects.env.getObjectModel();
  501               EnvironmentDescription desc = (EnvironmentDescription)objectModel.get(PROCESS_KEY);
  502               if ( null != desc ) {
  503                   desc.removeFromAutoRelease(component);
  504               }
  505           } else {
  506               throw new ProcessingException("Unable to remove component from automatic release: no environment available.");
  507           }
  508       }
  509   
  510       /**
  511        * Dispose
  512        */
  513       public void dispose() {
  514           if (this.getLogger().isDebugEnabled()) {
  515               this.getLogger().debug("CocoonComponentManager.dispose() called");
  516           }
  517   
  518           if (null != this.sourceResolver) {
  519               super.release((Component)this.sourceResolver);
  520               // We cannot null out sourceResolver here yet as some other not
  521               // disposed yet components might still have unreleased sources,
  522               // and they will call {@link #release(Source)} during their
  523               // dispose().
  524           }
  525   
  526           super.dispose();
  527   
  528           // All components now are released so sourceResolver should be not
  529           // needed anymore.
  530           this.sourceResolver = null;
  531   
  532           // This is used to track bug 27249
  533           this.wasDisposed = true;
  534       }
  535   
  536       /**
  537        * Get a <code>Source</code> object.
  538        */
  539       public Source resolveURI(final String location)
  540       throws MalformedURLException, IOException, SourceException {
  541           return this.resolveURI(location, null, null);
  542       }
  543   
  544       /**
  545        * Get a <code>Source</code> object.
  546        */
  547       public Source resolveURI(final String location,
  548                                String baseURI,
  549                                final Map    parameters)
  550       throws MalformedURLException, IOException, SourceException {
  551           if (baseURI == null) {
  552               final EnvironmentStack stack = (EnvironmentStack)environmentStack.get();
  553               if ( null != stack && !stack.isEmpty()) {
  554                   final EnvironmentStack.Item objects = stack.getCurrent();
  555                   baseURI = objects.env.getContext();
  556               }
  557           }
  558           return this.sourceResolver.resolveURI(location, baseURI, parameters);
  559       }
  560   
  561       /**
  562        * Releases a resolved resource
  563        */
  564       public void release(final Source source) {
  565           this.sourceResolver.release(source);
  566       }
  567   
  568   
  569       /* (non-Javadoc)
  570        * @see org.apache.avalon.excalibur.component.ExcaliburComponentManager#addComponent(java.lang.String, java.lang.Class, org.apache.avalon.framework.configuration.Configuration)
  571        */
  572       public void addComponent(String role, Class clazz, Configuration conf)
  573       throws ComponentException {
  574           super.addComponent(role, clazz, conf);
  575           // Note that at this point, we're not initialized and cannot do
  576           // lookups, so defer parental introductions to initialize().
  577           if (ParentAware.class.isAssignableFrom(clazz)) {
  578               this.parentAwareComponents.add(role);
  579           }
  580       }
  581   
  582       public void initialize() throws Exception {
  583           super.initialize();
  584           if (this.parentAwareComponents == null) {
  585               throw new ComponentException(null, "CocoonComponentManager already initialized");
  586           }
  587           // Set parents for parentAware components
  588           Iterator iter = this.parentAwareComponents.iterator();
  589           while (iter.hasNext()) {
  590               String role = (String)iter.next();
  591               this.getLogger().debug(".. "+role);
  592               if ( this.parentManager != null && this.parentManager.hasComponent( role ) ) {
  593                   // lookup new component
  594                   Component component = null;
  595                   try {
  596                       component = this.lookup( role );
  597                       ((ParentAware)component).setParentLocator( new ComponentLocatorImpl(this.parentManager, role ));
  598                   } catch (ComponentException ignore) {
  599                       // we don't set the parent then
  600                   } finally {
  601                       this.release( component );
  602                   }
  603               }
  604           }
  605           this.parentAwareComponents = null;  // null to save memory, and catch logic bugs.
  606       }
  607   
  608       /**
  609        * A runnable wrapper that inherits the environment stack of the thread it is
  610        * created in.
  611        * <p>
  612        * It's defined as an abstract class here to use some internals of EnvironmentHelper, and
  613        * should only be used through its public counterpart, {@link org.apache.cocoon.environment.CocoonRunnable}
  614        */
  615       public static abstract class AbstractCocoonRunnable implements Runnable {
  616           private Object parentStack = null;
  617   
  618           public AbstractCocoonRunnable() {
  619               // Clone the environment stack of the calling thread.
  620               // We'll use it in run() below
  621               Object stack = CocoonComponentManager.environmentStack.get();
  622               if (stack != null) {
  623                   this.parentStack = ((EnvironmentStack)stack).clone();
  624               }
  625           }
  626   
  627           /**
  628            * Calls {@link #doRun()} within the environment context of the creating thread.
  629            */
  630           public final void run() {
  631               // Install the stack from the parent thread and run the Runnable
  632               Object oldStack = environmentStack.get();
  633               CocoonComponentManager.environmentStack.set(this.parentStack);
  634               try {
  635                   this.doRun();
  636               } finally {
  637                   // Restore the previous stack
  638                   CocoonComponentManager.environmentStack.set(oldStack);
  639               }
  640               // FIXME: Check the lifetime of this run compared to the parent thread.
  641               // A CocoonThread is meant to start and die within the execution period of the parent request,
  642               // and it is an error if it lives longer as the parent environment is no more valid.
  643           }
  644   
  645           abstract protected void doRun();
  646       }
  647   }
  648   
  649   final class EnvironmentDescription {
  650   
  651       Environment environment;
  652       Map         objectModel;
  653       Map         requestLifecycleComponents;
  654       List        autoreleaseComponents      = new ArrayList(4);
  655   
  656       /**
  657        * Constructor
  658        */
  659       EnvironmentDescription(Environment env) {
  660           this.environment = env;
  661           this.objectModel = env.getObjectModel();
  662       }
  663   
  664       Map getGlobalRequestLifcecycleComponents() {
  665           Map m = (Map)this.environment.getAttribute(GlobalRequestLifecycleComponent.class.getName());
  666           if ( m == null ) {
  667               m = new HashMap();
  668               this.environment.setAttribute(GlobalRequestLifecycleComponent.class.getName(), m);
  669           }
  670           return m;
  671       }
  672   
  673       /**
  674        * Release all components of this environment
  675        * All RequestLifecycleComponents and autoreleaseComponents are
  676        * released.
  677        */
  678       synchronized void release() {
  679           if ( this.requestLifecycleComponents != null ) {
  680               final Iterator iter = this.requestLifecycleComponents.values().iterator();
  681               while (iter.hasNext()) {
  682                   final Object[] o = (Object[])iter.next();
  683                   final Component component = (Component)o[0];
  684                   ((CocoonComponentManager)o[1]).releaseRLComponent( component );
  685               }
  686               this.requestLifecycleComponents.clear();
  687           }
  688   
  689           for (int i = 0; i < this.autoreleaseComponents.size(); i++) {
  690               final Object[] o = (Object[])this.autoreleaseComponents.get(i);
  691               final Component component = (Component)o[0];
  692               if (o[1] instanceof ComponentManager) {
  693                   ((ComponentManager)o[1]).release( component );
  694               } else {
  695                   ((ComponentSelector) o[1]).release(component);
  696                   if (o[2] != null) {
  697                       ((ComponentManager) o[2]).release((Component) o[1]);
  698                   }
  699               }
  700           }
  701           this.autoreleaseComponents.clear();
  702           this.environment = null;
  703           this.objectModel = null;
  704       }
  705   
  706       /**
  707        * Add a RequestLifecycleComponent to the environment
  708        */
  709       void addRequestLifecycleComponent(final String role,
  710                                         final Component co,
  711                                         final ComponentManager manager) {
  712           if ( this.requestLifecycleComponents == null ) {
  713               this.requestLifecycleComponents = new HashMap();
  714           }
  715           this.requestLifecycleComponents.put(role, new Object[] {co, manager});
  716       }
  717   
  718       /**
  719        * Add a GlobalRequestLifecycleComponent to the environment
  720        */
  721       void addGlobalRequestLifecycleComponent(final String role,
  722                                         final Component co,
  723                                         final ComponentManager manager) {
  724           this.getGlobalRequestLifcecycleComponents().put(role, new Object[] {co, manager});
  725       }
  726   
  727       /**
  728        * Do we already have a request lifecycle component
  729        */
  730       boolean containsRequestLifecycleComponent(final String role) {
  731           if ( this.requestLifecycleComponents == null ) {
  732               return false;
  733           }
  734           return this.requestLifecycleComponents.containsKey( role );
  735       }
  736   
  737       /**
  738        * Do we already have a global request lifecycle component
  739        */
  740       boolean containsGlobalRequestLifecycleComponent(final String role) {
  741           return this.getGlobalRequestLifcecycleComponents().containsKey( role );
  742       }
  743   
  744       /**
  745        * Search a RequestLifecycleComponent
  746        */
  747       Component getRequestLifecycleComponent(final String role) {
  748           if ( this.requestLifecycleComponents == null ) {
  749               return null;
  750           }
  751           final Object[] o = (Object[])this.requestLifecycleComponents.get(role);
  752           if ( null != o ) {
  753               return (Component)o[0];
  754           }
  755           return null;
  756       }
  757   
  758       /**
  759        * Search a GlobalRequestLifecycleComponent
  760        */
  761       Component getGlobalRequestLifecycleComponent(final String role) {
  762           final Object[] o = (Object[])this.getGlobalRequestLifcecycleComponents().get(role);
  763           if ( null != o ) {
  764               return (Component)o[0];
  765           }
  766           return null;
  767       }
  768   
  769       /**
  770        * Add an automatically released component
  771        */
  772       synchronized void addToAutoRelease(final ComponentSelector selector,
  773                                          final Component         component,
  774                                          final ComponentManager  manager) {
  775           this.autoreleaseComponents.add(new Object[] {component, selector, manager});
  776       }
  777   
  778       /**
  779        * Add an automatically released component
  780        */
  781       synchronized void addToAutoRelease(final ComponentManager manager,
  782                                          final Component        component) {
  783           this.autoreleaseComponents.add(new Object[] {component, manager});
  784       }
  785   
  786       /**
  787        * Remove from automatically released components
  788        */
  789       synchronized void removeFromAutoRelease(final Component component)
  790       throws ProcessingException {
  791           int i = 0;
  792           boolean found = false;
  793           while (i < this.autoreleaseComponents.size() && !found) {
  794               final Object[] o = (Object[])this.autoreleaseComponents.get(i);
  795               if (o[0] == component) {
  796                   found = true;
  797                   if (o[1] instanceof ComponentManager) {
  798                       ((ComponentManager)o[1]).release( component );
  799                   } else {
  800                       ((ComponentSelector)o[1]).release( component );
  801                       if (o[2] != null) {
  802                           ((ComponentManager)o[2]).release( (Component)o[1] );
  803                       }
  804                   }
  805                   this.autoreleaseComponents.remove(i);
  806               } else {
  807                   i++;
  808               }
  809           }
  810           if (!found) {
  811               throw new ProcessingException("Unable to remove component from automatic release: component not found.");
  812           }
  813       }
  814   }

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