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.xml; 009 010 import java.io.File; 011 import java.io.IOException; 012 import java.io.Reader; 013 import java.io.Serializable; 014 import java.net.MalformedURLException; 015 import java.net.URL; 016 import java.security.Permission; 017 import java.util.ArrayList; 018 import java.util.List; 019 import java.util.Properties; 020 import java.lang.reflect.Type; 021 022 import javax.xml.parsers.DocumentBuilder; 023 import javax.xml.parsers.DocumentBuilderFactory; 024 import javax.xml.parsers.ParserConfigurationException; 025 026 import org.picocontainer.Characteristics; 027 import org.picocontainer.ComponentFactory; 028 import org.picocontainer.DefaultPicoContainer; 029 import org.picocontainer.MutablePicoContainer; 030 import org.picocontainer.Parameter; 031 import org.picocontainer.PicoClassNotFoundException; 032 import org.picocontainer.PicoCompositionException; 033 import org.picocontainer.PicoContainer; 034 import org.picocontainer.ComponentAdapter; 035 import org.picocontainer.ComponentMonitor; 036 import org.picocontainer.LifecycleStrategy; 037 import org.picocontainer.Injector; 038 import org.picocontainer.BehaviorFactory; 039 import org.picocontainer.injectors.ConstructorInjection; 040 import org.picocontainer.injectors.AbstractInjectionFactory; 041 import org.picocontainer.injectors.SingleMemberInjector; 042 import org.picocontainer.classname.ClassPathElement; 043 import org.picocontainer.classname.ClassLoadingPicoContainer; 044 import org.picocontainer.classname.DefaultClassLoadingPicoContainer; 045 import org.picocontainer.behaviors.Caching; 046 //import org.picocontainer.injectors.ConstructorInjection; 047 import org.picocontainer.lifecycle.NullLifecycleStrategy; 048 import org.picocontainer.monitors.NullComponentMonitor; 049 import org.picocontainer.parameters.ComponentParameter; 050 import org.picocontainer.parameters.ConstantParameter; 051 import org.picocontainer.classname.ClassName; 052 import org.picocontainer.script.LifecycleMode; 053 import org.picocontainer.script.ScriptedBuilder; 054 import org.picocontainer.script.ScriptedContainerBuilder; 055 import org.picocontainer.script.ScriptedPicoContainerMarkupException; 056 import org.w3c.dom.Element; 057 import org.w3c.dom.NodeList; 058 import org.xml.sax.EntityResolver; 059 import org.xml.sax.InputSource; 060 import org.xml.sax.SAXException; 061 import static org.picocontainer.script.xml.AttributeUtils.*; 062 import static org.picocontainer.script.xml.XMLConstants.*; 063 064 /** 065 * This class builds up a hierarchy of PicoContainers from an XML configuration file. 066 * 067 * @author Paul Hammant 068 * @author Aslak Hellesøy 069 * @author Jeppe Cramon 070 * @author Mauro Talevi 071 */ 072 public class XMLContainerBuilder extends ScriptedContainerBuilder { 073 074 private final static String DEFAULT_COMPONENT_INSTANCE_FACTORY = BeanComponentInstanceFactory.class.getName(); 075 076 077 078 private Element rootElement; 079 080 081 /** 082 * The XMLComponentInstanceFactory globally defined for the container. 083 * It may be overridden at node level. 084 */ 085 private XMLComponentInstanceFactory componentInstanceFactory; 086 087 public XMLContainerBuilder(Reader script, ClassLoader classLoader) { 088 this(script,classLoader, LifecycleMode.AUTO_LIFECYCLE); 089 } 090 091 public XMLContainerBuilder(Reader script, ClassLoader classLoader, LifecycleMode lifecycleMode) { 092 super(script, classLoader, lifecycleMode); 093 try { 094 DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 095 parse(documentBuilder, new InputSource(script)); 096 } catch (ParserConfigurationException e) { 097 throw new ScriptedPicoContainerMarkupException(e); 098 } 099 } 100 101 public XMLContainerBuilder(final URL script, ClassLoader classLoader) { 102 this(script,classLoader, LifecycleMode.AUTO_LIFECYCLE); 103 } 104 105 public XMLContainerBuilder(final URL script, ClassLoader classLoader, LifecycleMode lifecycleMode) { 106 super(script, classLoader, lifecycleMode); 107 try { 108 DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 109 documentBuilder.setEntityResolver(new EntityResolver() { 110 public InputSource resolveEntity(String publicId, String systemId) throws IOException { 111 URL url = new URL(script, systemId); 112 return new InputSource(url.openStream()); 113 } 114 }); 115 parse(documentBuilder, new InputSource(script.toString())); 116 } catch (ParserConfigurationException e) { 117 throw new ScriptedPicoContainerMarkupException(e); 118 } 119 } 120 121 private void parse(DocumentBuilder documentBuilder, InputSource inputSource) { 122 try { 123 rootElement = documentBuilder.parse(inputSource).getDocumentElement(); 124 } catch (SAXException e) { 125 throw new ScriptedPicoContainerMarkupException(e); 126 } catch (IOException e) { 127 throw new ScriptedPicoContainerMarkupException(e); 128 } 129 } 130 131 protected PicoContainer createContainerFromScript(PicoContainer parentContainer, Object assemblyScope) { 132 try { 133 // create ComponentInstanceFactory for the container 134 componentInstanceFactory = createComponentInstanceFactory(rootElement.getAttribute(COMPONENT_INSTANCE_FACTORY)); 135 MutablePicoContainer childContainer = createMutablePicoContainer( 136 parentContainer, new ContainerOptions(rootElement)); 137 populateContainer(childContainer); 138 return childContainer; 139 } catch (PicoClassNotFoundException e) { 140 throw new ScriptedPicoContainerMarkupException("Class not found:" + e.getMessage(), e); 141 } 142 } 143 144 private MutablePicoContainer createMutablePicoContainer(PicoContainer parentContainer, ContainerOptions containerOptions) throws PicoCompositionException { 145 boolean caching = containerOptions.isCaching(); 146 boolean inherit = containerOptions.isInheritParentBehaviors(); 147 String monitorName = containerOptions.getMonitorName(); 148 String componentFactoryName = containerOptions.getComponentFactoryName(); 149 150 if (inherit) { 151 if (!(parentContainer instanceof MutablePicoContainer)) { 152 throw new PicoCompositionException("For behavior inheritance to be used, the parent picocontainer must be of type MutablePicoContainer"); 153 } 154 155 MutablePicoContainer parentPico = (MutablePicoContainer)parentContainer; 156 return parentPico.makeChildContainer(); 157 } 158 159 ScriptedBuilder builder = new ScriptedBuilder(parentContainer); 160 if (caching) builder.withCaching(); 161 return builder 162 .withClassLoader(getClassLoader()) 163 .withLifecycle() 164 .withComponentFactory(componentFactoryName) 165 .withMonitor(monitorName) 166 .build(); 167 168 } 169 170 public void populateContainer(MutablePicoContainer container) { 171 try { 172 String parentClass = rootElement.getAttribute("parentclassloader"); 173 ClassLoader classLoader = getClassLoader(); 174 if (parentClass != null && !EMPTY.equals(parentClass)) { 175 classLoader = classLoader.loadClass(parentClass).getClassLoader(); 176 } 177 ClassLoadingPicoContainer scriptedContainer = new DefaultClassLoadingPicoContainer(classLoader, container); 178 ClassLoadingPicoContainer classLoadingPicoContainer = new DefaultClassLoadingPicoContainer(getClassLoader()); 179 addComponentsAndChildContainers(scriptedContainer, rootElement, classLoadingPicoContainer); 180 } catch (ClassNotFoundException e) { 181 throw new ScriptedPicoContainerMarkupException("Class not found: " + e.getMessage(), e); 182 } catch (IOException e) { 183 throw new ScriptedPicoContainerMarkupException(e); 184 } catch (SAXException e) { 185 throw new ScriptedPicoContainerMarkupException(e); 186 } 187 } 188 189 private void addComponentsAndChildContainers(ClassLoadingPicoContainer parentContainer, Element containerElement, ClassLoadingPicoContainer knownComponentAdapterFactories) throws ClassNotFoundException, IOException, SAXException { 190 191 ClassLoadingPicoContainer metaContainer = new DefaultClassLoadingPicoContainer(getClassLoader(), 192 new CompFactoryWrappingComponentFactory(), knownComponentAdapterFactories); 193 NodeList children = containerElement.getChildNodes(); 194 // register classpath first, regardless of order in the document. 195 for (int i = 0; i < children.getLength(); i++) { 196 if (children.item(i) instanceof Element) { 197 Element childElement = (Element) children.item(i); 198 String name = childElement.getNodeName(); 199 if (CLASSPATH.equals(name)) { 200 addClasspath(parentContainer, childElement); 201 } 202 } 203 } 204 for (int i = 0; i < children.getLength(); i++) { 205 if (children.item(i) instanceof Element) { 206 Element childElement = (Element) children.item(i); 207 String name = childElement.getNodeName(); 208 if (CONTAINER.equals(name)) { 209 MutablePicoContainer childContainer = parentContainer.makeChildContainer(); 210 ClassLoadingPicoContainer childPicoContainer = new DefaultClassLoadingPicoContainer(parentContainer.getComponentClassLoader(), childContainer); 211 addComponentsAndChildContainers(childPicoContainer, childElement, metaContainer); 212 } else if (COMPONENT_IMPLEMENTATION.equals(name) 213 || COMPONENT.equals(name)) { 214 addComponent(parentContainer, childElement, new Properties[0]); 215 } else if (COMPONENT_INSTANCE.equals(name)) { 216 registerComponentInstance(parentContainer, childElement); 217 } else if (COMPONENT_ADAPTER.equals(name)) { 218 addComponentAdapter(parentContainer, childElement, metaContainer); 219 } else if (COMPONENT_ADAPTER_FACTORY.equals(name)) { 220 addComponentFactory(childElement, metaContainer); 221 } else if (CLASSLOADER.equals(name)) { 222 addClassLoader(parentContainer, childElement, metaContainer); 223 } else if (!CLASSPATH.equals(name)) { 224 throw new ScriptedPicoContainerMarkupException("Unsupported element:" + name); 225 } 226 } 227 } 228 } 229 230 231 private void addComponentFactory(Element element, ClassLoadingPicoContainer metaContainer) throws MalformedURLException, ClassNotFoundException { 232 if (notSet(element.getAttribute(KEY))) { 233 throw new ScriptedPicoContainerMarkupException("'" + KEY + "' attribute not specified for " + element.getNodeName()); 234 } 235 Element node = (Element)element.cloneNode(false); 236 NodeList children = element.getChildNodes(); 237 String key = null; 238 for (int i = 0; i < children.getLength(); i++) { 239 if (children.item(i) instanceof Element) { 240 Element childElement = (Element) children.item(i); 241 String name = childElement.getNodeName(); 242 if (COMPONENT_ADAPTER_FACTORY.equals(name)) { 243 if (!"".equals(childElement.getAttribute(KEY))) { 244 throw new ScriptedPicoContainerMarkupException("'" + KEY + "' attribute must not be specified for nested " + element.getNodeName()); 245 } 246 childElement = (Element)childElement.cloneNode(true); 247 key = "ContrivedKey:" + String.valueOf(System.identityHashCode(childElement)); 248 childElement.setAttribute(KEY, key); 249 addComponentFactory(childElement, metaContainer); 250 // replace nested CAF with a ComponentParameter using an internally generated key 251 //Element parameter = node.getOwnerDocument().createElement(PARAMETER); 252 //parameter.setAttribute(KEY, key); 253 //node.appendChild(parameter); 254 } else if (PARAMETER.equals(name)) { 255 node.appendChild(childElement.cloneNode(true)); 256 } 257 } 258 } 259 // handle CAF now as standard component in the metaContainer 260 if (key != null) { 261 addComponent(metaContainer, node, new ForCaf(key)); 262 } else { 263 addComponent(metaContainer, node, new ForCaf[0]); 264 } 265 } 266 267 @SuppressWarnings("serial") 268 public class ForCaf extends Properties { 269 270 public ForCaf(String key) { 271 super.put("ForCAF", key); 272 } 273 } 274 275 private void addClassLoader(ClassLoadingPicoContainer parentContainer, Element childElement, ClassLoadingPicoContainer metaContainer) throws IOException, SAXException, ClassNotFoundException { 276 String parentClass = childElement.getAttribute("parentclassloader"); 277 ClassLoader parentClassLoader = parentContainer.getComponentClassLoader(); 278 if (parentClass != null && !EMPTY.equals(parentClass)) { 279 parentClassLoader = parentClassLoader.loadClass(parentClass).getClassLoader(); 280 } 281 ClassLoadingPicoContainer scripted = new DefaultClassLoadingPicoContainer(parentClassLoader, parentContainer); 282 addComponentsAndChildContainers(scripted, childElement, metaContainer); 283 } 284 285 private void addClasspath(ClassLoadingPicoContainer container, Element classpathElement) throws IOException, ClassNotFoundException { 286 NodeList children = classpathElement.getChildNodes(); 287 for (int i = 0; i < children.getLength(); i++) { 288 if (children.item(i) instanceof Element) { 289 Element childElement = (Element) children.item(i); 290 291 String fileName = childElement.getAttribute(FILE); 292 String urlSpec = childElement.getAttribute(URL); 293 URL url; 294 if (urlSpec != null && !EMPTY.equals(urlSpec)) { 295 url = new URL(urlSpec); 296 } else { 297 File file = new File(fileName); 298 if (!file.exists()) { 299 throw new IOException(file.getAbsolutePath() + " doesn't exist"); 300 } 301 url = file.toURL(); 302 } 303 ClassPathElement cpe = container.addClassLoaderURL(url); 304 addPermissions(cpe, childElement); 305 } 306 } 307 } 308 309 private void addPermissions(ClassPathElement classPathElement, Element classPathXmlElement) throws ClassNotFoundException { 310 NodeList children = classPathXmlElement.getChildNodes(); 311 for (int i = 0; i < children.getLength(); i++) { 312 if (children.item(i) instanceof Element) { 313 Element childElement = (Element) children.item(i); 314 315 String permissionClassName = childElement.getAttribute(CLASSNAME); 316 String action = childElement.getAttribute(CONTEXT); 317 String value = childElement.getAttribute(VALUE); 318 MutablePicoContainer mpc = new DefaultPicoContainer(); 319 mpc.addComponent(Permission.class, Class.forName(permissionClassName), new ConstantParameter(action), new ConstantParameter(value)); 320 321 Permission permission = mpc.getComponent(Permission.class); 322 classPathElement.grantPermission(permission); 323 } 324 } 325 326 } 327 328 private void addComponent(ClassLoadingPicoContainer container, Element element, Properties... props) throws ClassNotFoundException, MalformedURLException { 329 String className = element.getAttribute(CLASS); 330 if (notSet(className)) { 331 throw new ScriptedPicoContainerMarkupException("'" + CLASS + "' attribute not specified for " + element.getNodeName()); 332 } 333 334 Parameter[] parameters = createChildParameters(container, element); 335 Class<?> clazz = container.getComponentClassLoader().loadClass(className); 336 Object key = element.getAttribute(KEY); 337 if (notSet(key)) { 338 String classKey = element.getAttribute(CLASS_NAME_KEY); 339 if (isSet(classKey)) { 340 key = getClassLoader().loadClass(classKey); 341 } else { 342 key = clazz; 343 } 344 } 345 if (parameters == null) { 346 container.addComponent(key, clazz); 347 } else { 348 container.as(props).addComponent(key, clazz, parameters); 349 } 350 } 351 352 353 354 private Parameter[] createChildParameters(ClassLoadingPicoContainer container, Element element) throws ClassNotFoundException, MalformedURLException { 355 List<Parameter> parametersList = new ArrayList<Parameter>(); 356 NodeList children = element.getChildNodes(); 357 for (int i = 0; i < children.getLength(); i++) { 358 if (children.item(i) instanceof Element) { 359 Element childElement = (Element) children.item(i); 360 if (PARAMETER.equals(childElement.getNodeName())) { 361 parametersList.add(createParameter(container, childElement)); 362 } 363 364 if (PARAMETER_ZERO.equals(childElement.getNodeName())) { 365 //Check: We can't check everything here since we aren't schema validating 366 //But it will at least catch some goofs. 367 if (!parametersList.isEmpty()) { 368 throw new PicoCompositionException("Cannot mix other parameters with '" + PARAMETER_ZERO +"' nodes." ); 369 } 370 371 return Parameter.ZERO; 372 } 373 } 374 } 375 376 Parameter[] parameters = null; 377 if (!parametersList.isEmpty()) { 378 parameters = parametersList.toArray(new Parameter[parametersList.size()]); 379 } 380 return parameters; 381 } 382 383 /** 384 * Build the org.picocontainer.Parameter from the <code>parameter</code> element. This could 385 * create either a ComponentParameter or ConstantParameter instance, 386 * depending on the values of the element's attributes. This is somewhat 387 * complex because there are five constructors for ComponentParameter and one for 388 * ConstantParameter. These are: 389 * 390 * <a href="http://www.picocontainer.org/picocontainer/latest/picocontainer/apidocs/org/picocontainer/defaults/ComponentParameter.html">ComponentParameter Javadocs</a>: 391 * 392 * <code>ComponentParameter() - Expect any scalar paramter of the appropriate type or an Array. 393 * ComponentParameter(boolean emptyCollection) - Expect any scalar paramter of the appropriate type or an Array. 394 * ComponentParameter(Class componentValueType, boolean emptyCollection) - Expect any scalar paramter of the appropriate type or the collecting type Array,Collectionor Map. 395 * ComponentParameter(Class componentKeyType, Class componentValueType, boolean emptyCollection) - Expect any scalar paramter of the appropriate type or the collecting type Array,Collectionor Map. 396 * ComponentParameter(Object componentKey) - Expect a parameter matching a component of a specific key.</code> 397 * 398 * and 399 * 400 * <a href="http://www.picocontainer.org/picocontainer/latest/picocontainer/apidocs/org/picocontainer/defaults/ConstantParameter.html">ConstantParameter Javadocs</a>: 401 * 402 * <code>ConstantParameter(Object value)</code> 403 * 404 * The rules for this are, in order: 405 * 406 * 1) If the <code>key</code> attribute is not null/empty, the fifth constructor will be used. 407 * 2) If the <code>componentKeyType</code> attribute is not null/empty, the fourth constructor will be used. 408 * In this case, both the <code>componentValueType</code> and <code>emptyCollection</code> attributes must be non-null/empty or an exception will be thrown. 409 * 3) If the <code>componentValueType</code> attribute is not null/empty, the third constructor will be used. 410 * In this case, the <code>emptyCollection</code> attribute must be non-null/empty. 411 * 4) If the <code>emptyCollection</code> attribute is not null/empty, the second constructor will be used. 412 * 5) If there is no child element of the parameter, the first constructor will be used. 413 * 6) Otherwise, the return value will be a ConstantParameter with the return from the createInstance value. 414 * @param element 415 * @param pico 416 * @return 417 * @throws ClassNotFoundException 418 * @throws MalformedURLException 419 */ 420 private Parameter createParameter(PicoContainer pico, Element element) throws ClassNotFoundException, MalformedURLException { 421 final Parameter parameter; 422 String key = element.getAttribute(KEY); 423 String emptyCollectionString = element.getAttribute(EMPTY_COLLECTION); 424 String componentValueTypeString = element.getAttribute(COMPONENT_VALUE_TYPE); 425 String componentKeyTypeString = element.getAttribute(COMPONENT_KEY_TYPE); 426 427 // key not null/empty takes precidence 428 if (key != null && !EMPTY.equals(key)) { 429 parameter = new ComponentParameter(key); 430 } else if (componentKeyTypeString != null && !EMPTY.equals(componentKeyTypeString)) { 431 if (emptyCollectionString == null || componentValueTypeString == null || 432 EMPTY.equals(emptyCollectionString) || EMPTY.equals(componentValueTypeString)) { 433 434 throw new ScriptedPicoContainerMarkupException("The componentKeyType attribute was specified (" + 435 componentKeyTypeString + ") but one or both of the emptyCollection (" + 436 emptyCollectionString + ") or componentValueType (" + componentValueTypeString + 437 ") was empty or null."); 438 } 439 440 Class<?> componentKeyType = getClassLoader().loadClass(componentKeyTypeString); 441 Class<?> componentValueType = getClassLoader().loadClass(componentValueTypeString); 442 443 boolean emptyCollection = Boolean.valueOf(emptyCollectionString); 444 445 parameter = new ComponentParameter(componentKeyType, componentValueType, emptyCollection); 446 } else if (componentValueTypeString != null && !EMPTY.equals(componentValueTypeString)) { 447 if (emptyCollectionString == null || EMPTY.equals(emptyCollectionString)) { 448 449 throw new ScriptedPicoContainerMarkupException("The componentValueType attribute was specified (" + 450 componentValueTypeString + ") but the emptyCollection (" + 451 emptyCollectionString + ") was empty or null."); 452 } 453 454 Class<?> componentValueType = getClassLoader().loadClass(componentValueTypeString); 455 456 boolean emptyCollection = Boolean.valueOf(emptyCollectionString); 457 458 parameter = new ComponentParameter(componentValueType, emptyCollection); 459 } else if (emptyCollectionString != null && !EMPTY.equals(emptyCollectionString)) { 460 boolean emptyCollection = Boolean.valueOf(emptyCollectionString); 461 462 parameter = new ComponentParameter(emptyCollection); 463 } 464 else if (getFirstChildElement(element, false) == null) { 465 parameter = new ComponentParameter(); 466 } else { 467 Object instance = createInstance(pico, element); 468 parameter = new ConstantParameter(instance); 469 } 470 return parameter; 471 } 472 473 474 private void registerComponentInstance(ClassLoadingPicoContainer container, Element element) throws ClassNotFoundException, PicoCompositionException, MalformedURLException { 475 Object instance = createInstance(container, element); 476 String key = element.getAttribute(KEY); 477 String classKey = element.getAttribute(CLASS_NAME_KEY); 478 if (notSet(key)) { 479 if (!notSet(classKey)) { 480 container.addComponent(getClassLoader().loadClass(classKey), instance); 481 } else { 482 container.addComponent(instance); 483 } 484 } else { 485 container.addComponent(key, instance); 486 } 487 } 488 489 private Object createInstance(PicoContainer pico, Element element) throws MalformedURLException { 490 XMLComponentInstanceFactory factory = createComponentInstanceFactory(element.getAttribute(FACTORY)); 491 Element instanceElement = getFirstChildElement(element, true); 492 return factory.makeInstance(pico, instanceElement, getClassLoader()); 493 } 494 495 private Element getFirstChildElement(Element parent, boolean fail) { 496 NodeList children = parent.getChildNodes(); 497 Element child = null; 498 for (int i = 0; i < children.getLength(); i++) { 499 if (children.item(i) instanceof Element) { 500 child = (Element) children.item(i); 501 break; 502 } 503 } 504 if (child == null && fail) { 505 throw new ScriptedPicoContainerMarkupException(parent.getNodeName() + " needs a child element"); 506 } 507 return child; 508 } 509 510 private XMLComponentInstanceFactory createComponentInstanceFactory(String factoryClass) { 511 if ( notSet(factoryClass)) { 512 // no factory has been specified for the node 513 // return globally defined factory for the container - if there is one 514 if (componentInstanceFactory != null) { 515 return componentInstanceFactory; 516 } 517 factoryClass = DEFAULT_COMPONENT_INSTANCE_FACTORY; 518 } 519 520 // using a PicoContainer is overkill here. 521 try { 522 return (XMLComponentInstanceFactory)getClassLoader().loadClass(factoryClass).newInstance(); 523 } catch (InstantiationException e) { 524 throw new PicoCompositionException(e); 525 } catch (IllegalAccessException e) { 526 throw new PicoCompositionException(e); 527 } catch (ClassNotFoundException e) { 528 throw new PicoClassNotFoundException(factoryClass, e); 529 } 530 } 531 532 private void addComponentAdapter(ClassLoadingPicoContainer container, Element element, ClassLoadingPicoContainer metaContainer) throws ClassNotFoundException, PicoCompositionException, MalformedURLException { 533 String className = element.getAttribute(CLASS); 534 if (notSet(className)) { 535 throw new ScriptedPicoContainerMarkupException("'" + CLASS + "' attribute not specified for " + element.getNodeName()); 536 } 537 Class<?> implementationClass = getClassLoader().loadClass(className); 538 Object key = element.getAttribute(KEY); 539 String classKey = element.getAttribute(CLASS_NAME_KEY); 540 if (notSet(key)) { 541 if (!notSet(classKey)) { 542 key = getClassLoader().loadClass(classKey); 543 } else { 544 key = implementationClass; 545 } 546 } 547 Parameter[] parameters = createChildParameters(container, element); 548 ComponentFactory componentFactory = createComponentFactory(element.getAttribute(FACTORY), metaContainer); 549 550 container.as(Characteristics.NONE).addAdapter(componentFactory.createComponentAdapter(new NullComponentMonitor(), new NullLifecycleStrategy(), new Properties(), key, implementationClass, parameters)); 551 } 552 553 private ComponentFactory createComponentFactory(String factoryName, ClassLoadingPicoContainer metaContainer) throws PicoCompositionException { 554 if ( notSet(factoryName)) { 555 return new Caching().wrap(new ConstructorInjection()); 556 } 557 final Serializable key; 558 if (metaContainer.getComponentAdapter(factoryName) != null) { 559 key = factoryName; 560 } else { 561 metaContainer.addComponent(ComponentFactory.class, new ClassName(factoryName)); 562 key = ComponentFactory.class; 563 } 564 return (ComponentFactory) metaContainer.getComponent(key); 565 } 566 567 568 @SuppressWarnings({"serial","synthetic-access"}) 569 public static class CompFactoryWrappingComponentFactory extends AbstractInjectionFactory { 570 571 ConstructorInjection constructorInjection = new ConstructorInjection(); 572 573 public <T> ComponentAdapter<T> createComponentAdapter(ComponentMonitor monitor, LifecycleStrategy lifecycle, Properties props, Object key, Class<T> impl, Parameter... parms) 574 throws PicoCompositionException { 575 576 ComponentAdapter<T> adapter = constructorInjection.createComponentAdapter(monitor, lifecycle, props, key, impl, parms); 577 String otherKey = props.getProperty("ForCAF"); 578 if (otherKey != null && !otherKey.equals("")) { 579 props.remove("ForCAF"); 580 return new MySingleMemberInjector(key, impl, parms, monitor, false, otherKey, (Injector) adapter); 581 } 582 return adapter; 583 } 584 } 585 586 @SuppressWarnings("serial") 587 private static class MySingleMemberInjector extends SingleMemberInjector { 588 private final String otherKey; 589 private final Injector injector; 590 591 private MySingleMemberInjector(Object key, Class impl, Parameter[] parms, 592 ComponentMonitor monitor, 593 boolean useNames, String otherKey, Injector injector) { 594 super(key, impl, parms, monitor, useNames); 595 this.otherKey = otherKey; 596 this.injector = injector; 597 } 598 599 @Override 600 public Object getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException { 601 BehaviorFactory bf = (BehaviorFactory) injector.getComponentInstance(container, into); 602 bf.wrap((ComponentFactory) container.getComponent(otherKey)); 603 return bf; 604 } 605 } 606 }