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 * Idea by Rachel Davies, Original code by Aslak Hellesoy and Paul Hammant *
009 *****************************************************************************/
010 package org.picocontainer.defaults;
011
012 import org.picocontainer.ComponentAdapter;
013 import org.picocontainer.ComponentMonitor;
014 import org.picocontainer.MutablePicoContainer;
015 import org.picocontainer.Parameter;
016 import org.picocontainer.PicoContainer;
017 import org.picocontainer.PicoInitializationException;
018 import org.picocontainer.PicoIntrospectionException;
019 import org.picocontainer.PicoVisitor;
020 import org.picocontainer.Startable;
021 import org.picocontainer.alternatives.EmptyPicoContainer;
022 import org.picocontainer.monitors.DefaultComponentMonitor;
023 import org.picocontainer.monitors.WriterComponentMonitor;
024 import org.picocontainer.tck.AbstractPicoContainerTestCase;
025 import org.picocontainer.testmodel.DecoratedTouchable;
026 import org.picocontainer.testmodel.DependsOnTouchable;
027 import org.picocontainer.testmodel.SimpleTouchable;
028 import org.picocontainer.testmodel.Touchable;
029
030 import java.io.Serializable;
031 import java.io.StringWriter;
032 import java.lang.reflect.Method;
033 import java.util.ArrayList;
034 import java.util.Collection;
035 import java.util.HashMap;
036 import java.util.LinkedList;
037 import java.util.List;
038
039 /**
040 * @author Aslak Hellesøy
041 * @author Paul Hammant
042 * @author Ward Cunningham
043 * @author Mauro Talevi
044 * @version $Revision: 3116 $
045 */
046 public class DefaultPicoContainerTestCase extends AbstractPicoContainerTestCase {
047 protected MutablePicoContainer createPicoContainer(PicoContainer parent) {
048 return new DefaultPicoContainer(parent);
049 }
050
051 public void testInstantiationWithNullComponentAdapterFactory(){
052 try {
053 new DefaultPicoContainer((ComponentAdapterFactory)null, (PicoContainer)null);
054 fail("NPE expected");
055 } catch (NullPointerException e) {
056 // expected
057 }
058 }
059 public void testUpDownDependenciesCannotBeFollowed() {
060 MutablePicoContainer parent = createPicoContainer(null);
061 MutablePicoContainer child = createPicoContainer(parent);
062
063 // ComponentF -> ComponentA -> ComponentB+C
064 child.registerComponentImplementation(ComponentF.class);
065 parent.registerComponentImplementation(ComponentA.class);
066 child.registerComponentImplementation(ComponentB.class);
067 child.registerComponentImplementation(ComponentC.class);
068
069 try {
070 child.getComponentInstance(ComponentF.class);
071 fail("Thrown " + UnsatisfiableDependenciesException.class.getName() + " expected");
072 } catch (final UnsatisfiableDependenciesException e) {
073 assertEquals(ComponentB.class, e.getUnsatisfiedDependencyType());
074 }
075 }
076
077 public void testComponentsCanBeRemovedByInstance() {
078 MutablePicoContainer pico = createPicoContainer(null);
079 pico.registerComponentImplementation(HashMap.class);
080 pico.registerComponentImplementation(ArrayList.class);
081 List list = (List) pico.getComponentInstanceOfType(List.class);
082 pico.unregisterComponentByInstance(list);
083 assertEquals(1, pico.getComponentAdapters().size());
084 assertEquals(1, pico.getComponentInstances().size());
085 assertEquals(HashMap.class, pico.getComponentInstanceOfType(Serializable.class).getClass());
086 }
087
088 public void testComponentInstancesListIsReturnedForNullType(){
089 MutablePicoContainer pico = createPicoContainer(null);
090 List componentInstances = pico.getComponentInstancesOfType(null);
091 assertNotNull(componentInstances);
092 assertEquals(0, componentInstances.size());
093 }
094
095 public void testComponentsWithCommonSupertypeWhichIsAConstructorArgumentCanBeLookedUpByConcreteType() {
096 MutablePicoContainer pico = createPicoContainer(null);
097 pico.registerComponentImplementation(LinkedList.class, LinkedList.class, new Parameter[0]);
098 pico.registerComponentImplementation(ArrayList.class);
099 assertEquals(ArrayList.class, pico.getComponentInstanceOfType(ArrayList.class).getClass());
100 }
101
102 /*
103 When pico tries to resolve DecoratedTouchable it find as dependency itself and SimpleTouchable.
104 Problem is basically the same as above. Pico should not consider self as solution.
105
106 JS
107 fixed it ( PICO-222 )
108 KP
109 */
110 public void testUnambiguouSelfDependency() {
111 MutablePicoContainer pico = createPicoContainer(null);
112 pico.registerComponentImplementation(SimpleTouchable.class);
113 pico.registerComponentImplementation(DecoratedTouchable.class);
114 Touchable t = (Touchable) pico.getComponentInstance(DecoratedTouchable.class);
115 assertNotNull(t);
116 }
117
118 public static class Thingie {
119 public Thingie(List c) {
120 assertNotNull(c);
121 }
122 }
123
124 public void testThangCanBeInstantiatedWithArrayList() {
125 MutablePicoContainer pico = new DefaultPicoContainer();
126 pico.registerComponentImplementation(Thingie.class);
127 pico.registerComponentImplementation(ArrayList.class);
128 assertNotNull(pico.getComponentInstance(Thingie.class));
129 }
130
131 public void testGetComponentAdaptersOfTypeNullReturnsEmptyList() {
132 DefaultPicoContainer pico = new DefaultPicoContainer();
133 List adapters = pico.getComponentAdaptersOfType(null);
134 assertNotNull(adapters);
135 assertEquals(0, adapters.size());
136 }
137
138
139 public static class Service {
140 }
141
142 public static class TransientComponent {
143 private Service service;
144
145 public TransientComponent(Service service) {
146 this.service = service;
147 }
148 }
149
150 public void testDefaultPicoContainerReturnsNewInstanceForEachCallWhenUsingTransientComponentAdapter() {
151 DefaultPicoContainer picoContainer = new DefaultPicoContainer();
152 picoContainer.registerComponentImplementation(Service.class);
153 picoContainer.registerComponent(new ConstructorInjectionComponentAdapter(TransientComponent.class, TransientComponent.class));
154 TransientComponent c1 = (TransientComponent) picoContainer.getComponentInstance(TransientComponent.class);
155 TransientComponent c2 = (TransientComponent) picoContainer.getComponentInstance(TransientComponent.class);
156 assertNotSame(c1, c2);
157 assertSame(c1.service, c2.service);
158 }
159
160 public static class DependsOnCollection {
161 public DependsOnCollection(Collection c) {
162 }
163 }
164
165 public void testShouldProvideInfoAboutDependingWhenAmbiguityHappens() {
166 MutablePicoContainer pico = this.createPicoContainer(null);
167 pico.registerComponentInstance(new ArrayList());
168 pico.registerComponentInstance(new LinkedList());
169 pico.registerComponentImplementation(DependsOnCollection.class);
170 try {
171 pico.getComponentInstanceOfType(DependsOnCollection.class);
172 fail();
173 } catch (AmbiguousComponentResolutionException expected) {
174 String doc = DependsOnCollection.class.getName();
175 assertEquals("class " + doc + " has ambiguous dependency on interface java.util.Collection, resolves to multiple classes: [class java.util.ArrayList, class java.util.LinkedList]", expected.getMessage());
176 }
177 }
178
179 public void testInstantiationWithMonitorAndParent() {
180 StringWriter writer = new StringWriter();
181 ComponentMonitor monitor = new WriterComponentMonitor(writer);
182 DefaultPicoContainer parent = new DefaultPicoContainer();
183 DefaultPicoContainer child = new DefaultPicoContainer(monitor, parent);
184 parent.registerComponentImplementation("st", SimpleTouchable.class);
185 child.registerComponentImplementation("dot", DependsOnTouchable.class);
186 DependsOnTouchable dot = (DependsOnTouchable) child.getComponentInstance("dot");
187 assertNotNull(dot);
188 assertTrue("writer not empty", writer.toString().length() > 0);
189 }
190
191 public void testStartCapturedByMonitor() {
192 final StringBuffer sb = new StringBuffer();
193 DefaultPicoContainer dpc = new DefaultPicoContainer(new DefaultComponentMonitor() {
194 public void invoking(Method method, Object instance) {
195 sb.append(method.toString());
196 }
197 });
198 dpc.registerComponentImplementation(DefaultPicoContainer.class);
199 dpc.start();
200 assertEquals("ComponentMonitor should have been notified that the component had been started",
201 "public abstract void org.picocontainer.Startable.start()", sb.toString());
202 }
203
204 public void testCanChangeMonitor() {
205 StringWriter writer1 = new StringWriter();
206 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1);
207 DefaultPicoContainer pico = new DefaultPicoContainer(monitor1);
208 pico.registerComponentImplementation("t1", SimpleTouchable.class);
209 pico.registerComponentImplementation("t3", SimpleTouchable.class);
210 Touchable t1 = (Touchable) pico.getComponentInstance("t1");
211 assertNotNull(t1);
212 assertTrue("writer not empty", writer1.toString().length() > 0);
213 StringWriter writer2 = new StringWriter();
214 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2);
215 pico.changeMonitor(monitor2);
216 pico.registerComponentImplementation("t2", SimpleTouchable.class);
217 Touchable t2 = (Touchable) pico.getComponentInstance("t2");
218 assertNotNull(t2);
219 assertTrue("writer not empty", writer2.toString().length() > 0);
220 assertTrue("writers of same length", writer1.toString().length() == writer2.toString().length());
221 Touchable t3 = (Touchable) pico.getComponentInstance("t3");
222 assertNotNull(t3);
223 assertTrue("old writer was used", writer1.toString().length() < writer2.toString().length());
224 }
225
226 public void testCanChangeMonitorOfChildContainers() {
227 StringWriter writer1 = new StringWriter();
228 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1);
229 DefaultPicoContainer parent = new DefaultPicoContainer();
230 DefaultPicoContainer child = new DefaultPicoContainer(monitor1);
231 parent.addChildContainer(child);
232 child.registerComponentImplementation("t1", SimpleTouchable.class);
233 child.registerComponentImplementation("t3", SimpleTouchable.class);
234 Touchable t1 = (Touchable) child.getComponentInstance("t1");
235 assertNotNull(t1);
236 assertTrue("writer not empty", writer1.toString().length() > 0);
237 StringWriter writer2 = new StringWriter();
238 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2);
239 parent.changeMonitor(monitor2);
240 child.registerComponentImplementation("t2", SimpleTouchable.class);
241 Touchable t2 = (Touchable) child.getComponentInstance("t2");
242 assertNotNull(t2);
243 assertTrue("writer not empty", writer2.toString().length() > 0);
244 String s1 = writer1.toString();
245 String s2 = writer2.toString();
246 assertTrue("writers of same length", s1.length() == s2.length());
247 Touchable t3 = (Touchable) child.getComponentInstance("t3");
248 assertNotNull(t3);
249 assertTrue("old writer was used", writer1.toString().length() < writer2.toString().length());
250 }
251
252 public void testChangeMonitorIsIgnoredIfNotSupportingStrategy(){
253 StringWriter writer = new StringWriter();
254 ComponentMonitor monitor = new WriterComponentMonitor(writer);
255 DefaultPicoContainer parent = new DefaultPicoContainer(new ComponentAdapterFactoryWithNoMonitor(new ComponentAdapterWithNoMonitor(new SimpleTouchable())));
256 parent.addChildContainer(new EmptyPicoContainer());
257 parent.registerComponentImplementation("t1", SimpleTouchable.class);
258 parent.changeMonitor(monitor);
259 assertTrue("writer empty", writer.toString().length() == 0);
260 }
261
262 public void testCanReturnCurrentMonitorFromComponentAdapterFactory() {
263 StringWriter writer1 = new StringWriter();
264 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1);
265 DefaultPicoContainer pico = new DefaultPicoContainer(monitor1);
266 assertEquals(monitor1, pico.currentMonitor());
267 StringWriter writer2 = new StringWriter();
268 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2);
269 pico.changeMonitor(monitor2);
270 assertEquals(monitor2, pico.currentMonitor());
271 }
272
273 public void testCanReturnCurrentMonitorFromComponentAdapter() {
274 StringWriter writer1 = new StringWriter();
275 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1);
276 InstanceComponentAdapter adapterWithMonitor = new InstanceComponentAdapter(SimpleTouchable.class.getName(), new SimpleTouchable());
277 adapterWithMonitor.changeMonitor(monitor1);
278 DefaultPicoContainer pico = new DefaultPicoContainer(new ComponentAdapterFactoryWithNoMonitor(adapterWithMonitor));
279 pico.registerComponentImplementation("t1", SimpleTouchable.class);
280 assertEquals(monitor1, pico.currentMonitor());
281 StringWriter writer2 = new StringWriter();
282 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2);
283 pico.changeMonitor(monitor2);
284 assertEquals(monitor2, pico.currentMonitor());
285 }
286
287 public void testCanReturnCurrentMonitorFromChildContainer() {
288 StringWriter writer1 = new StringWriter();
289 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1);
290 DefaultPicoContainer pico = new DefaultPicoContainer(new ComponentAdapterFactoryWithNoMonitor(new ComponentAdapterWithNoMonitor(new SimpleTouchable())));
291 pico.registerComponentImplementation("t1", SimpleTouchable.class);
292 // first child does not support ComponentMonitorStrategy
293 pico.addChildContainer(new EmptyPicoContainer());
294 // second child does support ComponentMonitorStrategy
295 pico.addChildContainer(new DefaultPicoContainer(monitor1));
296 assertEquals(monitor1, pico.currentMonitor());
297 StringWriter writer2 = new StringWriter();
298 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2);
299 pico.changeMonitor(monitor2);
300 assertEquals(monitor2, pico.currentMonitor());
301 }
302
303 public void testCannotReturnCurrentMonitor() {
304 DefaultPicoContainer pico = new DefaultPicoContainer(new ComponentAdapterFactoryWithNoMonitor(null));
305 try {
306 pico.currentMonitor();
307 fail("PicoIntrospectionException expected");
308 } catch (PicoIntrospectionException e) {
309 assertEquals("No component monitor found in container or its children", e.getMessage());
310 }
311 }
312
313 private static class ComponentAdapterFactoryWithNoMonitor implements ComponentAdapterFactory {
314 private ComponentAdapter adapter;
315 public ComponentAdapterFactoryWithNoMonitor(ComponentAdapter adapter){
316 this.adapter = adapter;
317 }
318 public ComponentAdapter createComponentAdapter(Object componentKey, Class componentImplementation, Parameter[] parameters) throws PicoIntrospectionException, AssignabilityRegistrationException, NotConcreteRegistrationException {
319 return adapter;
320 }
321 }
322
323 private static class ComponentAdapterWithNoMonitor implements ComponentAdapter {
324 private Object instance;
325 public ComponentAdapterWithNoMonitor(Object instance){
326 this.instance = instance;
327 }
328 public Object getComponentKey() {
329 return instance.getClass();
330 }
331 public Class getComponentImplementation() {
332 return instance.getClass();
333 }
334 public Object getComponentInstance(PicoContainer container) throws PicoInitializationException, PicoIntrospectionException {
335 return instance;
336 }
337 public void verify(PicoContainer container) throws PicoIntrospectionException {
338 }
339 public void accept(PicoVisitor visitor) {
340 }
341 }
342
343 public void testMakeChildContainer() {
344 MutablePicoContainer parent = new DefaultPicoContainer();
345 parent.registerComponentImplementation("t1", SimpleTouchable.class);
346 MutablePicoContainer child = parent.makeChildContainer();
347 Object t1 = child.getParent().getComponentInstance("t1");
348 assertNotNull(t1);
349 assertTrue(t1 instanceof SimpleTouchable);
350 }
351
352 public void testCanUseCustomLifecycleStrategyForClassRegistrations() {
353 DefaultPicoContainer dpc = new DefaultPicoContainer(new FailingLifecycleStrategy(), null);
354 dpc.registerComponentImplementation(Startable.class, MyStartable.class);
355 try {
356 dpc.start();
357 fail("should have barfed");
358 } catch (RuntimeException e) {
359 assertEquals("foo", e.getMessage());
360 }
361 }
362
363 public void testCanUseCustomLifecycleStrategyForInstanceRegistrations() {
364 DefaultPicoContainer dpc = new DefaultPicoContainer(new FailingLifecycleStrategy(), null);
365 Startable myStartable = new MyStartable();
366 dpc.registerComponentInstance(Startable.class, myStartable);
367 try {
368 dpc.start();
369 fail("should have barfed");
370 } catch (RuntimeException e) {
371 assertEquals("foo", e.getMessage());
372 }
373 }
374
375 public static class FailingLifecycleStrategy implements LifecycleStrategy {
376 public void start(Object component) {
377 throw new RuntimeException("foo");
378 }
379
380 public void stop(Object component) {
381 }
382
383 public void dispose(Object component) {
384 }
385
386 public boolean hasLifecycle(Class type) {
387 return true;
388 }
389
390 }
391 public static class MyStartable implements Startable {
392 public MyStartable() {
393 }
394
395 public void start() {
396 }
397
398 public void stop() {
399 }
400 }
401
402 public static interface A {
403
404 }
405
406 public static class SimpleA implements A
407 {
408
409 }
410
411 public static class WrappingA implements A
412 {
413 private final A wrapped;
414
415 public WrappingA(A wrapped) {
416 this.wrapped = wrapped;
417 }
418 }
419
420 public void testCanRegisterTwoComponentsImplementingSameInterfaceOneWithInterfaceAsKey() throws Exception {
421 MutablePicoContainer container = createPicoContainer(null);
422
423 container.registerComponentImplementation(SimpleA.class);
424 container.registerComponentImplementation(A.class, WrappingA.class);
425
426 container.start();
427
428 assertEquals(WrappingA.class, container.getComponentInstance(A.class).getClass());
429 }
430
431 public void testCanRegisterTwoComponentsWithSameImplementionAndDifferentKey() throws Exception {
432 MutablePicoContainer container = createPicoContainer(null);
433
434 container.registerComponentImplementation(SimpleA.class);
435 container.registerComponentImplementation("A", SimpleA.class);
436
437 container.start();
438
439 assertNotNull(container.getComponentInstance("A"));
440 assertNotNull(container.getComponentInstance(SimpleA.class));
441 assertNotSame(container.getComponentInstance("A"), container.getComponentInstance(SimpleA.class));
442 }
443
444 public static class MyPicoContainer extends DefaultPicoContainer {
445
446 public ComponentAdapter registerComponent(ComponentAdapter componentAdapter) {
447 return super.registerComponent(new SynchronizedComponentAdapter(componentAdapter));
448 }
449
450 }
451
452 public void testDerivedPicoContainerCanOverloadRegisterComponentForAllCreatedComponentAdapters() {
453 MutablePicoContainer mpc = new MyPicoContainer();
454 assertEquals(SynchronizedComponentAdapter.class, mpc.registerComponent(new InstanceComponentAdapter("foo", "bar")).getClass());
455 assertEquals(SynchronizedComponentAdapter.class, mpc.registerComponentInstance("foobar").getClass());
456 assertEquals(SynchronizedComponentAdapter.class, mpc.registerComponentImplementation(SimpleA.class).getClass());
457 }
458 }