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