001    /*****************************************************************************
002     * Copyright (C) PicoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     *                                                                           *
008     * Original code by Aslak Hellesoy and Paul Hammant                          *
009     *****************************************************************************/
010    
011    package org.picocontainer.script.xml;
012    
013    import static org.junit.Assert.assertEquals;
014    import static org.junit.Assert.assertFalse;
015    import static org.junit.Assert.assertNotNull;
016    import static org.junit.Assert.assertNotSame;
017    import static org.junit.Assert.assertSame;
018    import static org.junit.Assert.assertTrue;
019    import static org.junit.Assert.fail;
020    
021    import java.io.File;
022    import java.io.IOException;
023    import java.io.Reader;
024    import java.io.StringReader;
025    import java.io.StringWriter;
026    import java.io.Writer;
027    import java.text.SimpleDateFormat;
028    import java.util.Date;
029    import java.util.Map;
030    
031    import javax.swing.JButton;
032    
033    import org.junit.Test;
034    import org.picocontainer.ComponentAdapter;
035    import org.picocontainer.ComponentFactory;
036    import org.picocontainer.DefaultPicoContainer;
037    import org.picocontainer.MutablePicoContainer;
038    import org.picocontainer.PicoContainer;
039    import org.picocontainer.PicoException;
040    import org.picocontainer.behaviors.AbstractBehaviorFactory;
041    import org.picocontainer.behaviors.Caching;
042    import org.picocontainer.containers.EmptyPicoContainer;
043    import org.picocontainer.injectors.AdaptingInjection;
044    import org.picocontainer.injectors.ConstructorInjection;
045    import org.picocontainer.lifecycle.StartableLifecycleStrategy;
046    import org.picocontainer.monitors.NullComponentMonitor;
047    import org.picocontainer.monitors.WriterComponentMonitor;
048    import org.picocontainer.script.AbstractScriptedContainerBuilderTestCase;
049    import org.picocontainer.script.ScriptedPicoContainerMarkupException;
050    import org.picocontainer.script.TestHelper;
051    import org.picocontainer.script.testmodel.CustomerEntityImpl;
052    import org.picocontainer.script.testmodel.DefaultWebServerConfig;
053    import org.picocontainer.script.testmodel.Entity;
054    import org.picocontainer.script.testmodel.ListSupport;
055    import org.picocontainer.script.testmodel.MapSupport;
056    import org.picocontainer.script.testmodel.OrderEntityImpl;
057    import org.picocontainer.script.testmodel.WebServerConfig;
058    import org.picocontainer.script.testmodel.WebServerConfigComp;
059    import org.picocontainer.script.xml.XMLComponentInstanceFactory;
060    import org.picocontainer.script.xml.XMLContainerBuilder;
061    import org.picocontainer.testmodel.SimpleTouchable;
062    import org.picocontainer.testmodel.Touchable;
063    import org.w3c.dom.Element;
064    import org.xml.sax.SAXException;
065    
066    import com.thoughtworks.xstream.XStream;
067    
068    /**
069     * @author Paul Hammant
070     * @author Aslak Hellesøy
071     * @author Jeppe Cramon
072     * @author Mauro Talevi
073     */
074    public final class XMLContainerBuilderTestCase extends AbstractScriptedContainerBuilderTestCase {
075    
076        //TODO some tests for XMLContainerBuilder that use a classloader that is retrieved at testtime.
077        // i.e. not a programatic consequence of this.getClass().getClassLoader()
078    
079        @Test public void testCreateSimpleContainer() {
080            Reader script = new StringReader("" +
081                    "<container>" +
082                    "  <component-implementation class='java.lang.StringBuffer'/>" +
083                    "  <component-implementation class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
084                    "  <component-implementation key='org.picocontainer.script.testmodel.WebServer' class='org.picocontainer.script.testmodel.WebServerImpl'/>" +
085                    "</container>");
086    
087            PicoContainer pico = buildContainer(script);
088            assertEquals(3, pico.getComponents().size());
089            assertNotNull(pico.getComponent(StringBuffer.class));
090            assertNotNull(pico.getComponent(DefaultWebServerConfig.class));
091            assertNotNull(pico.getComponent("org.picocontainer.script.testmodel.WebServer"));
092        }
093    
094        @Test public void testCreateSimpleContainerWithExplicitKeysAndParameters() {
095            Reader script = new StringReader("" +
096                    "<container>" +
097                    "  <component-implementation key='aBuffer' class='java.lang.StringBuffer'/>" +
098                    "  <component-implementation key='org.picocontainer.script.testmodel.WebServerConfig' class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
099                    "  <component-implementation key='org.picocontainer.script.testmodel.WebServer' class='org.picocontainer.script.testmodel.WebServerImpl'>" +
100                    "               <parameter key='org.picocontainer.script.testmodel.WebServerConfig'/>" +
101                    "               <parameter key='aBuffer'/>" +
102                    "  </component-implementation>" +
103                    "</container>");
104    
105            PicoContainer pico = buildContainer(script);
106            assertEquals(3, pico.getComponents().size());
107            assertNotNull(pico.getComponent("aBuffer"));
108            assertNotNull(pico.getComponent("org.picocontainer.script.testmodel.WebServerConfig"));
109            assertNotNull(pico.getComponent("org.picocontainer.script.testmodel.WebServer"));
110        }
111    
112        @Test public void testCreateSimpleContainerWithExplicitKeysAndImplicitParameter() {
113            Reader script = new StringReader("" +
114                    "<container>" +
115                    "  <component-implementation key='aBuffer' class='java.lang.StringBuffer'/>" +
116                    "  <component-implementation key='org.picocontainer.script.testmodel.WebServerConfig' class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
117                    "  <component-implementation key='org.picocontainer.script.testmodel.WebServer' class='org.picocontainer.script.testmodel.WebServerImpl'>" +
118                    "       <parameter/>" +
119                    "       <parameter key='aBuffer'/>" +
120                    "  </component-implementation>" +
121                    "</container>");
122    
123            PicoContainer pico = buildContainer(script);
124            assertEquals(3, pico.getComponents().size());
125            assertNotNull(pico.getComponent("aBuffer"));
126            assertNotNull(pico.getComponent("org.picocontainer.script.testmodel.WebServerConfig"));
127            assertNotNull(pico.getComponent("org.picocontainer.script.testmodel.WebServer"));
128        }
129    
130        @Test public void testNonParameterElementsAreIgnoredInComponentImplementation() {
131            Reader script = new StringReader("" +
132                    "<container>" +
133                    "  <component-implementation key='aBuffer' class='java.lang.StringBuffer'/>" +
134                    "  <component-implementation key='org.picocontainer.script.testmodel.WebServerConfig' class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
135                    "  <component-implementation key='org.picocontainer.script.testmodel.WebServer' class='org.picocontainer.script.testmodel.WebServerImpl'>" +
136                    "               <parameter key='org.picocontainer.script.testmodel.WebServerConfig'/>" +
137                    "               <parameter key='aBuffer'/>" +
138                    "               <any-old-stuff/>" +
139                    "  </component-implementation>" +
140                    "</container>");
141    
142            PicoContainer pico = buildContainer(script);
143            assertEquals(3, pico.getComponents().size());
144            assertNotNull(pico.getComponent("aBuffer"));
145            assertNotNull(pico.getComponent("org.picocontainer.script.testmodel.WebServerConfig"));
146            assertNotNull(pico.getComponent("org.picocontainer.script.testmodel.WebServer"));
147        }
148    
149        @Test public void testContainerCanHostAChild() {
150            Reader script = new StringReader("" +
151                    "<container>" +
152                    "  <component-implementation class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
153                    "  <component-implementation class='java.lang.StringBuffer'/>" +
154                    "  <container>" +
155                    "    <component-implementation key='org.picocontainer.script.testmodel.WebServer' class='org.picocontainer.script.testmodel.WebServerImpl'/>" +
156                    "  </container>" +
157                    "</container>");
158    
159            PicoContainer pico = buildContainer(script);
160            assertNotNull(pico.getComponent(DefaultWebServerConfig.class));
161    
162            StringBuffer sb = pico.getComponent(StringBuffer.class);
163            assertTrue(sb.toString().indexOf("-WebServerImpl") != -1);
164        }
165    
166        @Test public void testClassLoaderHierarchy() throws IOException {
167            File testCompJar = TestHelper.getTestCompJarFile();
168            File testCompJar2 = new File(testCompJar.getParentFile(), "TestComp2.jar");
169            File notStartableJar = new File(testCompJar.getParentFile(), "NotStartable.jar");
170    
171            assertTrue(testCompJar.isFile());
172            assertTrue(testCompJar2.isFile());
173    
174            Reader script = new StringReader("" +
175                    "<container>" +
176                    "  <classpath>" +
177                    "    <element file='" + testCompJar.getCanonicalPath() + "'>" +
178                    "      <grant classname='java.io.FilePermission' context='*' value='read' />" +
179                    "    </element>" +
180                    "  </classpath>" +
181                    "  <component-implementation key='foo' class='TestComp'/>" +
182                    "  <container>" +
183                    "    <classpath>" +
184                    "      <element file='" + testCompJar2.getCanonicalPath() + "'/>" +
185                    "      <element file='" + notStartableJar.getCanonicalPath() + "'/>" +
186                    "    </classpath>" +
187                    "    <component-implementation key='bar' class='TestComp2'/>" +
188                    "    <component-implementation key='phony' class='NotStartable'/>" +
189                    "  </container>" +
190                    "  <component-implementation class='java.lang.StringBuffer'/>" +
191                    "</container>");
192    
193            PicoContainer pico = buildContainer(script);
194            Object fooTestComp = pico.getComponent("foo");
195            assertNotNull("Container should have a 'foo' component", fooTestComp);
196    
197            StringBuffer sb = pico.getComponent(StringBuffer.class);
198            assertTrue("Container should have instantiated a 'TestComp2' component because it is Startable", sb.toString().indexOf("-TestComp2") != -1);
199            // We are using the DefaultLifecycleManager, which only instantiates Startable components, and not non-Startable components.
200            assertTrue("Container should NOT have instantiated a 'NotStartable' component because it is NOT Startable", sb.toString().indexOf("-NotStartable") == -1);
201        }
202    
203        @Test public void testUnknownclassThrowsPicoContainerMarkupException() {
204            try {
205                Reader script = new StringReader("" +
206                        "<container>" +
207                        "  <component-implementation class='FFFoo'/>" +
208                        "</container>");
209                buildContainer(script);
210                fail();
211            } catch (ScriptedPicoContainerMarkupException expected) {
212                assertTrue(expected.getCause() instanceof ClassNotFoundException);
213            }
214        }
215    
216        @Test public void testNoImplementationClassThrowsPicoContainerMarkupException() {
217            Reader script = new StringReader("" +
218                    "<container>" +
219                    "  <component-implementation/>" +
220                    "</container>");
221            try {
222                buildContainer(script);
223            } catch (ScriptedPicoContainerMarkupException expected) {
224                assertEquals("'class' attribute not specified for component-implementation", expected.getMessage());
225            }
226        }
227    
228        @Test public void testConstantParameterWithNoChildElementThrowsPicoContainerMarkupException() {
229            Reader script = new StringReader("" +
230                    "<container>" +
231                    "  <component-implementation class='org.picocontainer.script.xml.TestBeanComposer'>" +
232                    "               <parameter>" +
233                    "               </parameter>" +
234                    "  </component-implementation>" +
235                    "</container>");
236    
237            try {
238                buildContainer(script);
239            } catch (ScriptedPicoContainerMarkupException e) {
240                assertEquals("parameter needs a child element", e.getMessage());
241            }
242        }
243    
244        @Test public void testEmptyScriptDoesNotThrowsEmptyCompositionException() {
245            Reader script = new StringReader("<container/>");
246            buildContainer(script);
247        }
248    
249        @Test public void testCreateContainerFromScriptThrowsSAXException() {
250            Reader script = new StringReader("" +
251                    "<container component-adapter-factory='" + ConstructorInjection.class.getName() + "'>" +
252                    "  <component-implementation class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
253                    "<container>"); // open instead of close
254            try {
255                buildContainer(script);
256            } catch (ScriptedPicoContainerMarkupException e) {
257                assertTrue("SAXException", e.getCause() instanceof SAXException);
258            }
259        }
260    
261        @Test public void testCreateContainerFromNullScriptThrowsNullPointerException() {
262            Reader script = null;
263            try {
264                buildContainer(script);
265                fail("NullPointerException expected");
266            } catch (NullPointerException expected) {
267            }
268        }
269    
270        @Test public void testShouldThrowExceptionForNonExistantCafClass() {
271            Reader script = new StringReader("" +
272                    "<container component-adapter-factory='org.picocontainer.script.SomeInexistantFactory'>" +
273                    "  <component-implementation class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
274                    "</container>");
275            try {
276                buildContainer(script);
277                fail();
278            } catch (ScriptedPicoContainerMarkupException expected) {
279                assertTrue("Message of exception does not contain missing class", expected.getMessage().indexOf("org.picocontainer.script.SomeInexistantFactory") > 0);
280            }
281        }
282    
283        @Test public void testComponentInstanceWithNoChildElementThrowsPicoContainerMarkupException() {
284            Reader script = new StringReader("" +
285                    "<container>" +
286                    "  <component-instance>" +
287                    "  </component-instance>" +
288                    "</container>");
289    
290            try {
291                buildContainer(script);
292                fail();
293            } catch (ScriptedPicoContainerMarkupException expected) {
294                assertEquals("component-instance needs a child element", expected.getMessage());
295            }
296        }
297    
298        @Test public void testComponentInstanceWithFactoryCanBeUsed() {
299            Reader script = new StringReader("" +
300                    "<container>" +
301                    "  <component-instance factory='org.picocontainer.script.xml.XMLContainerBuilderTestCase$TestFactory'>" +
302                    "    <config-or-whatever/>" +
303                    "  </component-instance>" +
304                    "</container>");
305    
306            PicoContainer pico = buildContainer(script);
307            Object instance = pico.getComponents().get(0);
308            assertNotNull(instance);
309            assertTrue(instance instanceof String);
310            assertEquals("Hello", instance.toString());
311        }
312    
313        @Test public void testComponentInstanceWithDefaultFactory() {
314            Reader script = new StringReader("" +
315                    "<container>" +
316                    "  <component-instance>" +
317                    "       <org.picocontainer.script.xml.TestBean>" +
318                    "               <foo>10</foo>" +
319                    "               <bar>hello</bar>" +
320                    "       </org.picocontainer.script.xml.TestBean>" +
321                    "  </component-instance>" +
322                    "</container>");
323    
324            PicoContainer pico = buildContainer(script);
325            Object instance = pico.getComponents().get(0);
326            assertNotNull(instance);
327            assertTrue(instance instanceof TestBean);
328            assertEquals(10, ((TestBean) instance).getFoo());
329            assertEquals("hello", ((TestBean) instance).getBar());
330        }
331    
332        @Test public void testComponentInstanceWithBeanFactory() {
333            Reader script = new StringReader("" +
334                    "<container>" +
335                    "  <component-instance factory='org.picocontainer.script.xml.BeanComponentInstanceFactory'>" +
336                    "       <org.picocontainer.script.xml.TestBean>" +
337                    "               <foo>10</foo>" +
338                    "               <bar>hello</bar>" +
339                    "       </org.picocontainer.script.xml.TestBean>" +
340                    "  </component-instance>" +
341                    "</container>");
342    
343            PicoContainer pico = buildContainer(script);
344            Object instance = pico.getComponents().get(0);
345            assertNotNull(instance);
346            assertTrue(instance instanceof TestBean);
347            assertEquals(10, ((TestBean) instance).getFoo());
348            assertEquals("hello", ((TestBean) instance).getBar());
349        }
350    
351        @Test public void testComponentInstanceWithBeanFactoryAndInstanceThatIsDefinedInContainer() {
352            Reader script = new StringReader("" +
353                    "<container>" +
354                    "  <component-instance key='date' factory='org.picocontainer.script.xml.BeanComponentInstanceFactory'>" +
355                    "    <java.util.Date>" +
356                    "       <time>0</time>" +
357                    "    </java.util.Date>" +
358                    "  </component-instance>" +
359                    "  <component-instance factory='org.picocontainer.script.xml.BeanComponentInstanceFactory'>" +
360                    "    <java.text.SimpleDateFormat>" +
361                    "       <lenient>false</lenient>" +
362                    "       <date name='2DigitYearStart'>date</date>" +
363                    "    </java.text.SimpleDateFormat>" +
364                    "  </component-instance>" +
365                    "</container>");
366    
367            PicoContainer pico = buildContainer(script);
368            Object instance = pico.getComponent(SimpleDateFormat.class);
369            assertNotNull(instance);
370            assertTrue(instance instanceof SimpleDateFormat);
371            SimpleDateFormat format = ((SimpleDateFormat) instance);
372            assertFalse(format.isLenient());
373            assertEquals(new Date(0), format.get2DigitYearStart());
374        }
375    
376        @Test public void testComponentInstanceWithKey() {
377            Reader script = new StringReader("" +
378                    "<container>" +
379                    "  <component-instance key='aString'>" +
380                    "    <string>Hello</string>" +
381                    "  </component-instance>" +
382                    "" +
383                    "  <component-instance key='aLong'>" +
384                    "    <long>22</long>" +
385                    "  </component-instance>" +
386                    "" +
387                    "  <component-instance key='aButton'>" +
388                    "    <javax.swing.JButton>" +
389                    "      <text>Hello</text>" +
390                    "      <alignmentX>0.88</alignmentX>" +
391                    "    </javax.swing.JButton>" +
392                    "  </component-instance>" +
393                    "</container>");
394    
395            PicoContainer pico = buildContainer(script);
396            assertEquals("Hello", pico.getComponent("aString"));
397            assertEquals((long)22, pico.getComponent("aLong"));
398            JButton button = (JButton) pico.getComponent("aButton");
399            assertEquals("Hello", button.getText());
400            assertEquals(0.88, button.getAlignmentX(), 0.01);
401        }
402    
403        @Test public void testComponentInstanceWithClassKey() {
404            Reader script = new StringReader("" +
405                    "<container>" +
406                    "  <component-instance class-name-key='java.util.Map' factory='org.picocontainer.script.xml.XStreamComponentInstanceFactory'>" +
407                    "    <properties>" +
408                    "      <property name='foo' value='bar'/>" +
409                    "    </properties>" +
410                    "  </component-instance>" +
411                    "</container>");
412    
413            PicoContainer pico = buildContainer(script);
414            Map<?,?> map = pico.getComponent(Map.class);
415            assertNotNull(map);
416            assertEquals("bar", map.get("foo"));
417        }
418    
419        @Test public void testComponentInstanceWithFactoryAndKey() {
420            Reader script = new StringReader("" +
421                    "<container>" +
422                    "  <component-instance factory='org.picocontainer.script.xml.XMLContainerBuilderTestCase$TestFactory'" +
423                    "                                               key='aKey'>" +
424                    "    <config-or-whatever/>" +
425                    "  </component-instance>" +
426                    "</container>");
427    
428            PicoContainer pico = buildContainer(script);
429            Object instance = pico.getComponent("aKey");
430            assertNotNull(instance);
431            assertTrue(instance instanceof String);
432            assertEquals("Hello", instance.toString());
433        }
434    
435        @Test public void testComponentInstanceWithContainerFactoryAndKey() {
436            Reader script = new StringReader("" +
437                    "<container component-instance-factory='org.picocontainer.script.xml.XMLContainerBuilderTestCase$ContainerTestFactory'>" +
438                    "  <component-instance factory='org.picocontainer.script.xml.XMLContainerBuilderTestCase$TestFactory'" +
439                    "                                               key='firstKey'>" +
440                    "    <config-or-whatever/>" +
441                    "  </component-instance>" +
442                    "  <component-instance key='secondKey'>" +
443                    "    <config-or-whatever/>" +
444                    "  </component-instance>" +
445                    "</container>");
446    
447            PicoContainer pico = buildContainer(script);
448            Object first = pico.getComponent("firstKey");
449            assertNotNull(first);
450            assertTrue(first instanceof String);
451            assertEquals("Hello", first.toString());
452            Object second = pico.getComponent("secondKey");
453            assertNotNull(second);
454            assertTrue(second instanceof String);
455            assertEquals("ContainerHello", second.toString());
456        }
457    
458        public static class TestFactory implements XMLComponentInstanceFactory {
459            public Object makeInstance(PicoContainer pico, Element elem, ClassLoader classLoader) {
460                return "Hello";
461            }
462        }
463    
464        public static class ContainerTestFactory implements XMLComponentInstanceFactory {
465            public Object makeInstance(PicoContainer pico, Element elem, ClassLoader classLoader) {
466                return "ContainerHello";
467            }
468        }
469    
470        @Test public void testInstantiationOfComponentsWithParams() {
471            Reader script = new StringReader("" +
472                    "<container>" +
473                    "  <component-implementation class='org.picocontainer.script.testmodel.WebServerConfigComp'>" +
474                    "    <parameter><string>localhost</string></parameter>" +
475                    "    <parameter><int>8080</int></parameter>" +
476                    "  </component-implementation>" +
477                    "  <component-implementation key='org.picocontainer.script.testmodel.WebServer' class='org.picocontainer.script.testmodel.WebServerImpl'/>" +
478                    "</container>");
479            PicoContainer pico = buildContainer(script);
480            assertNotNull(pico.getComponent(WebServerConfigComp.class));
481            WebServerConfigComp config = pico.getComponent(WebServerConfigComp.class);
482            assertEquals("localhost", config.getHost());
483            assertEquals(8080, config.getPort());
484        }
485    
486        @Test public void testInstantiationOfComponentsWithParameterInstancesOfSameComponent() {
487            Reader script = new StringReader("" +
488                    "<container>" +
489                    "  <component-implementation class='org.picocontainer.script.xml.TestBeanComposer'>" +
490                    "               <parameter>" +
491                    "                       <org.picocontainer.script.xml.TestBean>" +
492                    "                               <foo>10</foo>" +
493                    "                               <bar>hello1</bar>" +
494                    "                       </org.picocontainer.script.xml.TestBean>" +
495                    "               </parameter>" +
496                    "               <parameter>" +
497                    "                       <org.picocontainer.script.xml.TestBean>" +
498                    "                               <foo>10</foo>" +
499                    "                               <bar>hello2</bar>" +
500                    "                       </org.picocontainer.script.xml.TestBean>" +
501                    "               </parameter>" +
502                    "  </component-implementation>" +
503                    "</container>");
504            PicoContainer pico = buildContainer(script);
505            assertNotNull(pico.getComponent(TestBeanComposer.class));
506            TestBeanComposer composer = pico.getComponent(TestBeanComposer.class);
507            assertEquals("bean1", "hello1", composer.getBean1().getBar());
508            assertEquals("bean2", "hello2", composer.getBean2().getBar());
509        }
510    
511        @Test public void testInstantiationOfComponentsWithParameterInstancesOfSameComponentAndBeanFactory() {
512            Reader script = new StringReader("" +
513                    "<container>" +
514                    "  <component-implementation class='org.picocontainer.script.xml.TestBeanComposer'>" +
515                    "               <parameter factory='org.picocontainer.script.xml.BeanComponentInstanceFactory'>" +
516                    "                       <org.picocontainer.script.xml.TestBean>" +
517                    "                               <foo>10</foo>" +
518                    "                               <bar>hello1</bar>" +
519                    "                       </org.picocontainer.script.xml.TestBean>" +
520                    "               </parameter>" +
521                    "               <parameter factory='org.picocontainer.script.xml.BeanComponentInstanceFactory'>" +
522                    "                       <org.picocontainer.script.xml.TestBean>" +
523                    "                               <foo>10</foo>" +
524                    "                               <bar>hello2</bar>" +
525                    "                       </org.picocontainer.script.xml.TestBean>" +
526                    "               </parameter>" +
527                    "  </component-implementation>" +
528                    "</container>");
529            PicoContainer pico = buildContainer(script);
530            assertNotNull(pico.getComponent(TestBeanComposer.class));
531            TestBeanComposer composer = pico.getComponent(TestBeanComposer.class);
532            assertEquals("bean1", "hello1", composer.getBean1().getBar());
533            assertEquals("bean2", "hello2", composer.getBean2().getBar());
534        }
535    
536        @Test public void testInstantiationOfComponentsWithParameterKeys() {
537            Reader script = new StringReader("" +
538                    "<container>" +
539                    "  <component-instance key='bean1'>" +
540                    "       <org.picocontainer.script.xml.TestBean>" +
541                    "               <foo>10</foo>" +
542                    "               <bar>hello1</bar>" +
543                    "       </org.picocontainer.script.xml.TestBean>" +
544                    "  </component-instance>" +
545                    "  <component-instance key='bean2'>" +
546                    "       <org.picocontainer.script.xml.TestBean>" +
547                    "               <foo>10</foo>" +
548                    "               <bar>hello2</bar>" +
549                    "       </org.picocontainer.script.xml.TestBean>" +
550                    "  </component-instance>" +
551                    "  <component-implementation class='org.picocontainer.script.xml.TestBeanComposer'>" +
552                    "               <parameter key='bean1'/>" +
553                    "               <parameter key='bean2'/>" +
554                    "  </component-implementation>" +
555                    "</container>");
556            PicoContainer pico = buildContainer(script);
557            assertNotNull(pico.getComponent(TestBeanComposer.class));
558            TestBeanComposer composer = pico.getComponent(TestBeanComposer.class);
559            assertEquals("bean1", "hello1", composer.getBean1().getBar());
560            assertEquals("bean2", "hello2", composer.getBean2().getBar());
561        }
562    
563        @Test public void testInstantiationOfComponentsWithComponentAdapter() {
564            Reader script = new StringReader("" +
565                    "<container>" +
566                    "  <component-instance key='bean1'>" +
567                    "       <org.picocontainer.script.xml.TestBean>" +
568                    "               <foo>10</foo>" +
569                    "               <bar>hello1</bar>" +
570                    "       </org.picocontainer.script.xml.TestBean>" +
571                    "  </component-instance>" +
572                    "  <component-instance key='bean2'>" +
573                    "       <org.picocontainer.script.xml.TestBean>" +
574                    "               <foo>10</foo>" +
575                    "               <bar>hello2</bar>" +
576                    "       </org.picocontainer.script.xml.TestBean>" +
577                    "  </component-instance>" +
578                    "  <component-adapter key='beanKey' class='org.picocontainer.script.xml.TestBeanComposer'>" +
579                    "               <parameter key='bean1'/>" +
580                    "               <parameter key='bean2'/>" +
581                    "  </component-adapter>" +
582                    "</container>");
583            PicoContainer pico = buildContainer(script);
584            assertNotNull(pico.getComponent("beanKey"));
585            TestBeanComposer composer = (TestBeanComposer) pico.getComponent("beanKey");
586            assertEquals("bean1", "hello1", composer.getBean1().getBar());
587            assertEquals("bean2", "hello2", composer.getBean2().getBar());
588        }
589    
590        @Test public void testComponentAdapterWithSpecifiedFactory() throws IOException {
591            Reader script = new StringReader("" +
592                    "<container>" +
593                    "  <component-instance key='bean1'>" +
594                    "       <org.picocontainer.script.xml.TestBean>" +
595                    "               <foo>10</foo>" +
596                    "               <bar>hello1</bar>" +
597                    "       </org.picocontainer.script.xml.TestBean>" +
598                    "  </component-instance>" +
599                    "  <component-instance key='bean2'>" +
600                    "       <org.picocontainer.script.xml.TestBean>" +
601                    "               <foo>10</foo>" +
602                    "               <bar>hello2</bar>" +
603                    "       </org.picocontainer.script.xml.TestBean>" +
604                    "  </component-instance>" +
605                    "  <component-adapter key='beanKey' class='org.picocontainer.script.xml.TestBeanComposer'" +
606                    "                                       factory='" + AdaptingInjection.class.getName() + "'>" +
607                    "               <parameter key='bean1'/>" +
608                    "               <parameter key='bean2'/>" +
609                    "  </component-adapter>" +
610                    "</container>");
611            MutablePicoContainer pico = (MutablePicoContainer)buildContainer(script);
612    
613    
614            assertNotNull(pico.getComponent("beanKey"));
615            TestBeanComposer composer = (TestBeanComposer) pico.getComponent("beanKey");
616            assertEquals("bean1", "hello1", composer.getBean1().getBar());
617            assertEquals("bean2", "hello2", composer.getBean2().getBar());
618        }
619    
620        @Test public void testComponentAdapterWithNoKeyUsesTypeAsKey() {
621            Reader script = new StringReader("" +
622                    "<container>" +
623                    "  <component-adapter class='org.picocontainer.script.xml.TestBeanComposer'/>" +
624                    "</container>");
625            PicoContainer pico = buildContainer(script);
626            ComponentAdapter<?> adapter = pico.getComponentAdapters().iterator().next();
627            assertSame(TestBeanComposer.class, adapter.getComponentImplementation());
628        }
629    
630        @Test public void testComponentAdapterWithNoClassThrowsPicoContainerMarkupException() {
631            Reader script = new StringReader("" +
632                    "<container>" +
633                    "  <component-adapter key='beanKey'/> " +
634                    "</container>");
635            try {
636                buildContainer(script);
637                fail();
638            } catch (ScriptedPicoContainerMarkupException expected) {
639                assertEquals("'class' attribute not specified for component-adapter", expected.getMessage());
640            }
641        }
642    
643        @Test public void testCachingCanBeUnsetAtContainerLevel() {
644            Reader script = new StringReader("" +
645                    "<container caching='false'>" +
646                    "  <component-implementation class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
647                    "</container>");
648    
649            PicoContainer pico = buildContainer(script);
650            Object wsc1 = pico.getComponent(WebServerConfig.class);
651            Object wsc2 = pico.getComponent(WebServerConfig.class);
652    
653            assertNotSame(wsc1, wsc2);
654        }
655    
656        @Test public void testCachingCanBeSetRedunadantlyAtContainerLevel() {
657            Reader script = new StringReader("" +
658                    "<container caching='true'>" +
659                    "  <component-implementation class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
660                    "</container>");
661    
662            PicoContainer pico = buildContainer(script);
663            Object wsc1 = pico.getComponent(WebServerConfig.class);
664            Object wsc2 = pico.getComponent(WebServerConfig.class);
665    
666            assertSame(wsc1, wsc2);
667        }
668    
669        @Test public void testCustomInjectionFactory() throws IOException {
670            Reader script = new StringReader("" +
671                    "<container component-adapter-factory='" + ConstructorInjection.class.getName() + "'>" +
672                    "</container>");
673    
674            MutablePicoContainer pico = (MutablePicoContainer) buildContainer(script);
675    
676            NullComponentMonitor cm = new NullComponentMonitor();
677            DefaultPicoContainer expected = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()),new StartableLifecycleStrategy(cm), new EmptyPicoContainer(), cm);
678            expected.start();
679    
680            XStream xs = new XStream();
681            assertEquals(xs.toXML(expected),xs.toXML(pico));
682    
683        }
684    
685    
686        @Test public void testComponentMonitorCanBeSpecified() {
687            Reader script = new StringReader("" +
688                    "<container component-monitor='" + StaticWriterComponentMonitor.class.getName() + "'>" +
689                    "  <component-implementation class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
690                    "</container>");
691    
692            PicoContainer pico = buildContainer(script);
693            pico.getComponent(WebServerConfig.class);
694            assertTrue(StaticWriterComponentMonitor.WRITER.toString().length() > 0);
695        }
696    
697        @Test public void testComponentMonitorCanBeSpecifiedIfCAFIsSpecified() {
698            Reader script = new StringReader("" +
699                    "<container component-adapter-factory='" + AdaptingInjection.class.getName() +
700                    "' component-monitor='" + StaticWriterComponentMonitor.class.getName() + "'>" +
701                    "  <component-implementation class='org.picocontainer.script.testmodel.DefaultWebServerConfig'/>" +
702                    "</container>");
703    
704            PicoContainer pico = buildContainer(script);
705            pico.getComponent(WebServerConfig.class);
706            assertTrue(StaticWriterComponentMonitor.WRITER.toString().length() > 0);
707        }
708    
709        @Test public void testAdaptersAlsoUseBehaviorFactory() {
710            Reader script = new StringReader("" +
711                    "<container>" +
712                    "  <component-adapter-factory class='org.picocontainer.injectors.ConstructorInjection' key='factory'/>" +
713                    "  <component-adapter class='org.picocontainer.script.testmodel.DefaultWebServerConfig' factory='factory'/>" +
714                    "</container>");
715            PicoContainer pico = buildContainer(script);
716            WebServerConfig cfg1 = pico.getComponent(WebServerConfig.class);
717            WebServerConfig cfg2 = pico.getComponent(WebServerConfig.class);
718            assertNotSame("Instances for components registered with a CICA must not be the same", cfg1, cfg2);
719        }
720    
721    
722        @SuppressWarnings("serial")
723        public static class MyComponentFactory extends ConstructorInjection {
724            public MyComponentFactory() {
725                super();
726            }
727        }
728        @SuppressWarnings("serial")
729        public static class MyComponentFactory2 extends AbstractBehaviorFactory {
730            public MyComponentFactory2(ComponentFactory delegate) {
731                wrap(delegate);
732            }
733        }
734        @SuppressWarnings("serial")
735        public static class MyComponentFactory3 extends AbstractBehaviorFactory {
736            public MyComponentFactory3(ComponentFactory delegate) {
737                wrap(delegate);
738            }
739        }
740    
741        public void BROKEN_testNestedCAFLooksRightinXml() {
742            Reader script = new StringReader("" +
743                    "<container>" +
744                    "  <component-adapter-factory class='"+ MyComponentFactory3.class.getName()+"' key='factory'>" +
745                    "    <component-adapter-factory class='"+ MyComponentFactory2.class.getName()+"'>" +
746                    "      <component-adapter-factory class='"+ MyComponentFactory.class.getName()+"'/>" +
747                    "    </component-adapter-factory>" +
748                    "  </component-adapter-factory>" +
749                    "</container>");
750            PicoContainer pico = buildContainer(script);
751    
752            String xml = new XStream().toXML(pico).replace('\"','\'');
753    
754            assertEquals("<org.picocontainer.classname.DefaultClassLoadingPicoContainer>\n" +
755                    "  <namedChildContainers/>\n" +
756                    "  <delegate class='org.picocontainer.DefaultPicoContainer'>\n" +
757                    "    <componentKeyToAdapterCache/>\n" +
758                    "    <componentFactory class='"+ MyComponentFactory3.class.getName()+"'>\n" +
759                    "      <delegate class='"+ MyComponentFactory2.class.getName()+"'>\n" +
760                    "        <delegate class='"+ MyComponentFactory.class.getName()+"'>\n" +
761                    "        </delegate>\n" +
762                    "      </delegate>\n" +
763                    "    </componentFactory>\n" +
764                    "    <children/>\n" +
765                    "    <componentAdapters/>\n" +
766                    "    <orderedComponentAdapters/>\n" +
767                    "    <started>true</started>\n" +
768                    "    <disposed>false</disposed>\n" +
769                    "    <childrenStarted/>\n" +
770                    "    <lifecycleStrategy class='org.picocontainer.lifecycle.StartableLifecycleStrategy'>\n" +
771                    "      <componentMonitor class='org.picocontainer.monitors.NullComponentMonitor'/>\n" +
772                    "    </lifecycleStrategy>\n" +
773                    "    <componentCharacteristic class='org.picocontainer.DefaultPicoContainer$1'>\n" +
774                    "      <outer-class reference='../..'/>\n" +
775                    "      <props/>\n" +
776                    "    </componentCharacteristic>\n" +
777                    "    <componentMonitor class='org.picocontainer.monitors.NullComponentMonitor' reference='../lifecycleStrategy/componentMonitor'/>\n" +
778                    "  </delegate>\n" +
779                    "</org.picocontainer.classname.DefaultClassLoadingPicoContainer>", xml);
780    
781            // This test suggests that testComponentCanUsePredefinedNestedCAF() is not testing what it hopes to test
782        }
783    
784    
785        public void BROKEN_testComponentCanUsePredefinedNestedCAF() {
786            Reader script = new StringReader("" +
787                    "<container>" +
788                    "  <component-adapter-factory class='org.picocontainer.behaviors.ImplementationHiding' key='factory'>" +
789                    "    <component-adapter-factory class='"+ MyComponentFactory.class.getName()+"'/>" +
790                    "  </component-adapter-factory>" +
791                    "  <component-adapter class-name-key='org.picocontainer.script.testmodel.WebServerConfig' class='org.picocontainer.script.testmodel.DefaultWebServerConfig' factory='factory'/>" +
792                    "</container>");
793            PicoContainer pico = buildContainer(script);
794            WebServerConfig cfg1 = pico.getComponent(WebServerConfig.class);
795            WebServerConfig cfg2 = pico.getComponent(WebServerConfig.class);
796            assertNotSame("Instances for components registered with a CICA must not be the same", cfg1, cfg2);
797            assertFalse("Instance exposes only interface", cfg1 instanceof DefaultWebServerConfig);
798        }
799    
800        @Test public void testChainOfWrappedComponents() {
801    
802           Reader script = new StringReader("" +
803                    "<container>\n" +
804                   "   <component-implementation key='wrapped' class='"+SimpleTouchable.class.getName()+"'/>" +
805                   "   <component-implementation class-name-key=\'"+Touchable.class.getName()+"\' class='"+WrapsTouchable.class.getName()+"'/>" +
806                    "</container>");
807    
808            PicoContainer pico = buildContainer(script);
809    
810            // decorators are fairly dirty - they replace a very select implementation in this TestCase.
811            assertNotNull(pico.getComponent(Touchable.class));
812        }
813        
814        @Test public void testListSupport() {
815    
816            Reader script = new StringReader("" +
817                     "<container>\n" +
818                    "   <component-implementation class='"+ListSupport.class.getName()+"'>" +
819                    "       <parameter empty-collection='false' component-value-type='"+Entity.class.getName()+"'/>" +
820                    "   </component-implementation>" +               
821                    "   <component-implementation class=\'"+CustomerEntityImpl.class.getName()+"\'/>" +
822                    "   <component-implementation class=\'"+OrderEntityImpl.class.getName()+"\'/>" +
823                     "</container>");
824    
825             PicoContainer pico = buildContainer(script);
826             
827             ListSupport listSupport = pico.getComponent(ListSupport.class);
828    
829             assertNotNull(listSupport);
830             assertNotNull(listSupport.getAListOfEntityObjects());
831             assertEquals(2, listSupport.getAListOfEntityObjects().size());
832    
833             Entity entity1 = listSupport.getAListOfEntityObjects().get(0);
834             Entity entity2 = listSupport.getAListOfEntityObjects().get(1);
835             
836             assertNotNull(entity1);
837             assertEquals(CustomerEntityImpl.class, entity1.getClass());
838             
839             assertNotNull(entity2);
840             assertEquals(OrderEntityImpl.class, entity2.getClass());
841         }
842        
843        @Test public void testMapSupport() {
844            
845            Reader script = new StringReader("" +
846                    "<container>\n" +
847                   "   <component-implementation class='"+ MapSupport.class.getName()+ "'>" +
848                   "       <parameter empty-collection='false' component-value-type='"+Entity.class.getName()+"'/>" +
849                   "   </component-implementation>" +               
850                   "   <component-implementation key='customer' class=\'"+CustomerEntityImpl.class.getName()+"\'/>" +
851                   "   <component-implementation key='order' class=\'"+OrderEntityImpl.class.getName()+"\'/>" +
852                    "</container>");
853            
854            PicoContainer pico = buildContainer(script);
855            
856            MapSupport mapSupport = pico.getComponent(MapSupport.class);
857    
858            assertNotNull(mapSupport);
859            assertNotNull(mapSupport.getAMapOfEntities());
860            assertEquals(2, mapSupport.getAMapOfEntities().size());
861    
862            Map<String, Entity> aMapOfEntities = mapSupport.getAMapOfEntities();
863            
864            Entity entity1 = aMapOfEntities.get("customer");
865            Entity entity2 = aMapOfEntities.get("order");
866            
867            assertNotNull(entity1);
868            assertEquals(CustomerEntityImpl.class, entity1.getClass());
869            
870            assertNotNull(entity2);
871            assertEquals(OrderEntityImpl.class, entity2.getClass()); 
872        }
873        
874        @Test public void testNoEmptyCollectionWithComponentKeyTypeFailure() {
875    
876            Reader script = new StringReader("" +
877                     "<container>\n" +
878                    "   <component-implementation class='"+ MapSupport.class.getName()+ "'>" +
879                    "       <parameter empty-collection='false' component-key-type='"+Entity.class.getName()+"'/>" +
880                    "   </component-implementation>" +               
881                    "   <component-implementation key='customer' class=\'"+CustomerEntityImpl.class.getName()+"\'/>" +
882                    "   <component-implementation key='order' class=\'"+OrderEntityImpl.class.getName()+"\'/>" +
883                     "</container>");
884    
885            try {
886                buildContainer(script);
887                fail("Thrown " + PicoException.class.getName() + " expected");
888            } catch (final PicoException e) {
889                assertTrue(e.getMessage().indexOf("one or both of the emptyCollection")>0);
890            }
891         }
892        
893        @Test public void testNoComponentValueTypeWithComponentKeyTypeFailure() {
894    
895            Reader script = new StringReader("" +
896                     "<container>\n" +
897                    "   <component-implementation class='"+ MapSupport.class.getName()+ "'>" +
898                    "       <parameter component-value-type='"+Entity.class.getName()+"' component-key-type='"+Entity.class.getName()+"'/>" +
899                    "   </component-implementation>" +               
900                    "   <component-implementation key='customer' class=\'"+CustomerEntityImpl.class.getName()+"\'/>" +
901                    "   <component-implementation key='order' class=\'"+OrderEntityImpl.class.getName()+"\'/>" +
902                     "</container>");
903    
904            try {
905                buildContainer(script);
906                fail("Thrown " + PicoException.class.getName() + " expected");
907            } catch (final PicoException e) {
908                assertTrue(e.getMessage().indexOf("but one or both of the emptyCollection")>0);
909            }
910         }   
911        
912        @Test public void testNoEmptyCollectionWithComponentValueTypeFailure() {
913    
914            Reader script = new StringReader("" +
915                     "<container>\n" +
916                    "   <component-implementation class='"+ MapSupport.class.getName()+ "'>" +
917                    "       <parameter component-value-type='"+Entity.class.getName()+"'/>" +
918                    "   </component-implementation>" +               
919                    "   <component-implementation key='customer' class=\'"+CustomerEntityImpl.class.getName()+"\'/>" +
920                    "   <component-implementation key='order' class=\'"+OrderEntityImpl.class.getName()+"\'/>" +
921                     "</container>");
922    
923            try {
924                buildContainer(script);
925                fail("Thrown " + PicoException.class.getName() + " expected");
926            } catch (final PicoException e) {
927                System.out.println(e);
928                
929                assertTrue(e.getMessage().indexOf("but the emptyCollection () was empty or null")>0);
930            }
931         }
932    
933        private PicoContainer buildContainer(Reader script) {
934            return buildContainer(new XMLContainerBuilder(script, getClass().getClassLoader()), null, "SOME_SCOPE");
935        }
936    
937        static public final class StaticWriterComponentMonitor extends WriterComponentMonitor {
938            static final Writer WRITER = new StringWriter();
939    
940            public StaticWriterComponentMonitor() {
941                super(WRITER);
942            }
943    
944        }
945    
946        // TODO: Move this into pico-tck 
947        public static class WrapsTouchable implements Touchable {
948            private final Touchable wrapped;
949            
950            public WrapsTouchable(final Touchable wrapped) {
951                this.wrapped = wrapped;
952            }
953    
954            public void touch() {
955                this.wrapped.touch();
956            }
957        }
958    }
959