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 }