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