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örn Lü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 }