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    }