001 package org.picocontainer.injectors;
002
003 import org.picocontainer.Parameter;
004 import org.picocontainer.ComponentMonitor;
005 import org.picocontainer.LifecycleStrategy;
006 import org.picocontainer.PicoCompositionException;
007 import org.picocontainer.PicoContainer;
008 import org.picocontainer.ParameterName;
009
010 import java.lang.reflect.Member;
011 import java.lang.reflect.Constructor;
012 import java.lang.reflect.InvocationTargetException;
013 import java.lang.reflect.Method;
014 import java.util.List;
015 import java.util.ArrayList;
016 import java.util.Collections;
017 import java.util.Set;
018 import java.util.HashSet;
019 import java.security.AccessController;
020 import java.security.PrivilegedAction;
021
022 public abstract class IterativeInjector extends AbstractInjector {
023 private transient ThreadLocalCyclicDependencyGuard instantiationGuard;
024 protected transient List<Member> injectionMembers;
025 protected transient Class[] injectionTypes;
026
027 /**
028 * Constructs a IterativeInjector
029 *
030 * @param componentKey the search key for this implementation
031 * @param componentImplementation the concrete implementation
032 * @param parameters the parameters to use for the initialization
033 * @param monitor the component monitor used by this addAdapter
034 * @param lifecycleStrategy the component lifecycle strategy used by this addAdapter
035 * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException
036 * if the implementation is not a concrete class.
037 * @throws NullPointerException if one of the parameters is <code>null</code>
038 */
039 public IterativeInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, LifecycleStrategy lifecycleStrategy) throws NotConcreteRegistrationException {
040 super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy);
041 }
042
043
044 protected Constructor getConstructor() {
045 Object retVal = AccessController.doPrivileged(new PrivilegedAction() {
046 public Object run() {
047 try {
048 return getComponentImplementation().getConstructor((Class[])null);
049 } catch (NoSuchMethodException e) {
050 return new PicoCompositionException(e);
051 } catch (SecurityException e) {
052 return new PicoCompositionException(e);
053 }
054 }
055 });
056 if (retVal instanceof Constructor) {
057 return (Constructor) retVal;
058 } else {
059 throw (PicoCompositionException) retVal;
060 }
061 }
062
063 private Parameter[] getMatchingParameterListForSetters(PicoContainer container) throws PicoCompositionException {
064 if (injectionMembers == null) {
065 initializeInjectionMembersAndTypeLists();
066 }
067
068 final List<Object> matchingParameterList = new ArrayList<Object>(Collections.nCopies(injectionMembers.size(), null));
069 final Set<Integer> nonMatchingParameterPositions = new HashSet<Integer>();
070 final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(injectionTypes);
071 for (int i = 0; i < currentParameters.length; i++) {
072 final Parameter parameter = currentParameters[i];
073 boolean failedDependency = true;
074 for (int j = 0; j < injectionTypes.length; j++) {
075 if (matchingParameterList.get(j) == null && parameter.isResolvable(container, this, injectionTypes[j],
076 new IterativeInjectorParameterName())) {
077 matchingParameterList.set(j, parameter);
078 failedDependency = false;
079 break;
080 }
081 }
082 if (failedDependency) {
083 nonMatchingParameterPositions.add(i);
084 }
085 }
086
087 final Set<Class> unsatisfiableDependencyTypes = new HashSet<Class>();
088 for (int i = 0; i < matchingParameterList.size(); i++) {
089 if (matchingParameterList.get(i) == null) {
090 unsatisfiableDependencyTypes.add(injectionTypes[i]);
091 }
092 }
093 if (unsatisfiableDependencyTypes.size() > 0) {
094 unsatisfiedDependencies(container, unsatisfiableDependencyTypes);
095 } else if (nonMatchingParameterPositions.size() > 0) {
096 throw new PicoCompositionException("Following parameters do not match any of the injectionMembers for " + getComponentImplementation() + ": " + nonMatchingParameterPositions.toString());
097 }
098 return matchingParameterList.toArray(new Parameter[matchingParameterList.size()]);
099 }
100
101 protected void unsatisfiedDependencies(PicoContainer container, Set<Class> unsatisfiableDependencyTypes) {
102 throw new UnsatisfiableDependenciesException(this, null, unsatisfiableDependencyTypes, container);
103 }
104
105 public Object getComponentInstance(final PicoContainer container) throws PicoCompositionException {
106 final Constructor constructor = getConstructor();
107 if (instantiationGuard == null) {
108 instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
109 public Object run() {
110 final Parameter[] matchingParameters = getMatchingParameterListForSetters(guardedContainer);
111 ComponentMonitor componentMonitor = currentMonitor();
112 Object componentInstance;
113
114 componentInstance = getOrMakeInstance(container, constructor, componentMonitor);
115 Member member = null;
116 Object injected[] = new Object[injectionMembers.size()];
117 try {
118 for (int i = 0; i < injectionMembers.size(); i++) {
119 member = injectionMembers.get(i);
120 componentMonitor.invoking(container, IterativeInjector.this, member, componentInstance);
121 if (matchingParameters[i] == null) {
122 continue;
123 }
124 Object toInject = matchingParameters[i].resolveInstance(guardedContainer, IterativeInjector.this, injectionTypes[i],
125 new IterativeInjectorParameterName());
126 injectIntoMember(member, componentInstance, toInject);
127 injected[i] = toInject;
128 }
129 return componentInstance;
130 } catch (InvocationTargetException e) {
131 return caughtInvocationTargetException(componentMonitor, member, componentInstance, e);
132 } catch (IllegalAccessException e) {
133 return caughtIllegalAccessException(componentMonitor, member, componentInstance, e);
134 }
135
136 }
137 };
138 }
139 instantiationGuard.setGuardedContainer(container);
140 return instantiationGuard.observe(getComponentImplementation());
141 }
142
143 protected Object getOrMakeInstance(PicoContainer container,
144 Constructor constructor,
145 ComponentMonitor componentMonitor) {
146 long startTime = System.currentTimeMillis();
147 Constructor constructorToUse = componentMonitor.instantiating(container,
148 IterativeInjector.this, constructor);
149 Object componentInstance;
150 try {
151 componentInstance = newInstance(constructorToUse, null);
152 } catch (InvocationTargetException e) {
153 componentMonitor.instantiationFailed(container, IterativeInjector.this, constructorToUse, e);
154 if (e.getTargetException() instanceof RuntimeException) {
155 throw (RuntimeException)e.getTargetException();
156 } else if (e.getTargetException() instanceof Error) {
157 throw (Error)e.getTargetException();
158 }
159 throw new PicoCompositionException(e.getTargetException());
160 } catch (InstantiationException e) {
161 return caughtInstantiationException(componentMonitor, constructor, e, container);
162 } catch (IllegalAccessException e) {
163 return caughtIllegalAccessException(componentMonitor, constructor, e, container);
164 }
165 componentMonitor.instantiated(container,
166 IterativeInjector.this,
167 constructorToUse,
168 componentInstance,
169 null,
170 System.currentTimeMillis() - startTime);
171 return componentInstance;
172 }
173
174 protected void injectIntoMember(Member member, Object componentInstance, Object toInject)
175 throws IllegalAccessException, InvocationTargetException {
176 ((Method)member).invoke(componentInstance, toInject);
177 }
178
179 public void verify(final PicoContainer container) throws PicoCompositionException {
180 if (verifyingGuard == null) {
181 verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
182 public Object run() {
183 final Parameter[] currentParameters = getMatchingParameterListForSetters(guardedContainer);
184 for (int i = 0; i < currentParameters.length; i++) {
185 currentParameters[i].verify(container, IterativeInjector.this, injectionTypes[i],
186 new IterativeInjectorParameterName());
187 }
188 return null;
189 }
190 };
191 }
192 verifyingGuard.setGuardedContainer(container);
193 verifyingGuard.observe(getComponentImplementation());
194 }
195
196 protected void initializeInjectionMembersAndTypeLists() {
197 injectionMembers = new ArrayList<Member>();
198 final List<Class> typeList = new ArrayList<Class>();
199 final Method[] methods = getMethods();
200 for (final Method method : methods) {
201 final Class[] parameterTypes = method.getParameterTypes();
202 // We're only interested if there is only one parameter and the method name is bean-style.
203 if (parameterTypes.length == 1) {
204 boolean isInjector = isInjectorMethod(method);
205 if (isInjector) {
206 injectionMembers.add(method);
207 typeList.add(parameterTypes[0]);
208 }
209 }
210 }
211 injectionTypes = typeList.toArray(new Class[0]);
212 }
213
214 protected boolean isInjectorMethod(Method method) {
215 return false;
216 }
217
218 private Method[] getMethods() {
219 return (Method[]) AccessController.doPrivileged(new PrivilegedAction() {
220 public Object run() {
221 return getComponentImplementation().getMethods();
222 }
223 });
224 }
225
226 private static class IterativeInjectorParameterName implements ParameterName {
227 public String getName() {
228 return ""; // TODO
229 }
230 }
231
232 }