001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components;
018:
019: import java.io.IOException;
020: import java.net.MalformedURLException;
021: import java.util.ArrayList;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026:
027: import org.apache.avalon.excalibur.component.ExcaliburComponentManager;
028: import org.apache.avalon.framework.component.Component;
029: import org.apache.avalon.framework.component.ComponentException;
030: import org.apache.avalon.framework.component.ComponentManager;
031: import org.apache.avalon.framework.component.ComponentSelector;
032: import org.apache.avalon.framework.component.Recomposable;
033: import org.apache.avalon.framework.configuration.Configuration;
034: import org.apache.avalon.framework.configuration.ConfigurationException;
035: import org.apache.avalon.framework.logger.Logger;
036: import org.apache.cocoon.ProcessingException;
037: import org.apache.cocoon.Processor;
038: import org.apache.cocoon.environment.Environment;
039: import org.apache.cocoon.xml.XMLConsumer;
040: import org.apache.excalibur.instrument.InstrumentManager;
041: import org.apache.excalibur.source.Source;
042: import org.apache.excalibur.source.SourceException;
043: import org.apache.excalibur.source.SourceResolver;
044:
045: /**
046: * Cocoon Component Manager.
047: * This manager extends the {@link ExcaliburComponentManager}
048: * by a special lifecycle handling for a {@link RequestLifecycleComponent}
049: * and by handling the lookup of the {@link SourceResolver}.
050: * WARNING: This is a "private" Cocoon core class - do NOT use this class
051: * directly - and do not assume that a {@link ComponentManager} you get
052: * via the compose() method is an instance of CocoonComponentManager.
053: *
054: * @author <a href="mailto:bluetkemeier@s-und-n.de">Björn Lütkemeier</a>
055: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
056: * @version CVS $Id: CocoonComponentManager.java 433543 2006-08-22 06:22:54Z crossley $
057: */
058: public final class CocoonComponentManager extends
059: ExcaliburComponentManager implements SourceResolver {
060:
061: /** The key used to store the current process environment */
062: private static final String PROCESS_KEY = CocoonComponentManager.class
063: .getName();
064:
065: /** The environment attribute used to keep track of the actual environment in which the pipeline was built. */
066: private static final String PROCESSOR_ATTR = "CocoonComponentManager.processor";
067:
068: /** The environment information */
069: protected static final ThreadLocal environmentStack = new ThreadLocal();
070:
071: /** The configured {@link SourceResolver} */
072: private SourceResolver sourceResolver;
073:
074: /** The {@link SitemapConfigurationHolder}s */
075: private Map sitemapConfigurationHolders = new HashMap(15);
076:
077: /** The parent component manager for implementing parent aware components */
078: private ComponentManager parentManager;
079:
080: /** Temporary list of parent-aware components. Will be null for most of
081: * our lifecycle. */
082: private ArrayList parentAwareComponents = new ArrayList();
083:
084: /** has this been disposed? */
085: private boolean wasDisposed;
086:
087: /** The instrument manager (if any). */
088: private InstrumentManager instrumentManager;
089:
090: /** Create the ComponentManager */
091: public CocoonComponentManager() {
092: super (null, Thread.currentThread().getContextClassLoader());
093: }
094:
095: /** Create the ComponentManager with a Classloader */
096: public CocoonComponentManager(final ClassLoader loader) {
097: super (null, loader);
098: }
099:
100: /** Create the ComponentManager with a Classloader and parent ComponentManager */
101: public CocoonComponentManager(final ComponentManager manager,
102: final ClassLoader loader) {
103: super (manager, loader);
104: this .setParentManager(manager);
105: }
106:
107: /** Create the ComponentManager with a parent ComponentManager */
108: public CocoonComponentManager(final ComponentManager manager) {
109: super (manager);
110: this .setParentManager(manager);
111: }
112:
113: protected void setParentManager(final ComponentManager manager) {
114: this .parentManager = manager;
115: if (manager instanceof CocoonComponentManager) {
116: this
117: .setInstrumentManager(((CocoonComponentManager) manager).instrumentManager);
118: }
119: }
120:
121: /**
122: * @see org.apache.avalon.excalibur.component.ExcaliburComponentManager#setInstrumentManager(org.apache.excalibur.instrument.InstrumentManager)
123: */
124: public void setInstrumentManager(InstrumentManager iManager) {
125: this .instrumentManager = iManager;
126: super .setInstrumentManager(iManager);
127: }
128:
129: /**
130: * This hook must be called by the sitemap each time a sitemap is entered
131: * This method should never raise an exception, except when the
132: * parameters are not set!
133: */
134: public static void enterEnvironment(Environment env,
135: ComponentManager manager, Processor processor) {
136: if (null == env || null == manager || null == processor) {
137: throw new RuntimeException(
138: "CocoonComponentManager.enterEnvironment: "
139: + "All parameters must be set: " + env
140: + " - " + manager + " - " + processor);
141: }
142:
143: EnvironmentStack stack = (EnvironmentStack) environmentStack
144: .get();
145: if (stack == null) {
146: stack = new EnvironmentStack();
147: environmentStack.set(stack);
148: }
149: stack.push(new EnvironmentStack.Item(env, processor, manager,
150: stack.getOffset()));
151: stack.setOffset(stack.size() - 1);
152:
153: env.setAttribute(PROCESSOR_ATTR, processor);
154: }
155:
156: /**
157: * This hook must be called by the sitemap each time a sitemap is left.
158: * It's the counterpart to {@link #enterEnvironment(Environment, ComponentManager, Processor)}.
159: */
160: public static void leaveEnvironment() {
161: // Calling with true will avoid any change on the active processor
162: leaveEnvironment(true);
163: }
164:
165: /**
166: * This hook must be called by the sitemap each time a sitemap is left.
167: * It's the counterpart to {@link #enterEnvironment(Environment, ComponentManager, Processor)}.
168: *
169: * @param success indicates if the request was successfully handled by the environment that's being left
170: */
171: public static void leaveEnvironment(boolean success) {
172: final EnvironmentStack stack = (EnvironmentStack) environmentStack
173: .get();
174: final EnvironmentStack.Item objs = (EnvironmentStack.Item) stack
175: .pop();
176: stack.setOffset(objs.offset);
177:
178: if (stack.isEmpty()) {
179: final Environment env = objs.env;
180: final Map globalComponents = (Map) env
181: .getAttribute(GlobalRequestLifecycleComponent.class
182: .getName());
183: if (globalComponents != null) {
184:
185: final Iterator iter = globalComponents.values()
186: .iterator();
187: while (iter.hasNext()) {
188: final Object[] o = (Object[]) iter.next();
189: final Component c = (Component) o[0];
190: ((CocoonComponentManager) o[1])
191: .releaseRLComponent(c);
192: }
193: }
194: env.removeAttribute(GlobalRequestLifecycleComponent.class
195: .getName());
196:
197: // Setting this ThreadLocal to null allows it to be garbage collected
198: CocoonComponentManager.environmentStack.set(null);
199: } else {
200: if (!success) {
201: // Restore the current processor as being the active one
202: getCurrentEnvironment().setAttribute(PROCESSOR_ATTR,
203: getCurrentProcessor());
204: }
205: }
206: }
207:
208: /**
209: * INTERNAL METHOD. Do not use, can be removed without warning or deprecation cycle.
210: */
211: public static int markEnvironment() {
212: // TODO (CZ): This is only for testing - remove it later on. See also Cocoon.java.
213: final EnvironmentStack stack = (EnvironmentStack) environmentStack
214: .get();
215: if (stack != null) {
216: return stack.size();
217: }
218:
219: return 0;
220: }
221:
222: /**
223: * INTERNAL METHOD. Do not use, can be removed without warning or deprecation cycle.
224: */
225: public static void checkEnvironment(int depth, Logger logger)
226: throws Exception {
227: // TODO (CZ): This is only for testing - remove it later on. See also Cocoon.java.
228: final EnvironmentStack stack = (EnvironmentStack) environmentStack
229: .get();
230: int currentDepth = stack != null ? stack.size() : 0;
231: if (currentDepth != depth) {
232: logger
233: .error("ENVIRONMENT STACK HAS NOT BEEN CLEANED PROPERLY!");
234: throw new ProcessingException(
235: "Environment stack has not been cleaned up properly. "
236: + "Please report this (and if possible, together with a test case) "
237: + "to the Cocoon developers.");
238: }
239: }
240:
241: /**
242: * Create an environment aware xml consumer for the cocoon
243: * protocol
244: */
245: public static XMLConsumer createEnvironmentAwareConsumer(
246: XMLConsumer consumer) {
247: final EnvironmentStack stack = (EnvironmentStack) environmentStack
248: .get();
249: final EnvironmentStack.Item objs = stack.getCurrent();
250: return stack.getEnvironmentAwareConsumerWrapper(consumer,
251: objs.offset);
252: }
253:
254: /**
255: * This hook has to be called before a request is processed.
256: * The hook is called by the Cocoon component and by the
257: * cocoon protocol implementation.
258: * This method should never raise an exception, except when
259: * the environment is not set.
260: *
261: * @return A unique key within this thread.
262: */
263: public static Object startProcessing(Environment env) {
264: if (null == env) {
265: throw new RuntimeException(
266: "CocoonComponentManager.startProcessing: environment must be set.");
267: }
268: final EnvironmentDescription desc = new EnvironmentDescription(
269: env);
270: env.getObjectModel().put(PROCESS_KEY, desc);
271: env.startingProcessing();
272: return desc;
273: }
274:
275: /**
276: * This hook has to be called before a request is processed.
277: * The hook is called by the Cocoon component and by the
278: * cocoon protocol implementation.
279: * @param key A unique key within this thread return by
280: * {@link #startProcessing(Environment)}.
281: */
282: public static void endProcessing(Environment env, Object key) {
283: env.finishingProcessing();
284: final EnvironmentDescription desc = (EnvironmentDescription) key;
285: desc.release();
286: env.getObjectModel().remove(PROCESS_KEY);
287: }
288:
289: /**
290: * Return the current environment (for the cocoon: protocol)
291: */
292: public static Environment getCurrentEnvironment() {
293: final EnvironmentStack stack = (EnvironmentStack) environmentStack
294: .get();
295: if (null != stack && !stack.isEmpty()) {
296: return stack.getCurrent().env;
297: }
298: return null;
299: }
300:
301: /**
302: * Return the current processor (for the cocoon: protocol)
303: */
304: public static Processor getCurrentProcessor() {
305: final EnvironmentStack stack = (EnvironmentStack) environmentStack
306: .get();
307: if (null != stack && !stack.isEmpty()) {
308: return stack.getCurrent().processor;
309: }
310: return null;
311: }
312:
313: /**
314: * Return the processor that has actually processed the request
315: */
316: public static Processor getActiveProcessor(Environment env) {
317: return (Processor) env.getAttribute(PROCESSOR_ATTR);
318: }
319:
320: /**
321: * Get the current sitemap component manager.
322: * This method return the current sitemap component manager. This
323: * is the manager that holds all the components of the currently
324: * processed (sub)sitemap.
325: */
326: static public ComponentManager getSitemapComponentManager() {
327: final EnvironmentStack stack = (EnvironmentStack) environmentStack
328: .get();
329: if (null != stack && !stack.isEmpty()) {
330: EnvironmentStack.Item o = (EnvironmentStack.Item) stack
331: .peek();
332: return o.manager;
333: }
334:
335: // If we don't have an environment yet, just return null
336: return null;
337: }
338:
339: /**
340: * Return an instance of a component based on a Role. The Role is usually the Interface's
341: * Fully Qualified Name(FQN)--unless there are multiple Components for the same Role. In that
342: * case, the Role's FQN is appended with "Selector", and we return a ComponentSelector.
343: */
344: public Component lookup(final String role)
345: throws ComponentException {
346: if (null == role) {
347: final String message = "ComponentLocator Attempted to retrieve component with null role.";
348: throw new ComponentException(role, message);
349: }
350:
351: if (role.equals(SourceResolver.ROLE)) {
352: if (null == this .sourceResolver) {
353: if (wasDisposed) {
354: // (BD) working on bug 27249: I think we could throw an Exception here, as
355: // the following call fails anyway, but I'm not sure enough ;-)
356: getLogger()
357: .warn(
358: "Trying to lookup SourceResolver on disposed CocoonComponentManager");
359: }
360: this .sourceResolver = (SourceResolver) super
361: .lookup(role);
362: }
363: return this ;
364: }
365:
366: final EnvironmentStack stack = (EnvironmentStack) environmentStack
367: .get();
368: if (null != stack && !stack.isEmpty()) {
369: final EnvironmentStack.Item objects = stack.getCurrent();
370: final Map objectModel = objects.env.getObjectModel();
371: EnvironmentDescription desc = (EnvironmentDescription) objectModel
372: .get(PROCESS_KEY);
373: if (null != desc) {
374: Component component = desc
375: .getRequestLifecycleComponent(role);
376: if (null != component) {
377: return component;
378: }
379: component = desc
380: .getGlobalRequestLifecycleComponent(role);
381: if (null != component) {
382: return component;
383: }
384: }
385: }
386:
387: final Component component = super .lookup(role);
388:
389: if (component != null
390: && component instanceof RequestLifecycleComponent) {
391: if (stack == null || stack.isEmpty()) {
392: throw new ComponentException(role,
393: "ComponentManager has no Environment Stack.");
394: }
395:
396: final EnvironmentStack.Item objects = stack.getCurrent();
397: final Map objectModel = objects.env.getObjectModel();
398: EnvironmentDescription desc = (EnvironmentDescription) objectModel
399: .get(PROCESS_KEY);
400: if (null != desc) {
401: // first test if the parent CM has already initialized this component
402: if (!desc.containsRequestLifecycleComponent(role)) {
403: try {
404: if (component instanceof Recomposable) {
405: ((Recomposable) component).recompose(this );
406: }
407: ((RequestLifecycleComponent) component).setup(
408: objects.env, objectModel);
409: } catch (Exception local) {
410: throw new ComponentException(
411: role,
412: "Exception during setup of RequestLifecycleComponent.",
413: local);
414: }
415: desc.addRequestLifecycleComponent(role, component,
416: this );
417: }
418: }
419: }
420:
421: if (component != null
422: && component instanceof GlobalRequestLifecycleComponent) {
423: if (stack == null || stack.isEmpty()) {
424: throw new ComponentException(role,
425: "ComponentManager has no Environment Stack.");
426: }
427:
428: final EnvironmentStack.Item objects = stack.getCurrent();
429: final Map objectModel = objects.env.getObjectModel();
430: EnvironmentDescription desc = (EnvironmentDescription) objectModel
431: .get(PROCESS_KEY);
432: if (null != desc) {
433: // first test if the parent CM has already initialized this component
434: if (!desc.containsGlobalRequestLifecycleComponent(role)) {
435: try {
436: if (component instanceof Recomposable) {
437: ((Recomposable) component).recompose(this );
438: }
439: ((GlobalRequestLifecycleComponent) component)
440: .setup(objects.env, objectModel);
441: } catch (Exception local) {
442: throw new ComponentException(
443: role,
444: "Exception during setup of RequestLifecycleComponent.",
445: local);
446: }
447: desc.addGlobalRequestLifecycleComponent(role,
448: component, this );
449: }
450: }
451: }
452:
453: if (component != null
454: && component instanceof SitemapConfigurable) {
455: // FIXME: how can we prevent that this is called over and over again?
456: SitemapConfigurationHolder holder;
457:
458: holder = (SitemapConfigurationHolder) this .sitemapConfigurationHolders
459: .get(role);
460: if (null == holder) {
461: // create new holder
462: holder = new DefaultSitemapConfigurationHolder(role);
463: this .sitemapConfigurationHolders.put(role, holder);
464: }
465:
466: try {
467: ((SitemapConfigurable) component).configure(holder);
468: } catch (ConfigurationException ce) {
469: throw new ComponentException(
470: role,
471: "Exception during setup of SitemapConfigurable.",
472: ce);
473: }
474: }
475:
476: return component;
477: }
478:
479: /**
480: * Release a Component. This implementation makes sure it has a handle on the propper
481: * ComponentHandler, and let's the ComponentHandler take care of the actual work.
482: */
483: public void release(final Component component) {
484: if (null == component) {
485: return;
486: }
487:
488: if (component instanceof RequestLifecycleComponent
489: || component instanceof GlobalRequestLifecycleComponent) {
490: return;
491: }
492:
493: if (component == this ) {
494: return;
495: }
496:
497: super .release(component);
498: }
499:
500: /**
501: * Release a RequestLifecycleComponent
502: */
503: protected void releaseRLComponent(final Component component) {
504: super .release(component);
505: }
506:
507: /**
508: * Add an automatically released component
509: */
510: public static void addComponentForAutomaticRelease(
511: final ComponentSelector selector,
512: final Component component, final ComponentManager manager)
513: throws ProcessingException {
514: final EnvironmentStack stack = (EnvironmentStack) environmentStack
515: .get();
516: if (null != stack && !stack.isEmpty()) {
517: final EnvironmentStack.Item objects = (EnvironmentStack.Item) stack
518: .get(0);
519: final Map objectModel = objects.env.getObjectModel();
520: EnvironmentDescription desc = (EnvironmentDescription) objectModel
521: .get(PROCESS_KEY);
522: if (null != desc) {
523: desc.addToAutoRelease(selector, component, manager);
524: }
525: } else {
526: throw new ProcessingException(
527: "Unable to add component for automatic release: no environment available.");
528: }
529: }
530:
531: /**
532: * Add an automatically released component
533: */
534: public static void addComponentForAutomaticRelease(
535: final ComponentManager manager, final Component component)
536: throws ProcessingException {
537: final EnvironmentStack stack = (EnvironmentStack) environmentStack
538: .get();
539: if (null != stack && !stack.isEmpty()) {
540: final EnvironmentStack.Item objects = (EnvironmentStack.Item) stack
541: .get(0);
542: final Map objectModel = objects.env.getObjectModel();
543: EnvironmentDescription desc = (EnvironmentDescription) objectModel
544: .get(PROCESS_KEY);
545: if (null != desc) {
546: desc.addToAutoRelease(manager, component);
547: }
548: } else {
549: throw new ProcessingException(
550: "Unable to add component for automatic release: no environment available.");
551: }
552: }
553:
554: /**
555: * Remove from automatically released components
556: */
557: public static void removeFromAutomaticRelease(
558: final Component component) throws ProcessingException {
559: final EnvironmentStack stack = (EnvironmentStack) environmentStack
560: .get();
561: if (null != stack && !stack.isEmpty()) {
562: final EnvironmentStack.Item objects = (EnvironmentStack.Item) stack
563: .get(0);
564: final Map objectModel = objects.env.getObjectModel();
565: EnvironmentDescription desc = (EnvironmentDescription) objectModel
566: .get(PROCESS_KEY);
567: if (null != desc) {
568: desc.removeFromAutoRelease(component);
569: }
570: } else {
571: throw new ProcessingException(
572: "Unable to remove component from automatic release: no environment available.");
573: }
574: }
575:
576: /**
577: * Dispose
578: */
579: public void dispose() {
580: if (getLogger().isDebugEnabled()) {
581: getLogger()
582: .debug("CocoonComponentManager.dispose() called");
583: }
584:
585: if (null != this .sourceResolver) {
586: super .release(this .sourceResolver);
587: // We cannot null out sourceResolver here yet as some other not
588: // disposed yet components might still have unreleased sources,
589: // and they will call {@link #release(Source)} during their
590: // dispose().
591: }
592:
593: super .dispose();
594:
595: // All components now are released so sourceResolver should be not
596: // needed anymore.
597: this .sourceResolver = null;
598:
599: // This is used to track bug 27249
600: this .wasDisposed = true;
601: }
602:
603: /**
604: * Get a <code>Source</code> object.
605: */
606: public Source resolveURI(final String location)
607: throws MalformedURLException, IOException, SourceException {
608: return this .resolveURI(location, null, null);
609: }
610:
611: /**
612: * Get a <code>Source</code> object.
613: */
614: public Source resolveURI(final String location, String baseURI,
615: final Map parameters) throws MalformedURLException,
616: IOException, SourceException {
617: if (baseURI == null) {
618: final EnvironmentStack stack = (EnvironmentStack) environmentStack
619: .get();
620: if (null != stack && !stack.isEmpty()) {
621: final EnvironmentStack.Item objects = stack
622: .getCurrent();
623: baseURI = objects.env.getContext();
624: }
625: }
626: return this .sourceResolver.resolveURI(location, baseURI,
627: parameters);
628: }
629:
630: /**
631: * Releases a resolved resource
632: */
633: public void release(final Source source) {
634: this .sourceResolver.release(source);
635: }
636:
637: /* (non-Javadoc)
638: * @see org.apache.avalon.excalibur.component.ExcaliburComponentManager#addComponent(java.lang.String, java.lang.Class, org.apache.avalon.framework.configuration.Configuration)
639: */
640: public void addComponent(String role, Class clazz,
641: Configuration conf) throws ComponentException {
642: super .addComponent(role, clazz, conf);
643: // Note that at this point, we're not initialized and cannot do
644: // lookups, so defer parental introductions to initialize().
645: if (ParentAware.class.isAssignableFrom(clazz)) {
646: parentAwareComponents.add(role);
647: }
648: }
649:
650: public void initialize() throws Exception {
651: super .initialize();
652: if (parentAwareComponents == null) {
653: throw new ComponentException(null,
654: "CocoonComponentManager already initialized");
655: }
656: // Set parents for parentAware components
657: Iterator iter = parentAwareComponents.iterator();
658: while (iter.hasNext()) {
659: String role = (String) iter.next();
660: getLogger().debug(".. " + role);
661: if (parentManager != null
662: && parentManager.hasComponent(role)) {
663: // lookup new component
664: Component component = null;
665: try {
666: component = this .lookup(role);
667: ((ParentAware) component)
668: .setParentLocator(new ComponentLocatorImpl(
669: this .parentManager, role));
670: } catch (ComponentException ignore) {
671: // we don't set the parent then
672: } finally {
673: this .release(component);
674: }
675: }
676: }
677: parentAwareComponents = null; // null to save memory, and catch logic bugs.
678: }
679:
680: /**
681: * A runnable wrapper that inherits the environment stack of the thread it is
682: * created in.
683: * <p>
684: * It's defined as an abstract class here to use some internals of EnvironmentHelper, and
685: * should only be used through its public counterpart, {@link org.apache.cocoon.environment.CocoonRunnable}
686: */
687: public static abstract class AbstractCocoonRunnable implements
688: Runnable {
689: private Object parentStack = null;
690:
691: public AbstractCocoonRunnable() {
692: // Clone the environment stack of the calling thread.
693: // We'll use it in run() below
694: Object stack = CocoonComponentManager.environmentStack
695: .get();
696: if (stack != null) {
697: this .parentStack = ((EnvironmentStack) stack).clone();
698: }
699: }
700:
701: /**
702: * Calls {@link #doRun()} within the environment context of the creating thread.
703: */
704: public final void run() {
705: // Install the stack from the parent thread and run the Runnable
706: Object oldStack = environmentStack.get();
707: CocoonComponentManager.environmentStack
708: .set(this .parentStack);
709: try {
710: doRun();
711: } finally {
712: // Restore the previous stack
713: CocoonComponentManager.environmentStack.set(oldStack);
714: }
715: // FIXME: Check the lifetime of this run compared to the parent thread.
716: // A CocoonThread is meant to start and die within the execution period of the parent request,
717: // and it is an error if it lives longer as the parent environment is no more valid.
718: }
719:
720: abstract protected void doRun();
721: }
722: }
723:
724: final class EnvironmentDescription {
725:
726: Environment environment;
727: Map objectModel;
728: Map requestLifecycleComponents;
729: List autoreleaseComponents = new ArrayList(4);
730:
731: /**
732: * Constructor
733: */
734: EnvironmentDescription(Environment env) {
735: this .environment = env;
736: this .objectModel = env.getObjectModel();
737: }
738:
739: Map getGlobalRequestLifcecycleComponents() {
740: Map m = (Map) environment
741: .getAttribute(GlobalRequestLifecycleComponent.class
742: .getName());
743: if (m == null) {
744: m = new HashMap();
745: environment.setAttribute(
746: GlobalRequestLifecycleComponent.class.getName(), m);
747: }
748: return m;
749: }
750:
751: /**
752: * Release all components of this environment
753: * All RequestLifecycleComponents and autoreleaseComponents are
754: * released.
755: */
756: synchronized void release() {
757: if (this .requestLifecycleComponents != null) {
758: final Iterator iter = this .requestLifecycleComponents
759: .values().iterator();
760: while (iter.hasNext()) {
761: final Object[] o = (Object[]) iter.next();
762: final Component component = (Component) o[0];
763: ((CocoonComponentManager) o[1])
764: .releaseRLComponent(component);
765: }
766: this .requestLifecycleComponents.clear();
767: }
768:
769: for (int i = 0; i < autoreleaseComponents.size(); i++) {
770: final Object[] o = (Object[]) autoreleaseComponents.get(i);
771: final Component component = (Component) o[0];
772: if (o[1] instanceof ComponentManager) {
773: ((ComponentManager) o[1]).release(component);
774: } else {
775: ((ComponentSelector) o[1]).release(component);
776: if (o[2] != null) {
777: ((ComponentManager) o[2]).release((Component) o[1]);
778: }
779: }
780: }
781: this .autoreleaseComponents.clear();
782: this .environment = null;
783: this .objectModel = null;
784: }
785:
786: /**
787: * Add a RequestLifecycleComponent to the environment
788: */
789: void addRequestLifecycleComponent(final String role,
790: final Component co, final ComponentManager manager) {
791: if (this .requestLifecycleComponents == null) {
792: this .requestLifecycleComponents = new HashMap();
793: }
794: this .requestLifecycleComponents.put(role, new Object[] { co,
795: manager });
796: }
797:
798: /**
799: * Add a GlobalRequestLifecycleComponent to the environment
800: */
801: void addGlobalRequestLifecycleComponent(final String role,
802: final Component co, final ComponentManager manager) {
803: this .getGlobalRequestLifcecycleComponents().put(role,
804: new Object[] { co, manager });
805: }
806:
807: /**
808: * Do we already have a request lifecycle component
809: */
810: boolean containsRequestLifecycleComponent(final String role) {
811: if (this .requestLifecycleComponents == null) {
812: return false;
813: }
814: return this .requestLifecycleComponents.containsKey(role);
815: }
816:
817: /**
818: * Do we already have a global request lifecycle component
819: */
820: boolean containsGlobalRequestLifecycleComponent(final String role) {
821: return this .getGlobalRequestLifcecycleComponents().containsKey(
822: role);
823: }
824:
825: /**
826: * Search a RequestLifecycleComponent
827: */
828: Component getRequestLifecycleComponent(final String role) {
829: if (this .requestLifecycleComponents == null) {
830: return null;
831: }
832: final Object[] o = (Object[]) this .requestLifecycleComponents
833: .get(role);
834: if (null != o) {
835: return (Component) o[0];
836: }
837: return null;
838: }
839:
840: /**
841: * Search a GlobalRequestLifecycleComponent
842: */
843: Component getGlobalRequestLifecycleComponent(final String role) {
844: final Object[] o = (Object[]) this
845: .getGlobalRequestLifcecycleComponents().get(role);
846: if (null != o) {
847: return (Component) o[0];
848: }
849: return null;
850: }
851:
852: /**
853: * Add an automatically released component
854: */
855: synchronized void addToAutoRelease(
856: final ComponentSelector selector,
857: final Component component, final ComponentManager manager) {
858: this .autoreleaseComponents.add(new Object[] { component,
859: selector, manager });
860: }
861:
862: /**
863: * Add an automatically released component
864: */
865: synchronized void addToAutoRelease(final ComponentManager manager,
866: final Component component) {
867: this .autoreleaseComponents.add(new Object[] { component,
868: manager });
869: }
870:
871: /**
872: * Remove from automatically released components
873: */
874: synchronized void removeFromAutoRelease(final Component component)
875: throws ProcessingException {
876: int i = 0;
877: boolean found = false;
878: while (i < this .autoreleaseComponents.size() && !found) {
879: final Object[] o = (Object[]) this .autoreleaseComponents
880: .get(i);
881: if (o[0] == component) {
882: found = true;
883: if (o[1] instanceof ComponentManager) {
884: ((ComponentManager) o[1]).release(component);
885: } else {
886: ((ComponentSelector) o[1]).release(component);
887: if (o[2] != null) {
888: ((ComponentManager) o[2])
889: .release((Component) o[1]);
890: }
891: }
892: this .autoreleaseComponents.remove(i);
893: } else {
894: i++;
895: }
896: }
897: if (!found) {
898: throw new ProcessingException(
899: "Unable to remove component from automatic release: component not found.");
900: }
901: }
902: }
|