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 *
009 *****************************************************************************/
010 package org.picocontainer.defaults;
011
012 import static org.junit.Assert.assertEquals;
013 import static org.junit.Assert.assertNotNull;
014 import static org.junit.Assert.assertNull;
015
016 import org.junit.Test;
017 import org.picocontainer.DefaultPicoContainer;
018 import org.picocontainer.PicoContainer;
019 import org.picocontainer.Parameter;
020 import org.picocontainer.ComponentAdapter;
021 import org.picocontainer.NameBinding;
022 import org.picocontainer.PicoCompositionException;
023 import org.picocontainer.adapters.InstanceAdapter;
024 import org.picocontainer.parameters.ComponentParameter;
025 import org.picocontainer.injectors.ConstructorInjection;
026 import org.picocontainer.injectors.ConstructorInjector;
027
028 import java.lang.reflect.Type;
029 import java.lang.annotation.Annotation;
030
031 /**
032 * @author Paul Hammant
033 */
034 public class ResolveAdapterReductionTestCase {
035
036 int resolveAdapterCalls;
037 private Parameter[] parms;
038 private ComponentAdapter[] injecteeAdapters;
039
040 @Test
041 public void testThatResolveAdapterCanBeDoneOnceForASituationWhereItWasPreviouslyDoneAtLeastTwice() throws Exception {
042 resolveAdapterCalls = 0;
043 DefaultPicoContainer pico = new DefaultPicoContainer(new ConstructorInjection());
044 pico.addAdapter(new CountingConstructorInjector(One.class, One.class));
045 pico.addComponent(new Two());
046 long start = System.currentTimeMillis();
047 for (int x = 0; x<30000; x++) {
048 One one = pico.getComponent(One.class);
049 assertNotNull(one);
050 assertNotNull(one.two);
051 assertEquals("resolveAdapter for 'Two' should only be called once, regardless of how many getComponents there are",
052 1, resolveAdapterCalls);
053 }
054 System.out.println("GreediestConstructorTestCase elapsed: " + (System.currentTimeMillis() - start));
055 assertNotNull(parms);
056 assertEquals(1, parms.length);
057 assertEquals(true, parms[0] instanceof CountingComponentParameter);
058 assertNotNull(injecteeAdapters);
059 assertEquals(1, injecteeAdapters.length);
060 assertEquals(true, injecteeAdapters[0] instanceof InstanceAdapter);
061 }
062
063 @Test
064 public void testThatResolveAdapterCallsAreNotDuplicatedForMultipleConstructorsInTheSameComponent() throws Exception {
065 resolveAdapterCalls = 0;
066 DefaultPicoContainer pico = new DefaultPicoContainer(new ConstructorInjection());
067 // 'Three', in addition to a 'Two', requires a string, and an int for two of the longer constructors ....
068 pico.addAdapter(new CountingConstructorInjector(Three.class, Three.class));
069 // .. but we ain't going to provide them, forcing the smallest constructor to be used.
070 pico.addComponent(new Two());
071 long start = System.currentTimeMillis();
072 for (int x = 0; x<30000; x++) {
073 Three three = pico.getComponent(Three.class);
074 assertNotNull(three);
075 assertNotNull(three.two);
076 assertNull(three.string);
077 assertNull(three.integer);
078
079 // if we did not cache the results of the longer (unsatisfiable) constructors, then we'd be doing
080 // resolveAdapter(..) more than once. See ConstructorInjector.ResolverKey.
081 assertEquals("resolveAdapter for 'Two' should only be called once, regardless of how many getComponents there are",
082 1, resolveAdapterCalls);
083 }
084 System.out.println("GreediestConstructorTestCase elapsed: " + (System.currentTimeMillis() - start));
085 }
086
087 public static class One {
088 private final Two two;
089
090 public One(Two two) {
091 this.two = two;
092 }
093 }
094
095 public static class Two {
096 public Two() {
097 }
098 }
099
100 public static class Three {
101 private final Two two;
102 private final String string;
103 private final Integer integer;
104
105 public Three(Two two, String string, Integer integer) {
106 this.two = two;
107 this.string = string;
108 this.integer = integer;
109 }
110 public Three(Two two, String string) {
111 this.two = two;
112 this.string = string;
113 integer = null;
114 }
115 public Three(Two two) {
116 this.two = two;
117 string = null;
118 integer = null;
119 }
120 }
121
122 private class CountingConstructorInjector extends ConstructorInjector {
123
124 public CountingConstructorInjector(Class<?> componentKey, Class<?> componentImplementation) {
125 super(componentKey, componentImplementation, null);
126 }
127
128 protected CtorAndAdapters getGreediestSatisfiableConstructor(PicoContainer container) throws PicoCompositionException {
129 CtorAndAdapters adapters = super.getGreediestSatisfiableConstructor(container);
130 parms = adapters.getParameters();
131 injecteeAdapters = adapters.getInjecteeAdapters();
132 return adapters;
133 }
134
135 protected Parameter[] createDefaultParameters(Type[] parameters) {
136 Parameter[] componentParameters = new Parameter[parameters.length];
137 for (int i = 0; i < parameters.length; i++) {
138 componentParameters[i] = new CountingComponentParameter();
139
140 }
141 return componentParameters;
142 }
143
144 }
145 private class CountingComponentParameter extends ComponentParameter {
146 public int hashCode() {
147 return ResolveAdapterReductionTestCase.super.hashCode();
148 }
149
150 public boolean equals(Object o) {
151 return true;
152 }
153
154 protected <T> ComponentAdapter<T> resolveAdapter(PicoContainer container, ComponentAdapter adapter, Class<T> expectedType, NameBinding expectedNameBinding, boolean useNames, Annotation binding) {
155 if (expectedType == Two.class) {
156 resolveAdapterCalls++;
157 }
158 return super.resolveAdapter(container, adapter, expectedType, expectedNameBinding, useNames, binding); //To change body of overridden methods use File | Settings | File Templates.
159 }
160 }
161 }