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.injectors;
011
012 import java.lang.reflect.Constructor;
013 import java.lang.reflect.InvocationTargetException;
014 import java.lang.reflect.Member;
015 import java.lang.reflect.Modifier;
016 import java.lang.reflect.Type;
017 import java.util.Arrays;
018 import java.util.LinkedList;
019 import java.util.List;
020 import java.util.Set;
021
022 import org.picocontainer.ComponentAdapter;
023 import org.picocontainer.ComponentMonitor;
024 import org.picocontainer.LifecycleStrategy;
025 import org.picocontainer.ObjectReference;
026 import org.picocontainer.Parameter;
027 import org.picocontainer.PicoCompositionException;
028 import org.picocontainer.PicoContainer;
029 import org.picocontainer.PicoVisitor;
030 import org.picocontainer.adapters.AbstractAdapter;
031 import org.picocontainer.parameters.ComponentParameter;
032
033 /**
034 * This ComponentAdapter will instantiate a new object for each call to
035 * {@link org.picocontainer.ComponentAdapter#getComponentInstance(PicoContainer, Type)}.
036 * That means that when used with a PicoContainer, getComponent will
037 * return a new object each time.
038 *
039 * @author Aslak Hellesøy
040 * @author Paul Hammant
041 * @author Jörg Schaible
042 * @author Mauro Talevi
043 */
044 @SuppressWarnings("serial")
045 public abstract class AbstractInjector<T> extends AbstractAdapter<T> implements org.picocontainer.Injector<T> {
046
047 /** The cycle guard for the verification. */
048 protected transient ThreadLocalCyclicDependencyGuard verifyingGuard;
049 /** The parameters to use for initialization. */
050 protected transient Parameter[] parameters;
051 /** The strategy used to control the lifecycle */
052 private final boolean useNames;
053
054 /**
055 * Constructs a new ComponentAdapter for the given key and implementation.
056 * @param componentKey the search key for this implementation
057 * @param componentImplementation the concrete implementation
058 * @param parameters the parameters to use for the initialization
059 * @param monitor the component monitor used by this ComponentAdapter
060 * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException if the implementation is not a concrete class
061 * @throws NullPointerException if one of the parameters is <code>null</code>
062 */
063 protected AbstractInjector(final Object componentKey, final Class<?> componentImplementation, final Parameter[] parameters,
064 final ComponentMonitor monitor, final boolean useNames) {
065 super(componentKey, componentImplementation, monitor);
066 this.useNames = useNames;
067 checkConcrete();
068 if (parameters != null) {
069 for (int i = 0; i < parameters.length; i++) {
070 if(parameters[i] == null) {
071 throw new NullPointerException("Parameter " + i + " is null");
072 }
073 }
074 }
075 this.parameters = parameters;
076 }
077
078 public boolean useNames() {
079 return useNames;
080 }
081
082 private void checkConcrete() throws NotConcreteRegistrationException {
083 // Assert that the component class is concrete.
084 boolean isAbstract = (getComponentImplementation().getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT;
085 if (getComponentImplementation().isInterface() || isAbstract) {
086 throw new NotConcreteRegistrationException(getComponentImplementation());
087 }
088 }
089
090 /**
091 * Create default parameters for the given types.
092 *
093 * @param parameterTypes the parameter types
094 * @return the array with the default parameters.
095 */
096 protected Parameter[] createDefaultParameters(final Type[] parameterTypes) {
097 Parameter[] componentParameters = new Parameter[parameterTypes.length];
098 for (int i = 0; i < parameterTypes.length; i++) {
099 componentParameters[i] = ComponentParameter.DEFAULT;
100 }
101 return componentParameters;
102 }
103
104 @SuppressWarnings("unused")
105 public void verify(PicoContainer container) throws PicoCompositionException {
106 }
107
108 @Override
109 public T getComponentInstance(PicoContainer container) throws PicoCompositionException {
110 return getComponentInstance(container, NOTHING.class);
111 }
112
113 public abstract T getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException;
114
115 @SuppressWarnings("unused")
116 public Object decorateComponentInstance(PicoContainer container, Type into, T instance) {
117 return null;
118 }
119
120 @Override
121 public void accept(final PicoVisitor visitor) {
122 super.accept(visitor);
123 if (parameters != null) {
124 for (Parameter parameter : parameters) {
125 parameter.accept(visitor);
126 }
127 }
128 }
129
130
131 public String getDescriptor() {
132 return "Asbtract Injector";
133 }
134
135 /**
136 * Instantiate an object with given parameters and respect the accessible flag.
137 *
138 * @param constructor the constructor to use
139 * @param parameters the parameters for the constructor
140 * @return the new object.
141 * @throws InstantiationException
142 * @throws IllegalAccessException
143 * @throws InvocationTargetException
144 */
145 protected T newInstance(final Constructor<T> constructor, final Object[] parameters)
146 throws InstantiationException, IllegalAccessException, InvocationTargetException {
147 return constructor.newInstance(parameters);
148 }
149 /**
150 * inform monitor about component instantiation failure
151 * @param componentMonitor
152 * @param constructor
153 * @param e
154 * @param container
155 * @return
156 */
157 protected T caughtInstantiationException(final ComponentMonitor componentMonitor,
158 final Constructor<T> constructor,
159 final InstantiationException e, final PicoContainer container) {
160 // can't get here because checkConcrete() will catch it earlier, but see PICO-191
161 componentMonitor.instantiationFailed(container, this, constructor, e);
162 throw new PicoCompositionException("Should never get here");
163 }
164
165 /**
166 * inform monitor about access exception.
167 * @param componentMonitor
168 * @param constructor
169 * @param e
170 * @param container
171 * @return
172 */
173 protected T caughtIllegalAccessException(final ComponentMonitor componentMonitor,
174 final Constructor<T> constructor,
175 final IllegalAccessException e, final PicoContainer container) {
176 // can't get here because either filtered or access mode set
177 componentMonitor.instantiationFailed(container, this, constructor, e);
178 throw new PicoCompositionException(e);
179 }
180
181 /**
182 * inform monitor about exception while instantiating component
183 * @param componentMonitor
184 * @param member
185 * @param componentInstance
186 * @param e
187 * @return
188 */
189 protected T caughtInvocationTargetException(final ComponentMonitor componentMonitor,
190 final Member member,
191 final Object componentInstance, final InvocationTargetException e) {
192 componentMonitor.invocationFailed(member, componentInstance, e);
193 if (e.getTargetException() instanceof RuntimeException) {
194 throw (RuntimeException) e.getTargetException();
195 } else if (e.getTargetException() instanceof Error) {
196 throw (Error) e.getTargetException();
197 }
198 throw new PicoCompositionException(e.getTargetException());
199 }
200
201 protected Object caughtIllegalAccessException(final ComponentMonitor componentMonitor,
202 final Member member,
203 final Object componentInstance, final IllegalAccessException e) {
204 componentMonitor.invocationFailed(member, componentInstance, e);
205 throw new PicoCompositionException(e);
206 }
207
208 protected Type box(Type parameterType) {
209 if (parameterType instanceof Class && ((Class) parameterType).isPrimitive()) {
210 String parameterTypeName = ((Class) parameterType).getName();
211 if (parameterTypeName == "int") {
212 return Integer.class;
213 } else if (parameterTypeName == "boolean") {
214 return Boolean.class;
215 } else if (parameterTypeName == "long") {
216 return Long.class;
217 } else if (parameterTypeName == "float") {
218 return Float.class;
219 } else if (parameterTypeName == "double") {
220 return Double.class;
221 } else if (parameterTypeName == "char") {
222 return Character.class;
223 } else if (parameterTypeName == "byte") {
224 return Byte.class;
225 } else if (parameterTypeName == "short") {
226 return Short.class;
227 }
228 }
229 return parameterType;
230 }
231
232 /**
233 * Abstract utility class to detect recursion cycles.
234 * Derive from this class and implement {@link ThreadLocalCyclicDependencyGuard#run}.
235 * The method will be called by {@link ThreadLocalCyclicDependencyGuard#observe}. Select
236 * an appropriate guard for your scope. Any {@link ObjectReference} can be
237 * used as long as it is initialized with <code>Boolean.FALSE</code>.
238 *
239 * @author Jörg Schaible
240 */
241 static abstract class ThreadLocalCyclicDependencyGuard<T> extends ThreadLocal<Boolean> {
242
243 protected PicoContainer guardedContainer;
244
245 @Override
246 protected Boolean initialValue() {
247 return Boolean.FALSE;
248 }
249
250 /**
251 * Derive from this class and implement this function with the functionality
252 * to observe for a dependency cycle.
253 *
254 * @return a value, if the functionality result in an expression,
255 * otherwise just return <code>null</code>
256 */
257 public abstract T run();
258
259 /**
260 * Call the observing function. The provided guard will hold the {@link Boolean} value.
261 * If the guard is already <code>Boolean.TRUE</code> a {@link CyclicDependencyException}
262 * will be thrown.
263 *
264 * @param stackFrame the current stack frame
265 * @return the result of the <code>run</code> method
266 */
267 public final T observe(final Class<?> stackFrame) {
268 if (Boolean.TRUE.equals(get())) {
269 throw new CyclicDependencyException(stackFrame);
270 }
271 T result = null;
272 try {
273 set(Boolean.TRUE);
274 result = run();
275 } catch (final CyclicDependencyException e) {
276 e.push(stackFrame);
277 throw e;
278 } finally {
279 set(Boolean.FALSE);
280 }
281 return result;
282 }
283
284 public void setGuardedContainer(final PicoContainer container) {
285 this.guardedContainer = container;
286 }
287
288 }
289
290 public static class CyclicDependencyException extends PicoCompositionException {
291 private final List<Class> stack;
292
293 /**
294 * @param element
295 */
296 public CyclicDependencyException(final Class<?> element) {
297 super((Throwable)null);
298 this.stack = new LinkedList<Class>();
299 push(element);
300 }
301
302 /**
303 * @param element
304 */
305 public void push(final Class<?> element) {
306 stack.add(element);
307 }
308
309 public Class[] getDependencies() {
310 return stack.toArray(new Class[stack.size()]);
311 }
312
313 @Override
314 public String getMessage() {
315 return "Cyclic dependency: " + stack.toString();
316 }
317 }
318
319 /**
320 * Exception that is thrown as part of the introspection. Raised if a PicoContainer cannot resolve a
321 * type dependency because the registered {@link org.picocontainer.ComponentAdapter}s are not
322 * distinct.
323 *
324 * @author Paul Hammant
325 * @author Aslak Hellesøy
326 * @author Jon Tirsén
327 */
328 public static final class AmbiguousComponentResolutionException extends PicoCompositionException {
329
330
331 private Class<?> component;
332 private final Class<?> ambiguousDependency;
333 private final Object[] ambiguousComponentKeys;
334
335
336 /**
337 * Construct a new exception with the ambigous class type and the ambiguous component keys.
338 *
339 * @param ambiguousDependency the unresolved dependency type
340 * @param componentKeys the ambiguous keys.
341 */
342 public AmbiguousComponentResolutionException(final Class<?> ambiguousDependency, final Object[] componentKeys) {
343 super("");
344 this.ambiguousDependency = ambiguousDependency;
345 this.ambiguousComponentKeys = new Class[componentKeys.length];
346 System.arraycopy(componentKeys, 0, ambiguousComponentKeys, 0, componentKeys.length);
347 }
348
349 /**
350 * @return Returns a string containing the unresolved class type and the ambiguous keys.
351 */
352 @Override
353 public String getMessage() {
354 StringBuffer msg = new StringBuffer();
355 msg.append(component != null ? component : "<no-component>");
356 msg.append(" needs a '");
357 msg.append(ambiguousDependency.getName());
358 msg.append("' injected, but there are too many choices to inject. These:");
359 msg.append(Arrays.asList(getAmbiguousComponentKeys()));
360 msg.append(", refer http://picocontainer.org/help/ambiguous-injectable-help.html");
361 return msg.toString();
362 }
363
364 /**
365 * @return Returns the ambiguous component keys as array.
366 */
367 public Object[] getAmbiguousComponentKeys() {
368 return ambiguousComponentKeys;
369 }
370
371 public void setComponent(final Class<?> component) {
372 this.component = component;
373 }
374 }
375
376 /**
377 * Exception thrown when some of the component's dependencies are not satisfiable.
378 *
379 * @author Aslak Hellesøy
380 * @author Mauro Talevi
381 */
382 public static class UnsatisfiableDependenciesException extends PicoCompositionException {
383
384
385 private final ComponentAdapter<?> instantiatingComponentAdapter;
386 private final Set unsatisfiableDependencies;
387 private final Type unsatisfiedDependencyType;
388
389 /**
390 * The original container requesting the instantiation of the component.
391 */
392 private final PicoContainer leafContainer;
393
394 public UnsatisfiableDependenciesException(final ComponentAdapter<?> instantiatingComponentAdapter,
395 final Type unsatisfiedDependencyType, final Set unsatisfiableDependencies,
396 final PicoContainer leafContainer) {
397 super(instantiatingComponentAdapter.getComponentImplementation().getName() + " has unsatisfied dependency: " + unsatisfiedDependencyType
398 +" among unsatisfiable dependencies: "+unsatisfiableDependencies + " where " + leafContainer
399 + " was the leaf container being asked for dependencies.");
400 this.instantiatingComponentAdapter = instantiatingComponentAdapter;
401 this.unsatisfiableDependencies = unsatisfiableDependencies;
402 this.unsatisfiedDependencyType = unsatisfiedDependencyType;
403 this.leafContainer = leafContainer;
404 }
405
406 public ComponentAdapter<?> getUnsatisfiableComponentAdapter() {
407 return instantiatingComponentAdapter;
408 }
409
410 public Set getUnsatisfiableDependencies() {
411 return unsatisfiableDependencies;
412 }
413
414 public Type getUnsatisfiedDependencyType() {
415 return unsatisfiedDependencyType;
416 }
417
418 public PicoContainer getLeafContainer() {
419 return leafContainer;
420 }
421
422 }
423
424 /**
425 * @author Aslak Hellesoy
426 */
427 public static class NotConcreteRegistrationException extends PicoCompositionException {
428
429 private final Class<?> componentImplementation;
430
431 public NotConcreteRegistrationException(final Class<?> componentImplementation) {
432 super("Bad Access: '" + componentImplementation.getName() + "' is not instantiable");
433 this.componentImplementation = componentImplementation;
434 }
435
436 public Class<?> getComponentImplementation() {
437 return componentImplementation;
438 }
439 }
440 }