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 }