Author: rgielen Date: Tue Feb 28 00:28:57 2012 New Revision: 1294413 URL: http://svn.apache.org/viewvc?rev=1294413&view=rev Log: Simple code reformatting, no actual change.
Modified: struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java Modified: struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java?rev=1294413&r1=1294412&r2=1294413&view=diff ============================================================================== --- struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java (original) +++ struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java Tue Feb 28 00:28:57 2012 @@ -28,594 +28,602 @@ import java.security.AccessControlExcept /** * Default {@link Container} implementation. * - * @see ContainerBuilder * @author crazy...@google.com (Bob Lee) + * @see ContainerBuilder */ class ContainerImpl implements Container { - final Map<Key<?>, InternalFactory<?>> factories; - final Map<Class<?>,Set<String>> factoryNamesByType; + final Map<Key<?>, InternalFactory<?>> factories; + final Map<Class<?>, Set<String>> factoryNamesByType; - ContainerImpl(Map<Key<?>, InternalFactory<?>> factories) { - this.factories = factories; - Map<Class<?>,Set<String>> map = new HashMap<Class<?>,Set<String>>(); - for (Key<?> key : factories.keySet()) { - Set<String> names = map.get(key.getType()); - if (names == null) { - names = new HashSet<String>(); - map.put(key.getType(), names); - } - names.add(key.getName()); - } - - for (Entry<Class<?>,Set<String>> entry : map.entrySet()) { - entry.setValue(Collections.unmodifiableSet(entry.getValue())); - } - - this.factoryNamesByType = Collections.unmodifiableMap(map); - } - - @SuppressWarnings("unchecked") - <T> InternalFactory<? extends T> getFactory(Key<T> key) { - return (InternalFactory<T>) factories.get(key); - } - - /** - * Field and method injectors. - */ - final Map<Class<?>, List<Injector>> injectors = - new ReferenceCache<Class<?>, List<Injector>>() { - @Override - protected List<Injector> create(Class<?> key) { - List<Injector> injectors = new ArrayList<Injector>(); - addInjectors(key, injectors); - return injectors; - } - }; - - /** - * Recursively adds injectors for fields and methods from the given class to - * the given list. Injects parent classes before sub classes. - */ - void addInjectors(Class clazz, List<Injector> injectors) { - if (clazz == Object.class) { - return; - } - - // Add injectors for superclass first. - addInjectors(clazz.getSuperclass(), injectors); - - // TODO (crazybob): Filter out overridden members. - addInjectorsForFields(clazz.getDeclaredFields(), false, injectors); - addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors); - } - - void injectStatics(List<Class<?>> staticInjections) { - final List<Injector> injectors = new ArrayList<Injector>(); - - for (Class<?> clazz : staticInjections) { - addInjectorsForFields(clazz.getDeclaredFields(), true, injectors); - addInjectorsForMethods(clazz.getDeclaredMethods(), true, injectors); - } - - callInContext(new ContextualCallable<Void>() { - public Void call(InternalContext context) { - for (Injector injector : injectors) { - injector.inject(context, null); - } - return null; - } - }); - } - - void addInjectorsForMethods(Method[] methods, boolean statics, - List<Injector> injectors) { - addInjectorsForMembers(Arrays.asList(methods), statics, injectors, - new InjectorFactory<Method>() { - public Injector create(ContainerImpl container, Method method, - String name) throws MissingDependencyException { - return new MethodInjector(container, method, name); - } - }); - } - - void addInjectorsForFields(Field[] fields, boolean statics, - List<Injector> injectors) { - addInjectorsForMembers(Arrays.asList(fields), statics, injectors, - new InjectorFactory<Field>() { - public Injector create(ContainerImpl container, Field field, - String name) throws MissingDependencyException { - return new FieldInjector(container, field, name); - } - }); - } - - <M extends Member & AnnotatedElement> void addInjectorsForMembers( - List<M> members, boolean statics, List<Injector> injectors, - InjectorFactory<M> injectorFactory) { - for (M member : members) { - if (isStatic(member) == statics) { - Inject inject = member.getAnnotation(Inject.class); - if (inject != null) { - try { - injectors.add(injectorFactory.create(this, member, inject.value())); - } catch (MissingDependencyException e) { - if (inject.required()) { - throw new DependencyException(e); - } - } - } - } - } - } - - interface InjectorFactory<M extends Member & AnnotatedElement> { - Injector create(ContainerImpl container, M member, String name) - throws MissingDependencyException; - } - - private boolean isStatic(Member member) { - return Modifier.isStatic(member.getModifiers()); - } - - static class FieldInjector implements Injector { - - final Field field; - final InternalFactory<?> factory; - final ExternalContext<?> externalContext; - - public FieldInjector(ContainerImpl container, Field field, String name) - throws MissingDependencyException { - this.field = field; - if (!field.isAccessible()) { - SecurityManager sm = System.getSecurityManager(); - try { - if (sm != null) sm.checkPermission(new ReflectPermission("suppressAccessChecks")); - field.setAccessible(true); - } catch(AccessControlException e) { - throw new DependencyException("Security manager in use, could not access field: " - + field.getDeclaringClass().getName() + "(" + field.getName() + ")", e); - } - } - - Key<?> key = Key.newInstance(field.getType(), name); - factory = container.getFactory(key); - if (factory == null) { - throw new MissingDependencyException( - "No mapping found for dependency " + key + " in " + field + "."); - } - - this.externalContext = ExternalContext.newInstance(field, key, container); - } - - public void inject(InternalContext context, Object o) { - ExternalContext<?> previous = context.getExternalContext(); - context.setExternalContext(externalContext); - try { - field.set(o, factory.create(context)); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } finally { - context.setExternalContext(previous); - } - } - } - - /** - * Gets parameter injectors. - * - * @param member to which the parameters belong - * @param annotations on the parameters - * @param parameterTypes parameter types - * @return injections - */ - <M extends AccessibleObject & Member> ParameterInjector<?>[] - getParametersInjectors(M member, - Annotation[][] annotations, Class[] parameterTypes, String defaultName) - throws MissingDependencyException { - List<ParameterInjector<?>> parameterInjectors = - new ArrayList<ParameterInjector<?>>(); - - Iterator<Annotation[]> annotationsIterator = - Arrays.asList(annotations).iterator(); - for (Class<?> parameterType : parameterTypes) { - Inject annotation = findInject(annotationsIterator.next()); - String name = annotation == null ? defaultName : annotation.value(); - Key<?> key = Key.newInstance(parameterType, name); - parameterInjectors.add(createParameterInjector(key, member)); - } - - return toArray(parameterInjectors); - } - - <T> ParameterInjector<T> createParameterInjector( - Key<T> key, Member member) throws MissingDependencyException { - InternalFactory<? extends T> factory = getFactory(key); - if (factory == null) { - throw new MissingDependencyException( - "No mapping found for dependency " + key + " in " + member + "."); - } - - ExternalContext<T> externalContext = - ExternalContext.newInstance(member, key, this); - return new ParameterInjector<T>(externalContext, factory); - } - - @SuppressWarnings("unchecked") - private ParameterInjector<?>[] toArray( - List<ParameterInjector<?>> parameterInjections) { - return parameterInjections.toArray( - new ParameterInjector[parameterInjections.size()]); - } - - /** - * Finds the {@link Inject} annotation in an array of annotations. - */ - Inject findInject(Annotation[] annotations) { - for (Annotation annotation : annotations) { - if (annotation.annotationType() == Inject.class) { - return Inject.class.cast(annotation); - } - } - return null; - } - - static class MethodInjector implements Injector { - - final Method method; - final ParameterInjector<?>[] parameterInjectors; - - public MethodInjector(ContainerImpl container, Method method, String name) - throws MissingDependencyException { - this.method = method; - if (!method.isAccessible()) { - SecurityManager sm = System.getSecurityManager(); - try { - if (sm != null) sm.checkPermission(new ReflectPermission("suppressAccessChecks")); - method.setAccessible(true); - } catch(AccessControlException e) { - throw new DependencyException("Security manager in use, could not access method: " - + name + "(" + method.getName() + ")", e); - } - } - - Class<?>[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length == 0) { - throw new DependencyException( - method + " has no parameters to inject."); - } - parameterInjectors = container.getParametersInjectors( - method, method.getParameterAnnotations(), parameterTypes, name); - } - - public void inject(InternalContext context, Object o) { - try { - method.invoke(o, getParameters(method, context, parameterInjectors)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - - Map<Class<?>, ConstructorInjector> constructors = - new ReferenceCache<Class<?>, ConstructorInjector>() { - @Override - @SuppressWarnings("unchecked") - protected ConstructorInjector<?> create(Class<?> implementation) { - return new ConstructorInjector(ContainerImpl.this, implementation); - } - }; - - static class ConstructorInjector<T> { - - final Class<T> implementation; - final List<Injector> injectors; - final Constructor<T> constructor; - final ParameterInjector<?>[] parameterInjectors; - - ConstructorInjector(ContainerImpl container, Class<T> implementation) { - this.implementation = implementation; - - constructor = findConstructorIn(implementation); - if (!constructor.isAccessible()) { - SecurityManager sm = System.getSecurityManager(); - try { - if (sm != null) sm.checkPermission(new ReflectPermission("suppressAccessChecks")); - constructor.setAccessible(true); - } catch(AccessControlException e) { - throw new DependencyException("Security manager in use, could not access constructor: " - + implementation.getName() + "(" + constructor.getName() + ")", e); - } - } - - MissingDependencyException exception = null; - Inject inject = null; - ParameterInjector<?>[] parameters = null; - - try { - inject = constructor.getAnnotation(Inject.class); - parameters = constructParameterInjector(inject, container, constructor); - } catch (MissingDependencyException e) { - exception = e; - } - parameterInjectors = parameters; - - if ( exception != null) { - if ( inject != null && inject.required()) { - throw new DependencyException(exception); - } - } - injectors = container.injectors.get(implementation); - } - - ParameterInjector<?>[] constructParameterInjector( - Inject inject, ContainerImpl container, Constructor<T> constructor) throws MissingDependencyException{ - return constructor.getParameterTypes().length == 0 - ? null // default constructor. - : container.getParametersInjectors( - constructor, - constructor.getParameterAnnotations(), - constructor.getParameterTypes(), - inject.value() - ); - } - - @SuppressWarnings("unchecked") - private Constructor<T> findConstructorIn(Class<T> implementation) { - Constructor<T> found = null; - Constructor<T>[] declaredConstructors = (Constructor<T>[]) implementation - .getDeclaredConstructors(); - for(Constructor<T> constructor : declaredConstructors) { - if (constructor.getAnnotation(Inject.class) != null) { - if (found != null) { - throw new DependencyException("More than one constructor annotated" - + " with @Inject found in " + implementation + "."); - } - found = constructor; - } - } - if (found != null) { - return found; - } - - // If no annotated constructor is found, look for a no-arg constructor - // instead. - try { - return implementation.getDeclaredConstructor(); - } catch (NoSuchMethodException e) { - throw new DependencyException("Could not find a suitable constructor" - + " in " + implementation.getName() + "."); - } - } - - /** - * Construct an instance. Returns {@code Object} instead of {@code T} - * because it may return a proxy. - */ - Object construct(InternalContext context, Class<? super T> expectedType) { - ConstructionContext<T> constructionContext = - context.getConstructionContext(this); - - // We have a circular reference between constructors. Return a proxy. - if (constructionContext.isConstructing()) { - // TODO (crazybob): if we can't proxy this object, can we proxy the - // other object? - return constructionContext.createProxy(expectedType); - } - - // If we're re-entering this factory while injecting fields or methods, - // return the same instance. This prevents infinite loops. - T t = constructionContext.getCurrentReference(); - if (t != null) { - return t; - } - - try { - // First time through... - constructionContext.startConstruction(); - try { - Object[] parameters = - getParameters(constructor, context, parameterInjectors); - t = constructor.newInstance(parameters); - constructionContext.setProxyDelegates(t); - } finally { - constructionContext.finishConstruction(); - } - - // Store reference. If an injector re-enters this factory, they'll - // get the same reference. - constructionContext.setCurrentReference(t); - - // Inject fields and methods. - for (Injector injector : injectors) { - injector.inject(context, t); - } - - return t; - } catch (InstantiationException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } finally { - constructionContext.removeCurrentReference(); - } - } - } - - static class ParameterInjector<T> { - - final ExternalContext<T> externalContext; - final InternalFactory<? extends T> factory; - - public ParameterInjector(ExternalContext<T> externalContext, - InternalFactory<? extends T> factory) { - this.externalContext = externalContext; - this.factory = factory; - } - - T inject(Member member, InternalContext context) { - ExternalContext<?> previous = context.getExternalContext(); - context.setExternalContext(externalContext); - try { - return factory.create(context); - } finally { - context.setExternalContext(previous); - } - } - } - - private static Object[] getParameters(Member member, InternalContext context, - ParameterInjector[] parameterInjectors) { - if (parameterInjectors == null) { - return null; - } - - Object[] parameters = new Object[parameterInjectors.length]; - for (int i = 0; i < parameters.length; i++) { - parameters[i] = parameterInjectors[i].inject(member, context); - } - return parameters; - } - - void inject(Object o, InternalContext context) { - List<Injector> injectors = this.injectors.get(o.getClass()); - for (Injector injector : injectors) { - injector.inject(context, o); - } - } - - <T> T inject(Class<T> implementation, InternalContext context) { - try { - ConstructorInjector<T> constructor = getConstructor(implementation); - return implementation.cast( - constructor.construct(context, implementation)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @SuppressWarnings("unchecked") - <T> T getInstance(Class<T> type, String name, InternalContext context) { - ExternalContext<?> previous = context.getExternalContext(); - Key<T> key = Key.newInstance(type, name); - context.setExternalContext(ExternalContext.newInstance(null, key, this)); - try { - InternalFactory o = getFactory(key); - if (o != null) { - return getFactory(key).create(context); - } else { - return null; - } - } finally { - context.setExternalContext(previous); - } - } - - <T> T getInstance(Class<T> type, InternalContext context) { - return getInstance(type, DEFAULT_NAME, context); - } - - public void inject(final Object o) { - callInContext(new ContextualCallable<Void>() { - public Void call(InternalContext context) { - inject(o, context); - return null; - } - }); - } - - public <T> T inject(final Class<T> implementation) { - return callInContext(new ContextualCallable<T>() { - public T call(InternalContext context) { - return inject(implementation, context); - } - }); - } - - public <T> T getInstance(final Class<T> type, final String name) { - return callInContext(new ContextualCallable<T>() { - public T call(InternalContext context) { - return getInstance(type, name, context); - } - }); - } - - public <T> T getInstance(final Class<T> type) { - return callInContext(new ContextualCallable<T>() { - public T call(InternalContext context) { - return getInstance(type, context); - } - }); - } - - public Set<String> getInstanceNames(final Class<?> type) { - return factoryNamesByType.get(type); - } - - ThreadLocal<Object[]> localContext = - new ThreadLocal<Object[]>() { - @Override - protected Object[] initialValue() { - return new Object[1]; - } - }; - - /** - * Looks up thread local context. Creates (and removes) a new context if - * necessary. - */ - <T> T callInContext(ContextualCallable<T> callable) { - Object[] reference = localContext.get(); - if (reference[0] == null) { - reference[0] = new InternalContext(this); - try { - return callable.call((InternalContext)reference[0]); - } finally { - // Only remove the context if this call created it. - reference[0] = null; - } - } else { - // Someone else will clean up this context. - return callable.call((InternalContext)reference[0]); - } - } - - interface ContextualCallable<T> { - T call(InternalContext context); - } - - /** - * Gets a constructor function for a given implementation class. - */ - @SuppressWarnings("unchecked") - <T> ConstructorInjector<T> getConstructor(Class<T> implementation) { - return constructors.get(implementation); - } - - final ThreadLocal<Object> localScopeStrategy = - new ThreadLocal<Object>(); - - public void setScopeStrategy(Scope.Strategy scopeStrategy) { - this.localScopeStrategy.set(scopeStrategy); - } - - public void removeScopeStrategy() { - this.localScopeStrategy.remove(); - } - - /** - * Injects a field or method in a given object. - */ - interface Injector extends Serializable { - void inject(InternalContext context, Object o); - } - - static class MissingDependencyException extends Exception { - - MissingDependencyException(String message) { - super(message); - } - } + ContainerImpl( Map<Key<?>, InternalFactory<?>> factories ) { + this.factories = factories; + Map<Class<?>, Set<String>> map = new HashMap<Class<?>, Set<String>>(); + for ( Key<?> key : factories.keySet() ) { + Set<String> names = map.get(key.getType()); + if (names == null) { + names = new HashSet<String>(); + map.put(key.getType(), names); + } + names.add(key.getName()); + } + + for ( Entry<Class<?>, Set<String>> entry : map.entrySet() ) { + entry.setValue(Collections.unmodifiableSet(entry.getValue())); + } + + this.factoryNamesByType = Collections.unmodifiableMap(map); + } + + @SuppressWarnings("unchecked") + <T> InternalFactory<? extends T> getFactory( Key<T> key ) { + return (InternalFactory<T>) factories.get(key); + } + + /** + * Field and method injectors. + */ + final Map<Class<?>, List<Injector>> injectors = + new ReferenceCache<Class<?>, List<Injector>>() { + @Override + protected List<Injector> create( Class<?> key ) { + List<Injector> injectors = new ArrayList<Injector>(); + addInjectors(key, injectors); + return injectors; + } + }; + + /** + * Recursively adds injectors for fields and methods from the given class to the given list. Injects parent classes + * before sub classes. + */ + void addInjectors( Class clazz, List<Injector> injectors ) { + if (clazz == Object.class) { + return; + } + + // Add injectors for superclass first. + addInjectors(clazz.getSuperclass(), injectors); + + // TODO (crazybob): Filter out overridden members. + addInjectorsForFields(clazz.getDeclaredFields(), false, injectors); + addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors); + } + + void injectStatics( List<Class<?>> staticInjections ) { + final List<Injector> injectors = new ArrayList<Injector>(); + + for ( Class<?> clazz : staticInjections ) { + addInjectorsForFields(clazz.getDeclaredFields(), true, injectors); + addInjectorsForMethods(clazz.getDeclaredMethods(), true, injectors); + } + + callInContext(new ContextualCallable<Void>() { + public Void call( InternalContext context ) { + for ( Injector injector : injectors ) { + injector.inject(context, null); + } + return null; + } + }); + } + + void addInjectorsForMethods( Method[] methods, boolean statics, + List<Injector> injectors ) { + addInjectorsForMembers(Arrays.asList(methods), statics, injectors, + new InjectorFactory<Method>() { + public Injector create( ContainerImpl container, Method method, + String name ) throws MissingDependencyException { + return new MethodInjector(container, method, name); + } + }); + } + + void addInjectorsForFields( Field[] fields, boolean statics, + List<Injector> injectors ) { + addInjectorsForMembers(Arrays.asList(fields), statics, injectors, + new InjectorFactory<Field>() { + public Injector create( ContainerImpl container, Field field, + String name ) throws MissingDependencyException { + return new FieldInjector(container, field, name); + } + }); + } + + <M extends Member & AnnotatedElement> void addInjectorsForMembers( + List<M> members, boolean statics, List<Injector> injectors, + InjectorFactory<M> injectorFactory ) { + for ( M member : members ) { + if (isStatic(member) == statics) { + Inject inject = member.getAnnotation(Inject.class); + if (inject != null) { + try { + injectors.add(injectorFactory.create(this, member, inject.value())); + } catch ( MissingDependencyException e ) { + if (inject.required()) { + throw new DependencyException(e); + } + } + } + } + } + } + + interface InjectorFactory<M extends Member & AnnotatedElement> { + + Injector create( ContainerImpl container, M member, String name ) + throws MissingDependencyException; + } + + private boolean isStatic( Member member ) { + return Modifier.isStatic(member.getModifiers()); + } + + static class FieldInjector implements Injector { + + final Field field; + final InternalFactory<?> factory; + final ExternalContext<?> externalContext; + + public FieldInjector( ContainerImpl container, Field field, String name ) + throws MissingDependencyException { + this.field = field; + if (!field.isAccessible()) { + SecurityManager sm = System.getSecurityManager(); + try { + if (sm != null) { + sm.checkPermission(new ReflectPermission("suppressAccessChecks")); + } + field.setAccessible(true); + } catch ( AccessControlException e ) { + throw new DependencyException("Security manager in use, could not access field: " + + field.getDeclaringClass().getName() + "(" + field.getName() + ")", e); + } + } + + Key<?> key = Key.newInstance(field.getType(), name); + factory = container.getFactory(key); + if (factory == null) { + throw new MissingDependencyException( + "No mapping found for dependency " + key + " in " + field + "."); + } + + this.externalContext = ExternalContext.newInstance(field, key, container); + } + + public void inject( InternalContext context, Object o ) { + ExternalContext<?> previous = context.getExternalContext(); + context.setExternalContext(externalContext); + try { + field.set(o, factory.create(context)); + } catch ( IllegalAccessException e ) { + throw new AssertionError(e); + } finally { + context.setExternalContext(previous); + } + } + } + + /** + * Gets parameter injectors. + * + * @param member to which the parameters belong + * @param annotations on the parameters + * @param parameterTypes parameter types + * + * @return injections + */ + <M extends AccessibleObject & Member> ParameterInjector<?>[] + getParametersInjectors( M member, + Annotation[][] annotations, Class[] parameterTypes, String defaultName ) + throws MissingDependencyException { + List<ParameterInjector<?>> parameterInjectors = + new ArrayList<ParameterInjector<?>>(); + + Iterator<Annotation[]> annotationsIterator = + Arrays.asList(annotations).iterator(); + for ( Class<?> parameterType : parameterTypes ) { + Inject annotation = findInject(annotationsIterator.next()); + String name = annotation == null ? defaultName : annotation.value(); + Key<?> key = Key.newInstance(parameterType, name); + parameterInjectors.add(createParameterInjector(key, member)); + } + + return toArray(parameterInjectors); + } + + <T> ParameterInjector<T> createParameterInjector( + Key<T> key, Member member ) throws MissingDependencyException { + InternalFactory<? extends T> factory = getFactory(key); + if (factory == null) { + throw new MissingDependencyException( + "No mapping found for dependency " + key + " in " + member + "."); + } + + ExternalContext<T> externalContext = + ExternalContext.newInstance(member, key, this); + return new ParameterInjector<T>(externalContext, factory); + } + + @SuppressWarnings("unchecked") + private ParameterInjector<?>[] toArray( + List<ParameterInjector<?>> parameterInjections ) { + return parameterInjections.toArray( + new ParameterInjector[parameterInjections.size()]); + } + + /** + * Finds the {@link Inject} annotation in an array of annotations. + */ + Inject findInject( Annotation[] annotations ) { + for ( Annotation annotation : annotations ) { + if (annotation.annotationType() == Inject.class) { + return Inject.class.cast(annotation); + } + } + return null; + } + + static class MethodInjector implements Injector { + + final Method method; + final ParameterInjector<?>[] parameterInjectors; + + public MethodInjector( ContainerImpl container, Method method, String name ) + throws MissingDependencyException { + this.method = method; + if (!method.isAccessible()) { + SecurityManager sm = System.getSecurityManager(); + try { + if (sm != null) { + sm.checkPermission(new ReflectPermission("suppressAccessChecks")); + } + method.setAccessible(true); + } catch ( AccessControlException e ) { + throw new DependencyException("Security manager in use, could not access method: " + + name + "(" + method.getName() + ")", e); + } + } + + Class<?>[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length == 0) { + throw new DependencyException( + method + " has no parameters to inject."); + } + parameterInjectors = container.getParametersInjectors( + method, method.getParameterAnnotations(), parameterTypes, name); + } + + public void inject( InternalContext context, Object o ) { + try { + method.invoke(o, getParameters(method, context, parameterInjectors)); + } catch ( Exception e ) { + throw new RuntimeException(e); + } + } + } + + Map<Class<?>, ConstructorInjector> constructors = + new ReferenceCache<Class<?>, ConstructorInjector>() { + @Override + @SuppressWarnings("unchecked") + protected ConstructorInjector<?> create( Class<?> implementation ) { + return new ConstructorInjector(ContainerImpl.this, implementation); + } + }; + + static class ConstructorInjector<T> { + + final Class<T> implementation; + final List<Injector> injectors; + final Constructor<T> constructor; + final ParameterInjector<?>[] parameterInjectors; + + ConstructorInjector( ContainerImpl container, Class<T> implementation ) { + this.implementation = implementation; + + constructor = findConstructorIn(implementation); + if (!constructor.isAccessible()) { + SecurityManager sm = System.getSecurityManager(); + try { + if (sm != null) { + sm.checkPermission(new ReflectPermission("suppressAccessChecks")); + } + constructor.setAccessible(true); + } catch ( AccessControlException e ) { + throw new DependencyException("Security manager in use, could not access constructor: " + + implementation.getName() + "(" + constructor.getName() + ")", e); + } + } + + MissingDependencyException exception = null; + Inject inject = null; + ParameterInjector<?>[] parameters = null; + + try { + inject = constructor.getAnnotation(Inject.class); + parameters = constructParameterInjector(inject, container, constructor); + } catch ( MissingDependencyException e ) { + exception = e; + } + parameterInjectors = parameters; + + if (exception != null) { + if (inject != null && inject.required()) { + throw new DependencyException(exception); + } + } + injectors = container.injectors.get(implementation); + } + + ParameterInjector<?>[] constructParameterInjector( + Inject inject, ContainerImpl container, Constructor<T> constructor ) throws MissingDependencyException { + return constructor.getParameterTypes().length == 0 + ? null // default constructor. + : container.getParametersInjectors( + constructor, + constructor.getParameterAnnotations(), + constructor.getParameterTypes(), + inject.value() + ); + } + + @SuppressWarnings("unchecked") + private Constructor<T> findConstructorIn( Class<T> implementation ) { + Constructor<T> found = null; + Constructor<T>[] declaredConstructors = (Constructor<T>[]) implementation + .getDeclaredConstructors(); + for ( Constructor<T> constructor : declaredConstructors ) { + if (constructor.getAnnotation(Inject.class) != null) { + if (found != null) { + throw new DependencyException("More than one constructor annotated" + + " with @Inject found in " + implementation + "."); + } + found = constructor; + } + } + if (found != null) { + return found; + } + + // If no annotated constructor is found, look for a no-arg constructor + // instead. + try { + return implementation.getDeclaredConstructor(); + } catch ( NoSuchMethodException e ) { + throw new DependencyException("Could not find a suitable constructor" + + " in " + implementation.getName() + "."); + } + } + + /** + * Construct an instance. Returns {@code Object} instead of {@code T} because it may return a proxy. + */ + Object construct( InternalContext context, Class<? super T> expectedType ) { + ConstructionContext<T> constructionContext = + context.getConstructionContext(this); + + // We have a circular reference between constructors. Return a proxy. + if (constructionContext.isConstructing()) { + // TODO (crazybob): if we can't proxy this object, can we proxy the + // other object? + return constructionContext.createProxy(expectedType); + } + + // If we're re-entering this factory while injecting fields or methods, + // return the same instance. This prevents infinite loops. + T t = constructionContext.getCurrentReference(); + if (t != null) { + return t; + } + + try { + // First time through... + constructionContext.startConstruction(); + try { + Object[] parameters = + getParameters(constructor, context, parameterInjectors); + t = constructor.newInstance(parameters); + constructionContext.setProxyDelegates(t); + } finally { + constructionContext.finishConstruction(); + } + + // Store reference. If an injector re-enters this factory, they'll + // get the same reference. + constructionContext.setCurrentReference(t); + + // Inject fields and methods. + for ( Injector injector : injectors ) { + injector.inject(context, t); + } + + return t; + } catch ( InstantiationException e ) { + throw new RuntimeException(e); + } catch ( IllegalAccessException e ) { + throw new RuntimeException(e); + } catch ( InvocationTargetException e ) { + throw new RuntimeException(e); + } finally { + constructionContext.removeCurrentReference(); + } + } + } + + static class ParameterInjector<T> { + + final ExternalContext<T> externalContext; + final InternalFactory<? extends T> factory; + + public ParameterInjector( ExternalContext<T> externalContext, + InternalFactory<? extends T> factory ) { + this.externalContext = externalContext; + this.factory = factory; + } + + T inject( Member member, InternalContext context ) { + ExternalContext<?> previous = context.getExternalContext(); + context.setExternalContext(externalContext); + try { + return factory.create(context); + } finally { + context.setExternalContext(previous); + } + } + } + + private static Object[] getParameters( Member member, InternalContext context, + ParameterInjector[] parameterInjectors ) { + if (parameterInjectors == null) { + return null; + } + + Object[] parameters = new Object[parameterInjectors.length]; + for ( int i = 0; i < parameters.length; i++ ) { + parameters[i] = parameterInjectors[i].inject(member, context); + } + return parameters; + } + + void inject( Object o, InternalContext context ) { + List<Injector> injectors = this.injectors.get(o.getClass()); + for ( Injector injector : injectors ) { + injector.inject(context, o); + } + } + + <T> T inject( Class<T> implementation, InternalContext context ) { + try { + ConstructorInjector<T> constructor = getConstructor(implementation); + return implementation.cast( + constructor.construct(context, implementation)); + } catch ( Exception e ) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + <T> T getInstance( Class<T> type, String name, InternalContext context ) { + ExternalContext<?> previous = context.getExternalContext(); + Key<T> key = Key.newInstance(type, name); + context.setExternalContext(ExternalContext.newInstance(null, key, this)); + try { + InternalFactory o = getFactory(key); + if (o != null) { + return getFactory(key).create(context); + } else { + return null; + } + } finally { + context.setExternalContext(previous); + } + } + + <T> T getInstance( Class<T> type, InternalContext context ) { + return getInstance(type, DEFAULT_NAME, context); + } + + public void inject( final Object o ) { + callInContext(new ContextualCallable<Void>() { + public Void call( InternalContext context ) { + inject(o, context); + return null; + } + }); + } + + public <T> T inject( final Class<T> implementation ) { + return callInContext(new ContextualCallable<T>() { + public T call( InternalContext context ) { + return inject(implementation, context); + } + }); + } + + public <T> T getInstance( final Class<T> type, final String name ) { + return callInContext(new ContextualCallable<T>() { + public T call( InternalContext context ) { + return getInstance(type, name, context); + } + }); + } + + public <T> T getInstance( final Class<T> type ) { + return callInContext(new ContextualCallable<T>() { + public T call( InternalContext context ) { + return getInstance(type, context); + } + }); + } + + public Set<String> getInstanceNames( final Class<?> type ) { + return factoryNamesByType.get(type); + } + + ThreadLocal<Object[]> localContext = + new ThreadLocal<Object[]>() { + @Override + protected Object[] initialValue() { + return new Object[1]; + } + }; + + /** + * Looks up thread local context. Creates (and removes) a new context if necessary. + */ + <T> T callInContext( ContextualCallable<T> callable ) { + Object[] reference = localContext.get(); + if (reference[0] == null) { + reference[0] = new InternalContext(this); + try { + return callable.call((InternalContext) reference[0]); + } finally { + // Only remove the context if this call created it. + reference[0] = null; + } + } else { + // Someone else will clean up this context. + return callable.call((InternalContext) reference[0]); + } + } + + interface ContextualCallable<T> { + + T call( InternalContext context ); + } + + /** + * Gets a constructor function for a given implementation class. + */ + @SuppressWarnings("unchecked") + <T> ConstructorInjector<T> getConstructor( Class<T> implementation ) { + return constructors.get(implementation); + } + + final ThreadLocal<Object> localScopeStrategy = + new ThreadLocal<Object>(); + + public void setScopeStrategy( Scope.Strategy scopeStrategy ) { + this.localScopeStrategy.set(scopeStrategy); + } + + public void removeScopeStrategy() { + this.localScopeStrategy.remove(); + } + + /** + * Injects a field or method in a given object. + */ + interface Injector extends Serializable { + + void inject( InternalContext context, Object o ); + } + + static class MissingDependencyException extends Exception { + + MissingDependencyException( String message ) { + super(message); + } + } }