001    /*******************************************************************************
002     * Copyright (C) PicoContainer Organization. All rights reserved.
003     * ---------------------------------------------------------------------------
004     * The software in this package is published under the terms of the BSD style
005     * license a copy of which has been included with this distribution in the
006     * LICENSE.txt file.
007     ******************************************************************************/
008    package org.picocontainer.script.groovy.nodes;
009    
010    import java.util.Map;
011    
012    import org.picocontainer.DefaultPicoContainer;
013    import java.security.PrivilegedAction;
014    import org.picocontainer.ComponentFactory;
015    import java.security.AccessController;
016    
017    import org.picocontainer.behaviors.Caching;
018    import org.picocontainer.MutablePicoContainer;
019    import org.picocontainer.PicoContainer;
020    import org.picocontainer.ComponentMonitor;
021    import org.picocontainer.monitors.AbstractComponentMonitor;
022    import org.picocontainer.classname.DefaultClassLoadingPicoContainer;
023    import org.picocontainer.script.ScriptedPicoContainerMarkupException;
024    import org.picocontainer.script.NodeBuilderDecorator;
025    import org.picocontainer.classname.ClassLoadingPicoContainer;
026    import org.picocontainer.ComponentMonitorStrategy;
027    
028    /**
029     * Creates a new PicoContainer node. There may or may not be a parent container
030     * involved.
031     * 
032     * @author James Strachan
033     * @author Paul Hammant
034     * @author Aslak Hellesøy
035     * @author Michael Rimov
036     * @author Mauro Talevi
037     */
038    @SuppressWarnings("serial")
039    public class ChildContainerNode extends AbstractBuilderNode {
040    
041        /**
042         * Node name.
043         */
044        public static final String NODE_NAME = "container";
045    
046        /**
047         * Supported Attribute: 'class' Reference to a classname of the container to
048         * use.
049         */
050        private static final String CLASS = "class";
051    
052        /**
053         * The node decorator.
054         */
055        private final NodeBuilderDecorator decorator;
056    
057        /**
058         * Attribute: 'componentFactory' a reference to an instance of a component
059         * factory.
060         */
061        private static final String COMPONENT_ADAPTER_FACTORY = "componentFactory";
062    
063        /**
064         * Attribute: 'componentMonitor' a reference to an instance of a component
065         * monitor.
066         */
067        private static final String COMPONENT_MONITOR = "componentMonitor";
068    
069        /**
070         * Attribute that exists in test cases, but not necessarily used?
071         */
072        private static final String SCOPE = "scope";
073    
074        /**
075         * Attribute: 'parent' a reference to the parent for this new container.
076         */
077        private static final String PARENT = "parent";
078    
079        /**
080         * Constructs a child container node with a given decorator
081         * 
082         * @param decorator NodeBuilderDecorator
083         */
084        public ChildContainerNode(NodeBuilderDecorator decorator) {
085            super(NODE_NAME);
086            this.decorator = decorator;
087    
088            this.addAttribute(CLASS).addAttribute(COMPONENT_ADAPTER_FACTORY).addAttribute(COMPONENT_MONITOR).addAttribute(
089                    PARENT).addAttribute(SCOPE);
090    
091        }
092    
093        /**
094         * Creates a new container. There may or may not be a parent to this
095         * container. Supported attributes are
096         * <p>
097         * {@inheritDoc}
098         * </p>
099         * 
100         * @param current PicoContainer
101         * @param attributes Map
102         * @return Object
103         * @throws ScriptedPicoContainerMarkupException
104         */
105        @SuppressWarnings("unchecked")
106        public Object createNewNode(Object current, Map attributes) throws ScriptedPicoContainerMarkupException {
107    
108            return createChildContainer(attributes, (ClassLoadingPicoContainer) current);
109        }
110    
111        /**
112         * Returns the decorator
113         * 
114         * @return A NodeBuilderDecorator
115         */
116        private NodeBuilderDecorator getDecorator() {
117            return decorator;
118        }
119    
120        /**
121         * Creates a new container. There may or may not be a parent to this
122         * container. Supported attributes are:
123         * <ul>
124         * <li><tt>componentFactory</tt>: The ComponentFactory used for new
125         * container</li>
126         * <li><tt>componentMonitor</tt>: The ComponentMonitor used for new
127         * container</li>
128         * </ul>
129         * 
130         * @param attributes Map Attributes defined by the builder in the script.
131         * @param parent The parent container
132         * @return The PicoContainer
133         */
134        @SuppressWarnings("unchecked")
135        protected ClassLoadingPicoContainer createChildContainer(Map<String,Object> attributes, ClassLoadingPicoContainer parent) {
136    
137            ClassLoader parentClassLoader;
138            MutablePicoContainer childContainer;
139            if (parent != null) {
140                parentClassLoader = parent.getComponentClassLoader();
141                if (isAttribute(attributes, COMPONENT_ADAPTER_FACTORY)) {
142                    ComponentFactory componentFactory = createComponentFactory(attributes);
143                    childContainer = new DefaultPicoContainer(getDecorator()
144                            .decorate(componentFactory, attributes), parent);
145                    if (isAttribute(attributes, COMPONENT_MONITOR)) {
146                        changeComponentMonitor(childContainer, createComponentMonitor(attributes));
147                    }
148                    parent.addChildContainer(childContainer);
149                } else if (isAttribute(attributes, COMPONENT_MONITOR)) {
150                    ComponentFactory componentFactory = new Caching();
151                    childContainer = new DefaultPicoContainer(getDecorator()
152                            .decorate(componentFactory, attributes), parent);
153                    changeComponentMonitor(childContainer, createComponentMonitor(attributes));
154                } else {
155                    childContainer = parent.makeChildContainer();
156                }
157            } else {
158                parentClassLoader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
159                    public Object run() {
160                        return PicoContainer.class.getClassLoader();
161                    }
162                });
163                ComponentFactory componentFactory = createComponentFactory(attributes);
164                childContainer = new DefaultPicoContainer(getDecorator().decorate(componentFactory, attributes));
165                if (isAttribute(attributes, COMPONENT_MONITOR)) {
166                    changeComponentMonitor(childContainer, createComponentMonitor(attributes));
167                }
168            }
169    
170            MutablePicoContainer decoratedPico = getDecorator().decorate(childContainer);
171            if (isAttribute(attributes, CLASS)) {
172                Class<?> clazz = (Class<?>) attributes.get(CLASS);
173                return createPicoContainer(clazz, decoratedPico, parentClassLoader);
174            } else {
175                return new DefaultClassLoadingPicoContainer(parentClassLoader, decoratedPico);
176            }
177        }
178    
179        private void changeComponentMonitor(MutablePicoContainer childContainer, ComponentMonitor monitor) {
180            if (childContainer instanceof ComponentMonitorStrategy) {
181                ((ComponentMonitorStrategy) childContainer).changeMonitor(monitor);
182            }
183        }
184    
185        private ClassLoadingPicoContainer createPicoContainer(Class<?> clazz, MutablePicoContainer decoratedPico,
186                ClassLoader parentClassLoader) {
187            DefaultPicoContainer instantiatingContainer = new DefaultPicoContainer();
188            instantiatingContainer.addComponent(ClassLoader.class, parentClassLoader);
189            instantiatingContainer.addComponent(MutablePicoContainer.class, decoratedPico);
190            instantiatingContainer.addComponent(ClassLoadingPicoContainer.class, clazz);
191            Object componentInstance = instantiatingContainer.getComponent(ClassLoadingPicoContainer.class);
192            return (ClassLoadingPicoContainer) componentInstance;
193        }
194    
195        private ComponentFactory createComponentFactory(Map<String,Object> attributes) {
196            final ComponentFactory factory = (ComponentFactory) attributes.remove(COMPONENT_ADAPTER_FACTORY);
197            if (factory == null) {
198                return new Caching();
199            }
200            return factory;
201        }
202    
203        private ComponentMonitor createComponentMonitor(Map<String,Object> attributes) {
204            final ComponentMonitor monitor = (ComponentMonitor) attributes.remove(COMPONENT_MONITOR);
205            if (monitor == null) {
206                return new AbstractComponentMonitor();
207            }
208            return monitor;
209        }
210    
211    }