Package org.jvnet.hk2.internal

Source Code of org.jvnet.hk2.internal.Utilities

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.jvnet.hk2.internal;

import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Qualifier;
import javax.inject.Scope;
import javax.inject.Singleton;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

import org.glassfish.hk2.api.ActiveDescriptor;
import org.glassfish.hk2.api.ClassAnalyzer;
import org.glassfish.hk2.api.Context;
import org.glassfish.hk2.api.Descriptor;
import org.glassfish.hk2.api.DescriptorType;
import org.glassfish.hk2.api.DescriptorVisibility;
import org.glassfish.hk2.api.ErrorService;
import org.glassfish.hk2.api.ErrorType;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.HK2Loader;
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.MultiException;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.Proxiable;
import org.glassfish.hk2.api.ProxyCtl;
import org.glassfish.hk2.api.ProxyForSameScope;
import org.glassfish.hk2.api.Self;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.Unproxiable;
import org.glassfish.hk2.api.Unqualified;
import org.glassfish.hk2.api.UseProxy;
import org.glassfish.hk2.api.Visibility;
import org.glassfish.hk2.utilities.BuilderHelper;
import org.glassfish.hk2.utilities.NamedImpl;
import org.glassfish.hk2.utilities.reflection.Pretty;
import org.glassfish.hk2.utilities.reflection.ParameterizedTypeImpl;
import org.glassfish.hk2.utilities.reflection.ReflectionHelper;

import org.jvnet.hk2.annotations.Contract;
import org.jvnet.hk2.annotations.Optional;
import org.jvnet.hk2.annotations.Service;

/**
* This class contains a set of static utilities useful
* for implementing HK2
*
* @author jwells
*
*/
public class Utilities {
    private final static Object lock = new Object();
   
    // We don't want to hold onto these classes if they are released by others
    private static final Map<Class<?>, Set<MemberKey>> methodKeyCache = new WeakHashMap<Class<?>, Set<MemberKey>>();
    private static Map<Class<?>, Set<MemberKey>> fieldCache = new WeakHashMap<Class<?>, Set<MemberKey>>();
    private final static Map<Class<?>, Method> postConstructCache = new WeakHashMap<Class<?>, Method>();
    private final static Map<Class<?>, Method> preDestroyCache = new WeakHashMap<Class<?>, Method>();
   
    private final static String CONVENTION_POST_CONSTRUCT = "postConstruct";
    private final static String CONVENTION_PRE_DESTROY = "preDestroy";
   
    /**
     * Returns the class analyzer with the given name
     *
     * @param sli The ServiceLocator to search in.  May not be null
     * @param analyzerName The name of the analyzer (may be null for the default analyzer)
     * @param errorCollector A non-null collector of exceptions
     * @return The ClassAnalyzer corresponding to the name, or null if none was found
     */
    public static ClassAnalyzer getClassAnalyzer(
            ServiceLocatorImpl sli,
            String analyzerName,
            Collector errorCollector) {
        return sli.getAnalyzer(analyzerName, errorCollector);
    }
   
    /**
     * Gets the constructor given the implClass and analyzer.  Checks service output
     *
     * @param implClass The implementation class (not null)
     * @param analyzer The analyzer (not null)
     * @param collector A collector for errors (not null)
     * @return null on failure (collector will have failures), non-null on success
     */
    public static <T> Constructor<T> getConstructor(Class<T> implClass, ClassAnalyzer analyzer, Collector collector) {
        Constructor<T> element = null;
        try {
            element = analyzer.getConstructor(implClass);
        }
        catch (MultiException me) {
            collector.addMultiException(me);
            return element;
        }
        catch (Throwable th) {
            collector.addThrowable(th);
            return element;
        }
       
        if (element == null) {
            collector.addThrowable(new AssertionError("null return from getConstructor method of analyzer " +
                analyzer + " for class " + implClass.getName()));
            return element;
        }
       
        return element;
    }
   
    /**
     * Gets the initializer methods from the given class and analyzer.  Checks service output
     *
     * @param implClass the non-null impl class
     * @param analyzer the non-null analyzer
     * @param collector for gathering errors
     * @return a non-null set (even in error cases, check the collector)
     */
    public static Set<Method> getInitMethods(Class<?> implClass, ClassAnalyzer analyzer, Collector collector) {
        Set<Method> retVal;
        try {
            retVal = analyzer.getInitializerMethods(implClass);
        }
        catch (MultiException me) {
            collector.addMultiException(me);
            return Collections.emptySet();
        }
        catch (Throwable th) {
            collector.addThrowable(th);
            return Collections.emptySet();
        }
       
        if (retVal == null) {
            collector.addThrowable(new AssertionError("null return from getInitializerMethods method of analyzer " +
                    analyzer + " for class " + implClass.getName()));
            return Collections.emptySet();
        }
       
        return retVal;
    }
   
    /**
     * Gets the initializer fields from the given class and analyzer.  Checks service output
     *
     * @param implClass the non-null impl class
     * @param analyzer the non-null analyzer
     * @param collector for gathering errors
     * @return a non-null set (even in error cases, check the collector)
     */
    public static Set<Field> getInitFields(Class<?> implClass, ClassAnalyzer analyzer, Collector collector) {
        Set<Field> retVal;
        try {
            retVal = analyzer.getFields(implClass);
        }
        catch (MultiException me) {
            collector.addMultiException(me);
            return Collections.emptySet();
        }
        catch (Throwable th) {
            collector.addThrowable(th);
            return Collections.emptySet();
        }
       
        if (retVal == null) {
            collector.addThrowable(new AssertionError("null return from getFields method of analyzer " +
                    analyzer + " for class " + implClass.getName()));
            return Collections.emptySet();
        }
       
        return retVal;
    }
   
    /**
     * Gets the post construct from the analyzer, checking output
     *
     * @param implClass The non-null implementation class
     * @param analyzer The non-null analyzer
     * @param collector The non-null error collector
     * @return The possibly null post-construct method (check the collector for errors)
     */
    public static Method getPostConstruct(Class<?> implClass, ClassAnalyzer analyzer, Collector collector) {
        try {
            return analyzer.getPostConstructMethod(implClass);
        }
        catch (MultiException me) {
            collector.addMultiException(me);
            return null;
        }
        catch (Throwable th) {
            collector.addThrowable(th);
            return null;
        }
    }
   
    /**
     * Gets the preDestroy from the analyzer, checking output
     *
     * @param implClass The non-null implementation class
     * @param analyzer The non-null analyzer
     * @param collector The non-null error collector
     * @return The possibly null pre-destroy method (check the collector for errors)
     */
    public static Method getPreDestroy(Class<?> implClass, ClassAnalyzer analyzer, Collector collector) {
        try {
            return analyzer.getPreDestroyMethod(implClass);
        }
        catch (MultiException me) {
            collector.addMultiException(me);
            return null;
        }
        catch (Throwable th) {
            collector.addThrowable(th);
            return null;
        }
    }
   
    /**
     * This utility will return the proper implementation class, taking into account that the
     * descriptor may be a factory
     *
     * @param descriptor The descriptor (reified and not null) that will be used to find the
     * implementation
     *
     * @return The real implementation class
     */
    private static Class<?> getFactoryAwareImplementationClass(ActiveDescriptor<?> descriptor) {
        if (descriptor.getDescriptorType().equals(DescriptorType.CLASS)) {
            return descriptor.getImplementationClass();
        }

        return getFactoryProductionClass(descriptor.getImplementationClass());
    }

    /**
     * Checks that the incoming lookup type is not improper in some way
     *
     * @param checkMe  class to check
     */
    public static void checkLookupType(Class<?> checkMe) {
        if (!checkMe.isAnnotation()) return;

        // If it is in annotation we need to ensure it is either a
        // scope or a qualifier
        if (checkMe.isAnnotationPresent(Scope.class)) return;
        if (checkMe.isAnnotationPresent(Qualifier.class)) return;

        throw new IllegalArgumentException("Lookup type " + checkMe + " must be a scope or annotation");
    }

    /**
     * This is used to check on the annotation set.  It must be done under protection because the annotations may
     * attempt to discover if they are equal using getDeclaredMembers permission
     *
     * @param candidateAnnotations The candidate annotations
     * @param requiredAnnotations The required annotations
     * @return true if the candidate set contains the entire required set
     */
    /* package */
    static boolean annotationContainsAll(final Set<Annotation> candidateAnnotations, final Set<Annotation> requiredAnnotations) {
        return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {

            @Override
            public Boolean run() {
                return candidateAnnotations.containsAll(requiredAnnotations);
            }

        });

    }

    private static Field[] getDeclaredFields(final Class<?> clazz) {
        return AccessController.doPrivileged(new PrivilegedAction<Field[]>() {

            @Override
            public Field[] run() {
                return clazz.getDeclaredFields();
            }

        });

    }

    /**
     * Get the declared methods of the given class.
     *
     * @param clazz  the class
     */
    private static Method[] getDeclaredMethods(final Class<?> clazz) {
        return AccessController.doPrivileged(new PrivilegedAction<Method[]>() {

            @Override
            public Method[] run() {
                return clazz.getDeclaredMethods();
            }

        });

    }

    /**
     * Converts the type to its java form, or returns the original
     *
     * @param type The type to convert
     * @return The translated type or the type itself
     */
    public static Class<?> translatePrimitiveType(Class<?> type) {
        Class<?> translation = Constants.PRIMITIVE_MAP.get(type);
        if (translation == null) return type;
        return translation;
    }

    /**
     * Calls the list of error services for the list of errors
     *
     * @param results    the results
     * @param callThese  the services to call
     */
    public static void handleErrors(NarrowResults results, LinkedList<ErrorService> callThese) {
        Collector collector = new Collector();
        for (ErrorResults errorResult : results.getErrors()) {
            for (ErrorService eService : callThese) {
                try {
                    eService.onFailure(new ErrorInformationImpl(
                            ErrorType.FAILURE_TO_REIFY,
                            errorResult.getDescriptor(),
                            errorResult.getInjectee(),
                            errorResult.getMe()));
                } catch (MultiException me) {
                    for (Throwable th : me.getErrors()) {
                        collector.addThrowable(th);
                    }
                } catch (Throwable th) {
                    collector.addThrowable(th);
                }
            }
        }

        collector.throwIfErrors();
    }

    /**
     * Loads the class using the loader from the given descriptor or the
     * classloader of the utilities class otherwise
     *
     * @param loadMe The fully qualified class name
     * @param fromMe The descriptor to use for the loader
     * @param collector The error collector to fill in if this returns null
     * @return null on failure to load (the failure will be added to the collector)
     */
    public static Class<?> loadClass(String loadMe, Descriptor fromMe, Collector collector) {
        HK2Loader loader = fromMe.getLoader();
        if (loader == null) {
            ClassLoader cl = Utilities.class.getClassLoader();
            if (cl == null) {
                cl = ClassLoader.getSystemClassLoader();
            }

            try {
                return cl.loadClass(loadMe);
            } catch (Throwable th) {
                collector.addThrowable(th);
                return null;
            }
        }

        try {
            return loader.loadClass(loadMe);
        } catch (Throwable th) {
            if (th instanceof MultiException) {
                MultiException me = (MultiException) th;

                for (Throwable th2 : me.getErrors()) {
                    collector.addThrowable(th2);
                }
            } else {
                collector.addThrowable(th);
            }

            return null;
        }

    }

    /**
     * Load the given class for the given injectee.
     *
     * @param implementation  the impl class name string
     * @param injectee        the injectee
     *
     * @return The class represented by this implementation and injectee
     */
    public static Class<?> loadClass(String implementation, Injectee injectee) {
        ClassLoader loader;
        if (injectee != null) {
            AnnotatedElement parent = injectee.getParent();

            if (parent instanceof Constructor) {
                loader = ((Constructor<?>) parent).getDeclaringClass().getClassLoader();
            } else if (parent instanceof Method) {
                loader = ((Method) parent).getDeclaringClass().getClassLoader();
            } else if (parent instanceof Field) {
                loader = ((Field) parent).getDeclaringClass().getClassLoader();
            } else {
                loader = injectee.getClass().getClassLoader();
            }
        } else {
            loader = Utilities.class.getClassLoader();
        }

        try {
            return loader.loadClass(implementation);
        } catch (Throwable th) {
            throw new MultiException(th);
        }
    }

    /**
     * Will return the class of the injection resolver annotation type, or null if
     * no injection resolver annotation can be found
     *
     * @param desc The reified descriptor to find the injection resolution on
     * @return The annotation type for this injection resolver
     */
    @SuppressWarnings("unchecked")
    public static Class<? extends Annotation> getInjectionResolverType(ActiveDescriptor<?> desc) {
        for (Type advertisedType : desc.getContractTypes()) {
            Class<?> rawClass = ReflectionHelper.getRawClass(advertisedType);

            if (!InjectionResolver.class.equals(rawClass)) continue;

            // Found the InjectionResolver
            if (!(advertisedType instanceof ParameterizedType)) {
                return null;
            }

            Type firstType = getFirstTypeArgument(advertisedType);
            if (!(firstType instanceof Class)) {
                return null;
            }

            Class<?> retVal = (Class<?>) firstType;

            if (!Annotation.class.isAssignableFrom(retVal)) {
                return null;
            }

            return (Class<? extends Annotation>) retVal;
        }

        return null;
    }

    /**
     * This method returns the class associated with the type of the
     * factory
     *
     * @param factoryClass The non-null factory class.  May not be null
     * @return the CLASS version of what the factory produces.  Will
     * not be null
     * @throws MultiException if there was an error analyzing the class
     */
    private static Class<?> getFactoryProductionClass(Class<?> factoryClass) {
        Type factoryProvidedType = getFactoryProductionType(factoryClass);
       
        Class<?> retVal = ReflectionHelper.getRawClass(factoryProvidedType);
        if (retVal != null) return retVal;
       
        throw new MultiException(new AssertionError("Could not find true produced type of factory " + factoryClass.getName()));
    }
   
    /**
     * This method returns the type produced by a factory class
     *
     * @param factoryClass The non-null factory class.  May not be null
     * @return the type version of what the factory produces.  Will
     * not be null
     * @throws MultiException if there was an error analyzing the class
     */
    public static Type getFactoryProductionType(Class<?> factoryClass) {
        Set<Type> factoryTypes = ReflectionHelper.getTypeClosure(factoryClass,
                Collections.singleton(Factory.class.getName()));
       
        ParameterizedType parameterizedType = (ParameterizedType) factoryTypes.iterator().next();

        Type factoryProvidedType = parameterizedType.getActualTypeArguments()[0];
       
        return factoryProvidedType;
    }

    /**
     * Checks to be sure the Factory class is ok
     *
     * @param factoryClass  the class to check
     * @param collector     the exception collector
     */
    public static void checkFactoryType(Class<?> factoryClass, Collector collector) {
        for (Type type : factoryClass.getGenericInterfaces()) {
            Class<?> rawClass = ReflectionHelper.getRawClass(type);
            if (rawClass == null) continue;

            if (!Factory.class.equals(rawClass)) continue;

            Type firstType = getFirstTypeArgument(type);

            if (firstType instanceof WildcardType) {
                // This should not be possible
                collector.addThrowable(new IllegalArgumentException("The class " +
                        Pretty.clazz(factoryClass) + " has a Wildcard as its type"));
            }
        }

    }

    private static Set<Type> getAutoAdvertisedTypes(Type t) {
        LinkedHashSet<Type> retVal = new LinkedHashSet<Type>();
        retVal.add(t);

        Class<?> rawClass = ReflectionHelper.getRawClass(t);
        if (rawClass == null) return retVal;

        Type genericSuperclass = rawClass.getGenericSuperclass();
        while (genericSuperclass != null) {
            Class<?> rawSuperclass = ReflectionHelper.getRawClass(genericSuperclass);
            if (rawSuperclass == null) break;

            if (rawSuperclass.isAnnotationPresent(Contract.class)) {
                retVal.add(genericSuperclass);
            }

            genericSuperclass = rawSuperclass.getGenericSuperclass();
        }

        while (rawClass != null) {
            for (Type iface : rawClass.getGenericInterfaces()) {
                Class<?> ifaceClass = ReflectionHelper.getRawClass(iface);
                if (ifaceClass.isAnnotationPresent(Contract.class)) {
                    retVal.add(iface);
                }
            }

            rawClass = rawClass.getSuperclass();
        }

        return retVal;
    }

    /**
     * Creates a reified automatically generated descriptor
     *
     * @param clazz The class to create the desciptor for
     * @param locator The service locator for whom we are creating this
     * @return A reified active descriptor
     *
     * @throws MultiException if there was an error in the class
     * @throws IllegalArgumentException If the class is null
     * @throws IllegalStateException If the name could not be determined from the Named annotation
     */
    public static <T> AutoActiveDescriptor<T> createAutoDescriptor(Class<T> clazz, ServiceLocatorImpl locator)
            throws MultiException, IllegalArgumentException, IllegalStateException {
        if (clazz == null) throw new IllegalArgumentException();

        Collector collector = new Collector();

        ClazzCreator<T> creator;
        Set<Annotation> qualifiers;
        Set<Type> contracts;
        Class<? extends Annotation> scope;
        String name;
        Boolean proxy = null;
        Boolean proxyForSameScope = null;
        String analyzerName;

        // Qualifiers naming dance
        qualifiers = ReflectionHelper.getQualifierAnnotations(clazz);
        name = ReflectionHelper.getNameFromAllQualifiers(qualifiers, clazz);
        qualifiers = getAllQualifiers(clazz, name, collector)// Fixes the @Named qualifier if it has no value

        contracts = getAutoAdvertisedTypes(clazz);
        ScopeInfo scopeInfo = getScopeInfo(clazz, null, collector);
        scope = scopeInfo.getAnnoType();
        analyzerName = getAutoAnalyzerName(clazz);

        creator = new ClazzCreator<T>(locator, clazz);

        collector.throwIfErrors();

        Map<String, List<String>> metadata = new HashMap<String, List<String>>();
        if (scopeInfo.getScope() != null) {
            BuilderHelper.getMetadataValues(scopeInfo.getScope(), metadata);
        }

        for (Annotation qualifier : qualifiers) {
            BuilderHelper.getMetadataValues(qualifier, metadata);
        }

        UseProxy useProxy = clazz.getAnnotation(UseProxy.class);
        if (useProxy != null) {
            proxy = useProxy.value();
        }
       
        ProxyForSameScope pfss = clazz.getAnnotation(ProxyForSameScope.class);
        if (pfss != null) {
            proxyForSameScope = pfss.value();
        }

        DescriptorVisibility visibility = DescriptorVisibility.NORMAL;
        Visibility vi = clazz.getAnnotation(Visibility.class);
        if (vi != null) {
            visibility = vi.value();
        }

        AutoActiveDescriptor<T> retVal = new AutoActiveDescriptor<T>(
                clazz,
                creator,
                contracts,
                scope,
                name,
                qualifiers,
                visibility,
                0,
                proxy,
                proxyForSameScope,
                analyzerName,
                metadata);
       
        creator.initialize(retVal, analyzerName, collector);
       
        collector.throwIfErrors();
       
        return retVal;
    }

    /**
     * Pre Destroys the given object
     *
     * @param preMe pre destroys the thing
     * @param locator The non-null service locator associated with the operation (for finding the strategy)
     * @param strategy The strategy to use for analyzing the class
     */
    public static void justPreDestroy(Object preMe, ServiceLocatorImpl locator, String strategy) {
        if (preMe == null) throw new IllegalArgumentException();
       
        Collector collector = new Collector();
       
        ClassAnalyzer analyzer = getClassAnalyzer(locator, strategy, collector);
        collector.throwIfErrors();
       
        collector.throwIfErrors();

        Class<?> baseClass = preMe.getClass();

        Method preDestroy = getPreDestroy(baseClass, analyzer, collector);

        collector.throwIfErrors();

        if (preDestroy == null) return;

        try {
            ReflectionHelper.invoke(preMe, preDestroy, new Object[0]);
        } catch (Throwable e) {
            throw new MultiException(e);
        }
    }

    /**
     * Post constructs the given object
     *
     * @param postMe post constructs the thing
     * @param locator The non-null service locator associated with the operation (for finding the strategy)
     * @param strategy The strategy to use for analyzing the class
     */
    public static void justPostConstruct(Object postMe, ServiceLocatorImpl locator, String strategy) {
        if (postMe == null) throw new IllegalArgumentException();
       
        Collector collector = new Collector();
       
        ClassAnalyzer analyzer = getClassAnalyzer(locator, strategy, collector);
        collector.throwIfErrors();

        Class<?> baseClass = postMe.getClass();

        Method postConstruct = getPostConstruct(baseClass, analyzer, collector);

        collector.throwIfErrors();

        if (postConstruct == null) return;

        try {
            ReflectionHelper.invoke(postMe, postConstruct, new Object[0]);
        } catch (Throwable e) {
            throw new MultiException(e);
        }
    }

    /**
     * Just creates the thing, doesn't try to do anything else
     * @param injectMe The object to inject into
     * @param locator The locator to find the injection points with
     * @param strategy The strategy to use for analyzing the class
     */
    public static void justInject(Object injectMe, ServiceLocatorImpl locator, String strategy) {
        if (injectMe == null) throw new IllegalArgumentException();
       
        Collector collector = new Collector();
       
        ClassAnalyzer analyzer = getClassAnalyzer(locator, strategy, collector);
        collector.throwIfErrors();
       
        Class<?> baseClass = injectMe.getClass();

        Set<Field> fields = Utilities.getInitFields(baseClass, analyzer, collector);
        Set<Method> methods = Utilities.getInitMethods(baseClass, analyzer, collector);

        collector.throwIfErrors();

        for (Field field : fields) {
            InjectionResolver<?> resolver = getInjectionResolver(locator, field);

            List<Injectee> injecteeFields = Utilities.getFieldInjectees(field, null);

            validateSelfInjectees(null, injecteeFields, collector);
            collector.throwIfErrors();

            Injectee injectee = injecteeFields.get(0);

            Object fieldValue = resolver.resolve(injectee, null);
           
            try {
                ReflectionHelper.setField(field, injectMe, fieldValue);
            }
            catch (Throwable th) {
                throw new MultiException(th);
            }
        }

        for (Method method : methods) {
            List<Injectee> injectees = Utilities.getMethodInjectees(method, null);

            validateSelfInjectees(null, injectees, collector);
            collector.throwIfErrors();

            Object args[] = new Object[injectees.size()];

            for (Injectee injectee : injectees) {
                InjectionResolver<?> resolver = getInjectionResolver(locator, injectee);
                args[injectee.getPosition()] = resolver.resolve(injectee, null);
            }

            try {
                ReflectionHelper.invoke(injectMe, method, args);
            } catch (Throwable e) {
                throw new MultiException(e);
            }
        }

    }

    /**
     * Just creates the thing, doesn't try to do anything else
     * @param createMe The thing to create
     * @param locator The locator to find the injection points with
     * @param strategy The strategy to use for analyzing the class
     * @return The constructed thing, no further injection is performed
     */
    @SuppressWarnings("unchecked")
    public static <T> T justCreate(Class<T> createMe, ServiceLocatorImpl locator, String strategy) {
        if (createMe == null) throw new IllegalArgumentException();
       
        Collector collector = new Collector();
        ClassAnalyzer analyzer = getClassAnalyzer(locator, strategy, collector);
        collector.throwIfErrors();

        Constructor<?> c = getConstructor(createMe, analyzer, collector);

        collector.throwIfErrors();

        List<Injectee> injectees = getConstructorInjectees(c, null);

        validateSelfInjectees(null, injectees, collector);
        collector.throwIfErrors();

        Object args[] = new Object[injectees.size()];

        for (Injectee injectee : injectees) {
            InjectionResolver<?> resolver = getInjectionResolver(locator, injectee);
            args[injectee.getPosition()] = resolver.resolve(injectee, null);
        }

        try {
          return (T) ReflectionHelper.makeMe(c, args);
        } catch (Throwable th) {
            throw new MultiException(th);
        }
    }

    /**
     * Returns all the interfaces the proxy must implement
     * @param contracts All of the advertised contracts
     * @return The array of contracts to add to the proxy
     */
    public static Class<?>[] getInterfacesForProxy(Set<Type> contracts) {
        LinkedList<Class<?>> retVal = new LinkedList<Class<?>>();
        retVal.add(ProxyCtl.class);    // Every proxy implements this interface

        for (Type type : contracts) {
            Class<?> rawClass = ReflectionHelper.getRawClass(type);
            if (rawClass == null) continue;
            if (!rawClass.isInterface()) continue;

            retVal.add(rawClass);
        }

        return retVal.toArray(new Class<?>[retVal.size()]);
    }

    /**
     * Returns true if this scope is proxiable
     *
     * @param scope The scope annotation to test
     * @return true if this must be proxied
     */
    public static boolean isProxiableScope(Class<? extends Annotation> scope) {
        return scope.isAnnotationPresent(Proxiable.class);
    }

    /**
     * Returns true if this scope is unproxiable
     *
     * @param scope The scope annotation to test
     * @return true if this must be proxied
     */
    public static boolean isUnproxiableScope(Class<? extends Annotation> scope) {
        if (scope.isAnnotationPresent(Unproxiable.class)) return true;
        return false;
    }

    /**
     * This method determines whether or not the descriptor should be proxied.
     * The given descriptor must be reified and valid.
     *
     * @param desc A non-null, reified ActiveDescriptor
     * @param injectee The injectee where this is being injected if known,
     * or null if not known
     * @return true if this descriptor must be proxied, false otherwise
     */
    public static boolean isProxiable(ActiveDescriptor<?> desc, Injectee injectee) {
        Boolean directed = desc.isProxiable();
       
        if (directed != null) {
            if (injectee == null) {
                // No other scope to compare to
                return directed;
            }
           
            if (!directed) {
                // Doesn't matter what the other scope is, not proxied
                return false;
            }
           
            ActiveDescriptor<?> injecteeDescriptor = injectee.getInjecteeDescriptor();
            if (injecteeDescriptor == null) {
                // No other scope to compare to
                return true;
            }
           
            Boolean sameScope = desc.isProxyForSameScope();
            if (sameScope == null || sameScope) {
                // The default case is to be lazy
                return true;
            }
           
            // OK, same scope is false, forced Proxy is true,
            // now we need to see if the scopes of the two
            // things are in fact the same
            if (desc.getScope().equals(injecteeDescriptor.getScope())) {
                // The scopes are the same, proxy-for-same-scope is false,
                // so the answer is no, do not proxy
                return false;
            }
           
            // The scopes are different, deal with it
            return true;
        }

        if (!isProxiableScope(desc.getScopeAnnotation())) return false;
       
        if (injectee == null) {
            // No other scope to compare to
            return true;
        }
       
        ActiveDescriptor<?> injecteeDescriptor = injectee.getInjecteeDescriptor();
        if (injecteeDescriptor == null) {
            // No other scope to compare to
            return true;
        }
       
        Boolean proxyForSameScope = desc.isProxyForSameScope();
        Proxiable proxiable = desc.getScopeAnnotation().getAnnotation(Proxiable.class);
       
        if (proxyForSameScope != null) {
            if (proxyForSameScope) {
              return true;
            }
        }
        else if (proxiable == null || proxiable.proxyForSameScope()) {
            // The default case is to be lazy
            return true;
        }
       
        // OK, same scope is false, and we are in Proxiable scope,
        // now we need to see if the scopes of the two
        // things are in fact the same
        if (desc.getScope().equals(injecteeDescriptor.getScope())) {
            // The scopes are the same, proxy-for-same-scope is false,
            // so the answer is no, do not proxy
           
            return false;
        }
       
        // The scopes are different, deal with it
        return true;
    }

    /**
     * Returns the first thing found in the set
     *
     * @param set The set from which to get the first element
     * @return the first thing found in the set
     */
    public static <T> T getFirstThingInList(List<T> set) {
        for (T t : set) {
            return t;
        }

        return null;
    }

    /**
     * Get all fields on this class and all subclasses
     *
     * @param clazz The class to inspect
     * @return A set of all the fields on this class
     */
    private static Set<Field> getAllFields(Class<?> clazz, Collector collector) {
        HashSet<Field> retVal = new HashSet<Field>();

        HashSet<MemberKey> keys = new HashSet<MemberKey>();

        getAllFieldKeys(clazz, keys, collector);

        for (MemberKey key : keys) {
            retVal.add((Field) key.getBackingMember());
        }

        return retVal;
    }
   
   

    private static void getAllFieldKeys(Class<?> clazz, Set<MemberKey> currentFields, Collector collector) {
        if (clazz == null) return;
       
        Set<MemberKey> discovered;
        synchronized (lock) {
            discovered = fieldCache.get(clazz);
        }
       
        if (discovered != null) {
            currentFields.addAll(discovered);
            return;
        }

        // Do superclasses first, so that inherited methods are
        // overriden in the set
        getAllFieldKeys(clazz.getSuperclass(), currentFields, collector);

        try {
            for (Field field : getDeclaredFields(clazz)) {
                currentFields.add(new MemberKey(field, false, false));
            }
           
            synchronized (lock) {
                fieldCache.put(clazz, new HashSet<MemberKey>(currentFields));
            }
        } catch (Throwable th) {
            collector.addThrowable(new IllegalStateException("Error while getting fields of class " + clazz.getName(), th));
        }

    }

    /**
     * Returns a constant ActiveDescriptor for the basic ServiceLocator
     *
     * @param locator The service locator to get the ActiveDescriptor for
     * @return An active descriptor specifically for the ServiceLocator
     */
    public static ActiveDescriptor<ServiceLocator> getLocatorDescriptor(ServiceLocator locator) {
        HashSet<Type> contracts = new HashSet<Type>();
        contracts.add(ServiceLocator.class);

        Set<Annotation> qualifiers = Collections.emptySet();

        ActiveDescriptor<ServiceLocator> retVal =
                new ConstantActiveDescriptor<ServiceLocator>(
                        locator,
                        contracts,
                        PerLookup.class,
                        null,
                        qualifiers,
                        DescriptorVisibility.LOCAL,
                        0,
                        null,
                        null,
                        null,
                        locator.getLocatorId(),
                        null);

        return retVal;
    }

    /**
     * Creates a Three Thirty constant active descriptor
     *
     * @param locator The service locator to get the ActiveDescriptor for
     * @return An active descriptor specifically for the ServiceLocator
     */
    public static ActiveDescriptor<InjectionResolver<Inject>> getThreeThirtyDescriptor(
            ServiceLocatorImpl locator) {
        ThreeThirtyResolver threeThirtyResolver = new ThreeThirtyResolver(locator);

        HashSet<Type> contracts = new HashSet<Type>();

        Type actuals[] = new Type[1];
        actuals[0] = Inject.class;

        contracts.add(new ParameterizedTypeImpl(InjectionResolver.class, actuals));

        Set<Annotation> qualifiers = new HashSet<Annotation>();
        qualifiers.add(new NamedImpl(InjectionResolver.SYSTEM_RESOLVER_NAME));

        ActiveDescriptor<InjectionResolver<Inject>> retVal =
                new ConstantActiveDescriptor<InjectionResolver<Inject>>(
                        threeThirtyResolver,
                        contracts,
                        Singleton.class,
                        InjectionResolver.SYSTEM_RESOLVER_NAME,
                        qualifiers,
                        DescriptorVisibility.NORMAL,
                        0,
                        null,
                        null,
                        null,
                        locator.getLocatorId(),
                        null);

        return retVal;
    }

    /**
     * Validates the constructors of the annotated type and returns the
     * producer for the annotatedType (if there is no valid producer
     * constructor then this method returns null)
     *
     * @param annotatedType The type to find the producer constructor
     * @param locator The service locator to use when analyzing constructors
     * @param collector The error collector
     * @return The producer constructor or null if the type has no valid
     * producer constructor
     */
    public static Constructor<?> findProducerConstructor(Class<?> annotatedType, ServiceLocatorImpl locator, Collector collector) {
        Constructor<?> zeroArgConstructor = null;
        Constructor<?> aConstructorWithInjectAnnotation = null;

        Set<Constructor<?>> allConstructors = getAllConstructors(annotatedType);
        for (Constructor<?> constructor : allConstructors) {

            Type rawParameters[] = constructor.getGenericParameterTypes();
            if (rawParameters.length <= 0) {
                zeroArgConstructor = constructor;
            }

            if (hasInjectAnnotation(locator, constructor, true)) {
                if (aConstructorWithInjectAnnotation != null) {
                    collector.addThrowable(new IllegalArgumentException("There is more than one constructor on class " +
                            Pretty.clazz(annotatedType)));
                    return null;
                }

                aConstructorWithInjectAnnotation = constructor;
            }

            if (!isProperConstructor(constructor)) {
                collector.addThrowable(new IllegalArgumentException("The constructor for " +
                        Pretty.clazz(annotatedType) + " may not have an annotation as a parameter"));
                return null;

            }

        }

        if (aConstructorWithInjectAnnotation != null) {
            return aConstructorWithInjectAnnotation;
        }

        if (zeroArgConstructor == null) {
            collector.addThrowable(new NoSuchMethodException("The class " + Pretty.clazz(annotatedType) +
                    " has no constructor marked @Inject and no zero argument constructor"));
            return null;
        }

        return zeroArgConstructor;
    }

    private static boolean isProperConstructor(Constructor<?> c) {
        for (Class<?> pClazz : c.getParameterTypes()) {
            if (pClazz.isAnnotation()) return false;
        }

        return true;
    }

    /**
     * Gets the first type argument if this is a parameterized
     * type, otherwise it returns Object.class
     *
     * @param type The type to find the first type argument on
     * @return If this is a class, Object.class. If this is a parameterized
     * type, the type of the first actual argument
     */
    public static Type getFirstTypeArgument(Type type) {
        if (type instanceof Class) {
            return Object.class;
        }

        if (!(type instanceof ParameterizedType)) return Object.class;

        ParameterizedType pt = (ParameterizedType) type;
        Type arguments[] = pt.getActualTypeArguments();
        if (arguments.length <= 0) return Object.class;

        return arguments[0];
    }

    /**
     * Gets all the constructors for a given class
     *
     * @param clazz The class to find the constructors of
     * @return A set of Constructors for the given class
     */
    private static Set<Constructor<?>> getAllConstructors(Class<?> clazz) {
        HashSet<Constructor<?>> retVal = new HashSet<Constructor<?>>();

        HashSet<MemberKey> keys = new HashSet<MemberKey>();

        getAllConstructorKeys(clazz, keys);

        for (MemberKey key : keys) {
            retVal.add((Constructor<?>) key.getBackingMember());
        }

        return retVal;
    }

    private static void getAllConstructorKeys(final Class<?> clazz, Set<MemberKey> currentConstructors) {
        if (clazz == null) return;

        // Constructors for the superclass do not equal constructors for this class
        Constructor<?> constructors[] = AccessController.doPrivileged(new PrivilegedAction<Constructor<?>[]>() {

            @Override
            public Constructor<?>[] run() {
                return clazz.getDeclaredConstructors();
            }
           
        });

        for (Constructor<?> constructor : constructors) {
            currentConstructors.add(new MemberKey(constructor, false, false));
        }

    }

    /**
     * Gets all methods, public, private etc on this class and on all
     * subclasses
     *
     * @param clazz The class to check out
     * @return A set of all methods on this class
     */
    private static Set<Method> getAllMethods(Class<?> clazz) {
        HashSet<Method> retVal = new HashSet<Method>();

        HashSet<MemberKey> keys = new HashSet<MemberKey>();

        getAllMethodKeys(clazz, keys);

        Method postConstructMethod = null;
        Method preDestroyMethod = null;
        for (MemberKey key : keys) {
            retVal.add((Method) key.getBackingMember());
            if (key.isPostConstruct()) {
                postConstructMethod = (Method) key.getBackingMember();
            }
            if (key.isPreDestroy()) {
                preDestroyMethod = (Method) key.getBackingMember();
            }
        }
       
        synchronized (lock) {
            // It is ok for postConstructMethod to be null
            postConstructCache.put(clazz, postConstructMethod);
           
            // It is ok for preDestroyMethod to be null
            preDestroyCache.put(clazz, preDestroyMethod);
        }

        return retVal;
    }
   
    private static boolean isPostConstruct(Method m) {
        if (m.isAnnotationPresent(PostConstruct.class)) return true;
       
        if (m.getParameterTypes().length != 0) return false;
        return CONVENTION_POST_CONSTRUCT.equals(m.getName());
    }
   
    private static boolean isPreDestroy(Method m) {
        if (m.isAnnotationPresent(PreDestroy.class)) return true;
       
        if (m.getParameterTypes().length != 0) return false;
        return CONVENTION_PRE_DESTROY.equals(m.getName());
    }

    private static void getAllMethodKeys(Class<?> clazz, Set<MemberKey> currentMethods) {
        if (clazz == null) return;
       
        Set<MemberKey> discoveredMethods;
        synchronized (lock) {
            discoveredMethods = methodKeyCache.get(clazz);
        }
       
        if (discoveredMethods != null) {
            currentMethods.addAll(discoveredMethods);
            return;
        }

        // Do superclasses first, so that inherited methods are
        // overriden in the set
        getAllMethodKeys(clazz.getSuperclass(), currentMethods);

        for (Method method : getDeclaredMethods(clazz)) {
            boolean isPostConstruct = isPostConstruct(method);
            boolean isPreDestroy = isPreDestroy(method);
           
            currentMethods.add(new MemberKey(method, isPostConstruct, isPreDestroy));
        }
       
        synchronized (lock) {
            methodKeyCache.put(clazz, new HashSet<MemberKey>(currentMethods));
        }
    }

    /**
     * Get all the initializer methods of the annotatedType.  If there are definitional
     * errors they will be put into the errorCollector (so as to get all the errors
     * at one shot)
     *
     * @param annotatedType The type to find the errors in
     * @param locator The locator to use when analyzing methods
     * @param errorCollector The collector to add errors to
     * @return A possibly empty but never null set of initializer methods
     */
    public static Set<Method> findInitializerMethods(
            Class<?> annotatedType,
            ServiceLocatorImpl locator,
            Collector errorCollector) {
        HashSet<Method> retVal = new HashSet<Method>();

        for (Method method : getAllMethods(annotatedType)) {
            if (!hasInjectAnnotation(locator, method, true)) {
                // Not an initializer method
                continue;
            }

            if (!isProperMethod(method)) {
                errorCollector.addThrowable(new IllegalArgumentException(
                        "An initializer method " + Pretty.method(method) +
                                " is static, abstract or has a parameter that is an annotation"));
                continue;
            }

            retVal.add(method);
        }

        return retVal;
    }

    /**
     * Will find all the initialize fields in the class
     *
     * @param annotatedType The class to search for fields
     * @param locator The locator to use when analyzing the class
     * @param errorCollector The error collector
     * @return A non-null but possibly empty set of initializer fields
     */
    public static Set<Field> findInitializerFields(Class<?> annotatedType,
                                                   ServiceLocatorImpl locator,
                                                   Collector errorCollector) {
        HashSet<Field> retVal = new HashSet<Field>();

        for (Field field : getAllFields(annotatedType, errorCollector)) {
            if (!hasInjectAnnotation(locator, field, false)) {
                // Not an initializer field
                continue;
            }

            if (!isProperField(field)) {
                errorCollector.addThrowable(new IllegalArgumentException("The field " +
                        Pretty.field(field) + " may not be static, final or have an Annotation type"));
                continue;
            }

            retVal.add(field);
        }

        return retVal;
    }

    /**
     * Checks whether an annotated element has any annotation that was used for the injection
     *
     * @param locator The service locator to use (as it will get all
     * the annotations that were added on as well as the normal Inject)
     * @param annotated  the annotated element
     * @param checkParams  check the params if true
     * @return True if element contains at least one inject annotation
     */
    private static boolean hasInjectAnnotation(ServiceLocatorImpl locator, AnnotatedElement annotated, boolean checkParams) {
        for (Annotation anno : annotated.getAnnotations()) {
            if (locator.isInjectAnnotation(anno)) {
                return true;
            }
        }

        if (!checkParams) return false;

        boolean isConstructor;
        Annotation allAnnotations[][];
        if (annotated instanceof Method) {
            Method m = (Method) annotated;

            isConstructor = false;
            allAnnotations = m.getParameterAnnotations();
        } else if (annotated instanceof Constructor) {
            Constructor<?> c = (Constructor<?>) annotated;

            isConstructor = true;
            allAnnotations = c.getParameterAnnotations();
        } else {
            return false;
        }

        for (Annotation allParamAnnotations[] : allAnnotations) {
            for (Annotation paramAnno : allParamAnnotations) {
                if (locator.isInjectAnnotation(paramAnno, isConstructor)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Gets the annotation that was used for the injection
     *
     * @param locator The service locator to use (as it will get all
     * the annotations that were added on as well as the normal Inject)
     * @param annotated the annotated annotated
     * @param checkParams  check the params if true
     * @param position index of constructor or method parameter which which will be checked
     *                 for inject annotations. The {@code position} parameter is only used when
     *                 {@code annotated} is method or constructor otherwise the value will be ignored.
     * @return The annotation that is the inject annotation, or null
     * if no inject annotation was found
     */
    private static Annotation getInjectAnnotation(ServiceLocatorImpl locator, AnnotatedElement annotated,
                                                  boolean checkParams, int position) {

        if (checkParams) {

            boolean isConstructor = false;
            boolean hasParams = false;
            Annotation allAnnotations[][] = null;
            if (annotated instanceof Method) {
                Method m = (Method) annotated;

                isConstructor = false;
                allAnnotations = m.getParameterAnnotations();
                hasParams = true;
            } else if (annotated instanceof Constructor) {
                Constructor<?> c = (Constructor<?>) annotated;

                isConstructor = true;
                allAnnotations = c.getParameterAnnotations();
                hasParams = true;
            }

            if (hasParams) {
                for (Annotation paramAnno : allAnnotations[position]) {
                    if (locator.isInjectAnnotation(paramAnno, isConstructor)) {
                        return paramAnno;
                    }
                }
            }
        }

        for (Annotation anno : annotated.getAnnotations()) {
            if (locator.isInjectAnnotation(anno)) {
                return anno;
            }
        }

        return null;
    }

    private static boolean isProperMethod(Method member) {
        if (ReflectionHelper.isStatic(member)) return false;
        if (isAbstract(member)) return false;
        for (Class<?> paramClazz : member.getParameterTypes()) {
            if (paramClazz.isAnnotation()) {
                return false;
            }
        }

        return true;
    }

    private static boolean isProperField(Field field) {
        if (ReflectionHelper.isStatic(field)) return false;
        if (isFinal(field)) return false;
        Class<?> type = field.getType();
        return !type.isAnnotation();
    }

    /**
     * Returns true if the underlying member is private
     *
     * @param member The non-null member to test
     * @return true if the member is private
     */
    public static boolean isPrivate(Member member) {
        int modifiers = member.getModifiers();

        return ((modifiers & Modifier.PRIVATE) != 0);
    }

    /**
     * Returns true if the underlying member is abstract
     *
     * @param member The non-null member to test
     * @return true if the member is abstract
     */
    public static boolean isAbstract(Member member) {
        int modifiers = member.getModifiers();

        return ((modifiers & Modifier.ABSTRACT) != 0);
    }

    /**
     * Returns true if the underlying member is abstract
     *
     * @param member The non-null member to test
     * @return true if the member is abstract
     */
    public static boolean isFinal(Member member) {
        int modifiers = member.getModifiers();

        return ((modifiers & Modifier.FINAL) != 0);
    }
   
    /**
     * Gets the analyzer name from the Service annotation
     *
     * @param c The class to get the analyzer name from
     * @return The name of the analyzer (null for default)
     */
    public static String getAutoAnalyzerName(Class<?> c) {
        Service s = c.getAnnotation(Service.class);
        if (s == null) return null;
       
        return s.analyzer();
    }

    @SuppressWarnings("unchecked")
    private static ScopeInfo getScopeInfo(
            AnnotatedElement annotatedGuy,
            Descriptor defaultScope,
            Collector collector) {
        AnnotatedElement topLevelElement = annotatedGuy;

        Annotation winnerScope = null;
        while (annotatedGuy != null) {
            Annotation current = internalGetScopeAnnotationType(
                    annotatedGuy,
                    collector);
            if (current != null) {
                if (annotatedGuy.equals(topLevelElement)) {
                    // We found a winner, no matter the inherited state
                    winnerScope = current;
                    break;
                } else {
                    if (current.annotationType().isAnnotationPresent(Inherited.class)) {
                        winnerScope = current;
                        break;
                    }

                    // This non-inherited annotation wipes out all scopes above it
                    break;
                }
            }

            if (annotatedGuy instanceof Class) {
                annotatedGuy = ((Class<?>) annotatedGuy).getSuperclass();
            } else {
                Method theMethod = (Method) annotatedGuy;
                Class<?> methodClass = theMethod.getDeclaringClass();

                annotatedGuy = null;
                Class<?> methodSuperclass = methodClass.getSuperclass();
                while (methodSuperclass != null) {
                    if (Factory.class.isAssignableFrom(methodSuperclass)) {
                        annotatedGuy = getFactoryProvideMethod(methodSuperclass);
                        break;
                    }

                    methodSuperclass = methodSuperclass.getSuperclass();
                }
            }
        }

        if (winnerScope != null) {
            return new ScopeInfo(winnerScope, winnerScope.annotationType());
        }


        if (topLevelElement.isAnnotationPresent(Service.class)) {
            return new ScopeInfo(null, Singleton.class);
        }

        if (defaultScope != null && defaultScope.getScope() != null) {
            Class<? extends Annotation> descScope = (Class<? extends Annotation>)
                    loadClass(defaultScope.getScope(), defaultScope, collector);
            if (descScope != null) {
                return new ScopeInfo(null, descScope);
            }
        }

        return new ScopeInfo(null, PerLookup.class);

    }

    /**
     * Returns the scope of this thing
     *
     * @param fromThis The annotated class or producer method
     * @param defaultScope The default scope if none other can be found
     * @return The scope of this class or producer method.  If no scope is
     * found will return the dependent scope
     */
    public static Class<? extends Annotation> getScopeAnnotationType(
            Class<?> fromThis,
            Descriptor defaultScope) {
        Collector collector = new Collector();

        ScopeInfo si = getScopeInfo(fromThis, defaultScope, collector);

        collector.throwIfErrors();

        return si.getAnnoType();
    }

    /**
     * Returns the scope of this thing
     *
     * @param annotatedGuy The annotated class or producer method
     * @param defaultScope The default scope if none other can be found
     * @param collector The error collector
     * @return The scope of this class or producer method.  If no scope is
     * found will return the dependent scope
     */
    public static Class<? extends Annotation> getScopeAnnotationType(
            AnnotatedElement annotatedGuy,
            Descriptor defaultScope,
            Collector collector) {
        ScopeInfo si = getScopeInfo(annotatedGuy, defaultScope, collector);
        return si.getAnnoType();
    }

    /**
     * This returns the scope annotation on this class *itself*, and no other
     * classes (like, not subclasses).
     */
    private static Annotation internalGetScopeAnnotationType(
            AnnotatedElement annotatedGuy,
            Collector collector) {
        boolean epicFail = false;
        Annotation retVal = null;
        for (Annotation annotation : annotatedGuy.getDeclaredAnnotations()) {
            if (annotation.annotationType().isAnnotationPresent(Scope.class)) {
                if (retVal != null) {
                    collector.addThrowable(new IllegalArgumentException("The type " + annotatedGuy +
                            " may not have more than one scope.  It has at least " +
                            Pretty.clazz(retVal.annotationType()) +
                            " and " + Pretty.clazz(annotation.annotationType())));
                    epicFail = true;
                    continue;
                }

                retVal = annotation;
            }
        }

        if (epicFail) return null;

        return retVal;
    }

    /**
     * Returns an injection resolver for the injectee
     *
     * @param locator The locator to use when finding the resolver
     * @param injectee Injectee from which the annotation should be extracted
     * @return Injection resolver used to resolve the injection for the injectee
     * @throws IllegalStateException If we could not find a valid resolver
     */
    public static InjectionResolver<?> getInjectionResolver(
            ServiceLocatorImpl locator, Injectee injectee) throws IllegalStateException {
        return getInjectionResolver(locator, injectee.getParent(), injectee.getPosition());

    }

    private static InjectionResolver<?> getInjectionResolver(
            ServiceLocatorImpl locator, AnnotatedElement annotatedGuy, int position) throws IllegalStateException {
        boolean methodOrConstructor = annotatedGuy instanceof Method || annotatedGuy instanceof Constructor<?>;
        Annotation injectAnnotation = getInjectAnnotation(locator, annotatedGuy, methodOrConstructor, position);

        Class<? extends Annotation> injectType = (injectAnnotation == null) ?
                Inject.class : injectAnnotation.annotationType();

        InjectionResolver<?> retVal = locator.getInjectionResolver(injectType);
        if (retVal == null) {
            // Not possible to get here, we only are here if we already found a resolver
            throw new IllegalStateException("There is no installed injection resolver for " +
                    Pretty.clazz(injectType) + " for type " + annotatedGuy);
        }

        return retVal;
    }

    /**
     * Returns an injection resolver for this AnnotatedElement. The method cannot be used for constructors
     * or methods.
     *
     * @param locator The locator to use when finding the resolver
     * @param annotatedGuy The annotated class or producer method
     * @return The scope of this class or producer method.  If no scope is
     * found will return the dependent scope
     * @throws IllegalStateException If we could not find a valid resolver
     */
    public static InjectionResolver<?> getInjectionResolver(
            ServiceLocatorImpl locator, AnnotatedElement annotatedGuy) throws IllegalStateException {
        if (annotatedGuy instanceof Method || annotatedGuy instanceof Constructor<?>) {
            throw new IllegalArgumentException("Annotated element '" + annotatedGuy + "' cannot be Method neither Constructor.");
        }
        return getInjectionResolver(locator, annotatedGuy, -1);
    }

    private final static String PROVIDE_METHOD = "provide";

    /**
     * This method will retrieve the provide method from a Factory
     *
     * @param clazz This class must implement factory
     * @return The provide method from this class
     */
    public static Method getFactoryProvideMethod(Class<?> clazz) {
        try {
            return clazz.getMethod(PROVIDE_METHOD);
        } catch (NoSuchMethodException e) {
            return null;
        }
    }

    /**
     * Returns the default name if one can be found.  Will only work on
     * classes and methods
     *
     * @param parent The parent annotated element
     * @param collector For errors
     * @return null if there is no default name (no Named)
     */
    public static String getDefaultNameFromMethod(Method parent, Collector collector) {
        Named named = parent.getAnnotation(Named.class);
        if (named == null) {
            return null;
        }

        if (named.value() == null || named.value().equals("")) {
            collector.addThrowable(new IllegalArgumentException(
                    "@Named on the provide method of a factory must have an explicit value"));
        }

        return named.value();
    }

    /**
     * Returns the full set of qualifier annotations on this class
     *
     * @param annotatedGuy The element we are searching for qualifiers
     * @param name The name this element must have
     * @param collector The error collector
     * @return A non-null but possibly empty set of qualifiers
     */
    public static Set<Annotation> getAllQualifiers(
            AnnotatedElement annotatedGuy,
            String name,
            Collector collector) {

        Named namedQualifier = null;
        Set<Annotation> retVal = ReflectionHelper.getQualifierAnnotations(annotatedGuy);
        for (Annotation anno : retVal) {
            if (anno instanceof Named) {
                namedQualifier = (Named) anno;
                break;
            }
        }

        if (name == null) {
            if (namedQualifier != null) {
                collector.addThrowable(new IllegalArgumentException("No name was in the descriptor, but this element(" +
                        annotatedGuy + " has a Named annotation with value: " + namedQualifier.value()));

                retVal.remove(namedQualifier);
            }

            return retVal;
        }

        if (namedQualifier == null || namedQualifier.value().equals("")) {
            if (namedQualifier != null) {
                retVal.remove(namedQualifier);
            }

            namedQualifier = new NamedImpl(name);

            retVal.add(namedQualifier);
        }

        if (!name.equals(namedQualifier.value())) {
            collector.addThrowable(new IllegalArgumentException("The class had an @Named qualifier that was inconsistent." +
                    "  The expected name is " + name +
                    " but the annotation has name " + namedQualifier.value()));
        }

        return retVal;
    }


    private static Set<Annotation> getAllQualifiers(
            Annotation memberAnnotations[]) {

        HashSet<Annotation> retVal = new HashSet<Annotation>();
        for (Annotation annotation : memberAnnotations) {
            if (ReflectionHelper.isAnnotationAQualifier(annotation)) {
                retVal.add(annotation);
            }
        }

        return retVal;
    }

    private static boolean isOptional(
            Annotation memberAnnotations[]) {

        for (Annotation annotation : memberAnnotations) {
            if (annotation.annotationType().equals(Optional.class)) {
                return true;
            }
        }

        return false;
    }

    private static boolean isSelf(
            Annotation memberAnnotations[]) {

        for (Annotation annotation : memberAnnotations) {
            if (annotation.annotationType().equals(Self.class)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Returns all the injectees for a constructor
     * @param c The constructor to analyze
     * @param injecteeDescriptor The descriptor of the injectee
     * @return the list (in order) of parameters to the constructor
     */
    public static List<Injectee> getConstructorInjectees(Constructor<?> c, ActiveDescriptor<?> injecteeDescriptor) {
        Type genericTypeParams[] = c.getGenericParameterTypes();
        Annotation paramAnnotations[][] = c.getParameterAnnotations();
        Unqualified unqualified = c.getAnnotation(Unqualified.class);

        List<Injectee> retVal = new LinkedList<Injectee>();

        for (int lcv = 0; lcv < genericTypeParams.length; lcv++) {
            retVal.add(new InjecteeImpl(genericTypeParams[lcv],
                    getAllQualifiers(paramAnnotations[lcv]),
                    lcv,
                    c,
                    isOptional(paramAnnotations[lcv]),
                    isSelf(paramAnnotations[lcv]),
                    unqualified,
                    injecteeDescriptor));
        }

        return retVal;
    }

    /**
     * Returns all the injectees for a constructor
     * @param c The constructor to analyze
     * @param injecteeDescriptor The descriptor of the injectee
     * @return the list (in order) of parameters to the constructor
     */
    public static List<Injectee> getMethodInjectees(Method c, ActiveDescriptor<?> injecteeDescriptor) {
        Type genericTypeParams[] = c.getGenericParameterTypes();
        Annotation paramAnnotations[][] = c.getParameterAnnotations();
        Unqualified unqualified = c.getAnnotation(Unqualified.class);

        List<Injectee> retVal = new LinkedList<Injectee>();

        for (int lcv = 0; lcv < genericTypeParams.length; lcv++) {
            retVal.add(new InjecteeImpl(genericTypeParams[lcv],
                    getAllQualifiers(paramAnnotations[lcv]),
                    lcv,
                    c,
                    isOptional(paramAnnotations[lcv]),
                    isSelf(paramAnnotations[lcv]),
                    unqualified,
                    injecteeDescriptor));
        }

        return retVal;
    }
   
    private static Set<Annotation> getFieldAdjustedQualifierAnnotations(Field f) {
        Set<Annotation> unadjustedAnnotations = ReflectionHelper.getQualifierAnnotations(f);
       
        // The getQualifierAnnotations will NOT add a Named annotation that has no
        // value.  So we must now determine if that is the case, and if so add
        // our own NamedImpl based on the name of the field
        Named n = f.getAnnotation(Named.class);
        if (n == null) return unadjustedAnnotations;
       
        if (n.value() == null || "".equals(n.value())) {
            unadjustedAnnotations.add(new NamedImpl(f.getName()));
        }
       
        return unadjustedAnnotations;
    }

    /**
     * Returns the injectees for a field
     * @param f The field to analyze
     * @param injecteeDescriptor The descriptor of the injectee
     * @return the list (in order) of parameters to the constructor
     */
    public static List<Injectee> getFieldInjectees(Field f, ActiveDescriptor<?> injecteeDescriptor) {
        List<Injectee> retVal = new LinkedList<Injectee>();
        Unqualified unqualified = f.getAnnotation(Unqualified.class);

        retVal.add(new InjecteeImpl(f.getGenericType(),
                getFieldAdjustedQualifierAnnotations(f),
                -1,
                f,
                isOptional(f.getAnnotations()),
                isSelf(f.getAnnotations()),
                unqualified,
                injecteeDescriptor));

        return retVal;
    }

    /**
     * This method validates a list of injectees to ensure that any self injectees have
     * the proper set of requirements.  It adds IllegalArgumentExceptions to the collector
     * if it finds errors
     *
     * @param givenDescriptor The descriptor associated with this injectee, or null if there are none
     * @param injectees The list of injectees to check.  Only self injectees are validates
     * @param collector The collector to add any errors to
     */
    public static void validateSelfInjectees(ActiveDescriptor<?> givenDescriptor,
                                             List<Injectee> injectees,
                                             Collector collector) {

        for (Injectee injectee : injectees) {
            if (!injectee.isSelf()) continue;

            Class<?> requiredRawClass = ReflectionHelper.getRawClass(injectee.getRequiredType());
            if (requiredRawClass == null || !(ActiveDescriptor.class.equals(requiredRawClass))) {
                collector.addThrowable(new IllegalArgumentException("Injection point " + injectee +
                        " does not have the required type of ActiveDescriptor"));
            }

            if (injectee.isOptional()) {
                collector.addThrowable(new IllegalArgumentException("Injection point " + injectee +
                        " is marked both @Optional and @Self"));
            }

            if (!injectee.getRequiredQualifiers().isEmpty()) {
                collector.addThrowable(new IllegalArgumentException("Injection point " + injectee +
                        " is marked @Self but has other qualifiers"));
            }

            if (givenDescriptor == null) {
                collector.addThrowable(new IllegalArgumentException("A class with injection point " + injectee +
                        " is being created or injected via the non-managed ServiceLocator API"));
            }
        }

    }

   

    /**
     * Finds the post construct method on this class
     * @param clazz The class to search for the post construct
     * @param collector An error collector
     * @return The post construct method or null
     */
    public static Method findPostConstruct(Class<?> clazz, Collector collector) {
        if (org.glassfish.hk2.api.PostConstruct.class.isAssignableFrom(clazz)) {
            // A little performance optimization
            try {
                return clazz.getMethod(CONVENTION_POST_CONSTRUCT, new Class<?>[0]);
            }
            catch (NoSuchMethodException e) {
                return null;
            }
        }
       
        boolean containsKey;
        Method retVal;
        synchronized (lock) {
            containsKey = postConstructCache.containsKey(clazz);
            retVal = postConstructCache.get(clazz);
        }
       
        if (!containsKey) {
            getAllMethods(clazz)// Fills in the cache
           
            synchronized (lock) {
                retVal = postConstructCache.get(clazz);
            }
        }
       
        if (retVal == null) return null;
           
        if (retVal.isAnnotationPresent(PostConstruct.class) &&
                (retVal.getParameterTypes().length != 0)) {
            collector.addThrowable(new IllegalArgumentException("The method " + Pretty.method(retVal) +
                        " annotated with @PostConstruct must not have any arguments"));
            return null;
        }
           
        return retVal;
    }

    /**
     * Finds the pre destroy method on this class
     * @param clazz The class to search for the pre destroy method
     * @param collector An error collector
     * @return The pre destroy method or null
     */
    public static Method findPreDestroy(Class<?> clazz, Collector collector) {
        if (org.glassfish.hk2.api.PreDestroy.class.isAssignableFrom(clazz)) {
            try {
                return clazz.getMethod(CONVENTION_PRE_DESTROY, new Class<?>[0]);
            }
            catch (NoSuchMethodException e) {
                return null;
            }
        }
       
        boolean containsKey;
        Method retVal;
        synchronized (lock) {
            containsKey = preDestroyCache.containsKey(clazz);
            retVal = preDestroyCache.get(clazz);
        }
       
        if (!containsKey) {
            getAllMethods(clazz)// Fills in the cache
           
            synchronized (lock) {
                retVal = preDestroyCache.get(clazz);
            }
        }
       
        if (retVal == null) return null;
       
        if (retVal.isAnnotationPresent(PreDestroy.class) &&
                (retVal.getParameterTypes().length != 0)) {
            collector.addThrowable(new IllegalArgumentException("The method " + Pretty.method(retVal) +
                    " annotated with @PreDestroy must not have any arguments"));
            return null;
        }
           
        return retVal;
    }

    /**
     * This method returns a set of qualifiers from an array of qualifiers.
     *
     * TODO  It can also do some sanity checking here (i.e., multiple
     * qualifiers of the same type, that sort of thing)
     *
     * @param qualifiers The qualifiers to convert.  May not be null, but
     * may be zero length
     * @param name The name this set of qualifiers must have
     * @return The set containing all the qualifiers
     */
    public static Set<Annotation> fixAndCheckQualifiers(Annotation qualifiers[], String name) {
        Set<Annotation> retVal = new HashSet<Annotation>();

        Set<String> dupChecker = new HashSet<String>();
        Named named = null;
        for (Annotation qualifier : qualifiers) {
            String annotationType = qualifier.annotationType().getName();
            if (dupChecker.contains(annotationType)) {
                throw new IllegalArgumentException(annotationType + " appears more than once in the qualifier list");
            }
            dupChecker.add(annotationType);

            retVal.add(qualifier);
            if (qualifier instanceof Named) {
                named = (Named) qualifier;

                if (named.value().equals("")) {
                    throw new IllegalArgumentException("The @Named qualifier must have a value");
                }

                if (name != null && !name.equals(named.value())) {
                    throw new IllegalArgumentException("The name passed to the method (" +
                            name + ") does not match the value of the @Named qualifier (" + named.value() + ")");
                }
            }
        }

        if (named == null && name != null) {
            retVal.add(new NamedImpl(name));
        }

        return retVal;
    }

    /**
     * Casts this thing to the given type
     * @param o The thing to cast
     * @return A casted version of o
     */
    @SuppressWarnings("unchecked")
    public static <T> T cast(Object o) {
        return (T) o;
    }

    private static <T> T secureCreate(final Class<?> superclass,
            final Class<?>[] interfaces,
            final MethodInterceptor callback,
            boolean useJDKProxy) {

        /* construct the classloader where the generated proxy will be created --
         * this classloader must have visibility into the cglib classloader as well as
         * the superclass' classloader
         */
        final ClassLoader delegatingLoader = (ClassLoader) AccessController
                .doPrivileged(new PrivilegedAction<Object>() {

                    @Override
                    public Object run() {
                        // create a delegating classloader that attempts to
                        // load from the superclass' classloader first,
                        // then hk2-locator's classloader second.
                        return new DelegatingClassLoader<T>(
                                Enhancer.class.getClassLoader(), superclass.getClassLoader());
                    }
                });

        if (useJDKProxy) {
            return AccessController.doPrivileged(new PrivilegedAction<T>() {

                @SuppressWarnings("unchecked")
                @Override
                public T run() {
                    return (T) Proxy.newProxyInstance(delegatingLoader, interfaces,
                            new MethodInterceptorInvocationHandler(callback));
                }

            });

        }


        return AccessController.doPrivileged(new PrivilegedAction<T>() {

            @SuppressWarnings("unchecked")
            @Override
            public T run() {
                EnhancerWithClassLoader<T> e = new EnhancerWithClassLoader<T>(delegatingLoader);

                e.setSuperclass(superclass);
                e.setInterfaces(interfaces);
                e.setCallback(callback);

                return (T) e.create();
            }

        });

    }

    /**
     * Creates the service (without the need for an intermediate ServiceHandle
     * to be created)
     *
     * @param root The ultimate parent of this operation
     * @param injectee the injectee we are creating this service for
     * @param locator The locator to use to find services
     * @param handle The ServiceHandle (or null if there is none)
     * @param requestedClass The class for the service we are looking for
     * @return The created service
     */
    @SuppressWarnings("unchecked")
    public static <T> T createService(ActiveDescriptor<T> root,
            Injectee injectee,
            ServiceLocatorImpl locator,
            ServiceHandle<T> handle,
            Class<?> requestedClass) {
        if (root == null) throw new IllegalArgumentException();

        T service = null;

        if (!root.isReified()) {
            root = (ActiveDescriptor<T>) locator.reifyDescriptor(root, injectee);
        }

        if (Utilities.isProxiable(root, injectee)) {
            boolean isInterface = (requestedClass == null) ? false : requestedClass.isInterface() ;

            final Class<?> proxyClass;
            Class<?> iFaces[];
            if (isInterface) {
                proxyClass = requestedClass;
                iFaces = new Class<?>[2];
                iFaces[0] = proxyClass;
                iFaces[1] = ProxyCtl.class;
            }
            else {
                proxyClass = Utilities.getFactoryAwareImplementationClass(root);

                iFaces = Utilities.getInterfacesForProxy(root.getContractTypes());
            }

            T proxy;
            try {
                proxy = (T) secureCreate(proxyClass,
                    iFaces,
                    new MethodInterceptorImpl(locator, root, handle),
                    isInterface);
            }
            catch (Throwable th) {
                Exception addMe = new IllegalArgumentException("While attempting to create a Proxy for " + proxyClass.getName() +
                        " in proxiable scope " + root.getScope() + " an error occured while creating the proxy");

                if (th instanceof MultiException) {
                    MultiException me = (MultiException) th;

                    me.addError(addMe);

                    throw me;
                }

                MultiException me = new MultiException(th);
                me.addError(addMe);
                throw me;
            }

            return proxy;
        }

        Context<?> context;
        try {
            context = locator.resolveContext(root.getScopeAnnotation());
        }
        catch (Throwable th) {
            Exception addMe = new IllegalStateException("While attempting to create a service for " + root +
                    " in scope " + root.getScope() + " an error occured while locating the context");
           
            if (th instanceof MultiException) {
                MultiException me = (MultiException) th;

                me.addError(addMe);

                throw me;
            }

            MultiException me = new MultiException(th);
            me.addError(addMe);
            throw me;
        }

        try {
            service = context.findOrCreate(root, handle);
        }
        catch (MultiException me) {
            throw me;
        }
        catch (Throwable th) {
            throw new MultiException(th);
        }
       
        if (service == null && !context.supportsNullCreation()) {
            throw new MultiException(new IllegalStateException("Context " +
                context + " findOrCreate returned a null for descriptor " + root +
                " and handle " + handle));
        }

        return service;
    }

    private static class MemberKey {
        private final Member backingMember;
        private final int hashCode;
        private final boolean postConstruct;
        private final boolean preDestroy;

        private MemberKey(Member method, boolean isPostConstruct, boolean isPreDestroy) {
            backingMember = method;
            hashCode = calculateHashCode();
            postConstruct = isPostConstruct;
            preDestroy = isPreDestroy;
        }

        private Member getBackingMember() {
            return backingMember;
        }
       
        private boolean isPostConstruct() {
            return postConstruct;
        }
       
        private boolean isPreDestroy() {
            return preDestroy;
        }

        private int calculateHashCode() {
            int startCode = 0;
            if (backingMember instanceof Method) {
                startCode = 1;
            } else if (backingMember instanceof Constructor) {
                startCode = 2;
            }

            startCode ^= backingMember.getName().hashCode();

            Class<?> parameters[];
            if (backingMember instanceof Method) {
                parameters = ((Method) backingMember).getParameterTypes();
            } else if (backingMember instanceof Constructor) {
                parameters = ((Constructor<?>) backingMember).getParameterTypes();
            } else {
                parameters = new Class<?>[0];
            }

            for (Class<?> param : parameters) {
                startCode ^= param.hashCode();
            }

            return startCode;
        }
       
        public int hashCode() {
            return hashCode;
        }

        public boolean equals(Object o) {
            if (o == null) return false;
            if (!(o instanceof MemberKey)) return false;

            MemberKey omk = (MemberKey) o;
            if (hashCode != omk.hashCode) return false;

            Member oMember = omk.backingMember;

            if (oMember.equals(backingMember)) {
                // If they are the same, they are the same!
                return true;
            }

            if ((backingMember instanceof Field) || (oMember instanceof Field)) {
                // Fields do not inherit
                return false;
            }

            if ((backingMember instanceof Method) && !(oMember instanceof Method)) {
                return false;
            }
            if ((backingMember instanceof Constructor) && !(oMember instanceof Constructor)) {
                return false;
            }

            if (!oMember.getName().equals(backingMember.getName())) {
                return false;
            }

            if (isPrivate(backingMember) || isPrivate(oMember)) {
                // If either are private, they are not the same
                return false;
            }

            Class<?> oParams[];
            Class<?> bParams[];
            if (backingMember instanceof Method) {
                oParams = ((Method) oMember).getParameterTypes();
                bParams = ((Method) backingMember).getParameterTypes();
            } else if (backingMember instanceof Constructor) {
                oParams = ((Constructor<?>) oMember).getParameterTypes();
                bParams = ((Constructor<?>) backingMember).getParameterTypes();
            } else {
                oParams = new Class<?>[0];
                bParams = new Class<?>[0];
            }

            if (oParams.length != bParams.length) return false;
            for (int i = 0; i < oParams.length; i++) {
                if (oParams[i] != bParams[i]) return false;
            }

            return true;
        }
    }

    private static class ScopeInfo {
        private final Annotation scope;
        private final Class<? extends Annotation> annoType;

        private ScopeInfo(Annotation scope, Class<? extends Annotation> annoType) {
            this.scope = scope;
            this.annoType = annoType;
        }

        private Annotation getScope() {
            return scope;
        }

        private Class<? extends Annotation> getAnnoType() {
            return annoType;
        }
    }

    private static class MethodInterceptorInvocationHandler implements InvocationHandler {
        private final MethodInterceptor interceptor;

        private MethodInterceptorInvocationHandler(MethodInterceptor interceptor) {
            this.interceptor = interceptor;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            return interceptor.intercept(proxy, method, args, null);
        }

    }
}
TOP

Related Classes of org.jvnet.hk2.internal.Utilities

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.