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