All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jvnet.hk2.internal.Utilities Maven / Gradle / Ivy

Go to download

Ehcache is an open source, standards-based cache used to boost performance, offload the database and simplify scalability. Ehcache is robust, proven and full-featured and this has made it the most widely-used Java-based cache.

There is a newer version: 2.10.9.2
Show newest version
/*
 * 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 javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;

import org.aopalliance.intercept.ConstructorInterceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.glassfish.hk2.utilities.cache.Cache;
import org.glassfish.hk2.utilities.cache.Computable;

import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
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.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

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 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.DynamicConfigurationListener;
import org.glassfish.hk2.api.DynamicConfigurationService;
import org.glassfish.hk2.api.ErrorService;
import org.glassfish.hk2.api.ErrorType;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.Filter;
import org.glassfish.hk2.api.HK2Loader;
import org.glassfish.hk2.api.IndexedFilter;
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.InstanceLifecycleListener;
import org.glassfish.hk2.api.InterceptionService;
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.ValidationService;
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.ContractsProvided;
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 String USE_SOFT_REFERENCE_PROPERTY = "org.jvnet.hk2.properties.useSoftReference";
    private final static boolean USE_SOFT_REFERENCE;
    static {
        USE_SOFT_REFERENCE = AccessController.doPrivileged(new PrivilegedAction() {

            @Override
            public Boolean run() {
                return Boolean.parseBoolean(System.getProperty(USE_SOFT_REFERENCE_PROPERTY, "true"));
            }
            
        });
    }
    
    private final static Object lock = new Object();
    
    /*
     * These caches must not hold any reference to a class (or even a Field or Method which has
     * backpointers to their host classes) because in OSGi-like scenarios when a classloader
     * is removed from the system these caches will keep that classloader referenced.
     * 
     * So instead we keep Strings for class names and MemberDescriptors (which do not hold onto
     * classes) for Fields and Methods.  However, that represents a memory leak by itself, since
     * the classes that are now gone would still forever be represented in these caches.
     * 
     * In order to fix that problem we keep PhantomReferences to the classes.  When the
     * class has no more references the PhantomReference will be enqueued in the deadClasses
     * ReferenceQueue and be cleaned the next time we touch this cache
     */
    private static final Map> methodKeyCache = new HashMap>();
    private static Map> fieldCache = new HashMap>();
    private final static Map postConstructCache = new HashMap();
    private final static Map preDestroyCache = new HashMap();
    
    private final static Map phantoms = new HashMap();
    private final static ReferenceQueue> deadClasses = new ReferenceQueue>();

    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  Constructor getConstructor(Class implClass, ClassAnalyzer analyzer, Collector collector) {
        Constructor 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;
        }
        final Constructor result = element;
        AccessController.doPrivileged(new PrivilegedAction(){

            @Override
            public Object run() {
                result.setAccessible(true);
                return null;
            }
        });

        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 getInitMethods(Class implClass, ClassAnalyzer analyzer, Collector collector) {
        Set 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 getInitFields(Class implClass, ClassAnalyzer analyzer, Collector collector) {
        Set 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 candidateAnnotations, final Set requiredAnnotations) {
        return AccessController.doPrivileged(new PrivilegedAction() {

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

        });

    }

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

            @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() {

            @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 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) {
            ClassLoader ccl = Thread.currentThread().getContextClassLoader();
            if (ccl != null) {
                try {
                    return ccl.loadClass(implementation);
                }
                catch (Throwable th2) {
                    MultiException me = new MultiException(th);
                    me.addError(th2);
                    
                    throw me;
                }
            }
            
            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 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) 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 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 getAutoAdvertisedTypes(Type t) {
        LinkedHashSet retVal = new LinkedHashSet();
        retVal.add(t);

        Class rawClass = ReflectionHelper.getRawClass(t);
        if (rawClass == null) return retVal;
        
        ContractsProvided provided = rawClass.getAnnotation(ContractsProvided.class);
        if (provided != null) {
            // Need to clear the retVal, since even the parent class may not be
            // in the provided set
            retVal.clear();
            
            for (Class providedContract : provided.value()) {
                retVal.add(providedContract);
            }
            
            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  AutoActiveDescriptor createAutoDescriptor(Class clazz, ServiceLocatorImpl locator)
            throws MultiException, IllegalArgumentException, IllegalStateException {
        if (clazz == null) throw new IllegalArgumentException();

        Collector collector = new Collector();

        ClazzCreator creator;
        Set qualifiers;
        Set contracts;
        Class 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(locator, clazz);

        collector.throwIfErrors();

        Map> metadata = new HashMap>();
        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 retVal = new AutoActiveDescriptor(
                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], locator.getNeutralContextClassLoader());
        } 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], locator.getNeutralContextClassLoader());
        } 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 fields = Utilities.getInitFields(baseClass, analyzer, collector);
        Set methods = Utilities.getInitMethods(baseClass, analyzer, collector);

        collector.throwIfErrors();

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

            List 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 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, locator.getNeutralContextClassLoader());
            } 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 justCreate(Class 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 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, locator.getNeutralContextClassLoader());
        } 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 contracts) {
        LinkedList> retVal = new LinkedList>();
        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 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 scope) {
        if (scope.isAnnotationPresent(Unproxiable.class)) return true;
        return false;
    }

    static Cache, Boolean> proxiableAnnotationCache =
            new Cache, Boolean>(new Computable, Boolean>(){

        @Override
        public Boolean compute(Class a) {
            return a.isAnnotationPresent(Proxiable.class);
        }
    });

    /**
     * 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
     */
    private 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;
        }

        final Class scopeAnnotation = desc.getScopeAnnotation();
        if (!proxiableAnnotationCache.compute(scopeAnnotation)) 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;
        }

        Proxiable proxiable = scopeAnnotation.getAnnotation(Proxiable.class);
        Boolean proxyForSameScope = desc.isProxyForSameScope();

        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 getFirstThingInList(List 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 getAllFields(Class clazz, Collector collector) {
        LinkedHashSet retVal = new LinkedHashSet();

        LinkedHashSet keys = new LinkedHashSet();

        getAllFieldKeys(clazz, keys, collector);

        for (MemberKey key : keys) {
            retVal.add(key.getBackingField(clazz));
        }

        return retVal;
    }



    private static void getAllFieldKeys(Class clazz, LinkedHashSet currentFields, Collector collector) {
        if (clazz == null) return;

        Set discovered;
        synchronized (lock) {
            discovered = fieldCache.get(clazz.getName());
        }

        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.getName(), new LinkedHashSet(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 getLocatorDescriptor(ServiceLocator locator) {
        HashSet contracts = new HashSet();
        contracts.add(ServiceLocator.class);

        Set qualifiers = Collections.emptySet();

        ActiveDescriptor retVal =
                new ConstantActiveDescriptor(
                        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> getThreeThirtyDescriptor(
            ServiceLocatorImpl locator) {
        ThreeThirtyResolver threeThirtyResolver = new ThreeThirtyResolver(locator);

        HashSet contracts = new HashSet();

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

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

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

        ActiveDescriptor> retVal =
                new ConstantActiveDescriptor>(
                        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> 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> getAllConstructors(final Class clazz) {
        HashSet> retVal = new LinkedHashSet>();
        
        Constructor constructors[] = AccessController.doPrivileged(new PrivilegedAction[]>() {

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

        });

        for (Constructor constructor : constructors) {
            retVal.add(constructor);
        }

        return retVal;
    }

    /**
     * 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 getAllMethods(Class clazz) {
        LinkedHashSet retVal = new LinkedHashSet();

        LinkedHashSet keys = new LinkedHashSet();

        getAllMethodKeys(clazz, keys);

        Method postConstructMethod = null;
        Method preDestroyMethod = null;
        for (MemberKey key : keys) {
            retVal.add(key.getBackingMethod(clazz));
            if (key.isPostConstruct()) {
                postConstructMethod = key.getBackingMethod(clazz);
            }
            if (key.isPreDestroy()) {
                preDestroyMethod = key.getBackingMethod(clazz);
            }
        }

        synchronized (lock) {
            // It is ok for postConstructMethod to be null
            postConstructCache.put(clazz.getName(), new MemberDescriptor(postConstructMethod));

            // It is ok for preDestroyMethod to be null
            preDestroyCache.put(clazz.getName(), new MemberDescriptor(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, LinkedHashSet currentMethods) {
        if (clazz == null) return;

        Set discoveredMethods;
        synchronized (lock) {
            discoveredMethods = methodKeyCache.get(clazz.getName());
        }

        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.getName(), new LinkedHashSet(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 findInitializerMethods(
            Class annotatedType,
            ServiceLocatorImpl locator,
            Collector errorCollector) {
        LinkedHashSet retVal = new LinkedHashSet();

        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 findInitializerFields(Class annotatedType,
                                                   ServiceLocatorImpl locator,
                                                   Collector errorCollector) {
        LinkedHashSet retVal = new LinkedHashSet();

        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;
    }

    private static class AnnotatedElementAnnotationInfo {
        final Annotation[] elementAnnotations;
        final Annotation[][] paramAnnotations;
        final boolean hasParams;
        final boolean isConstructor;

        AnnotatedElementAnnotationInfo(Annotation[] elementAnnotation, boolean hasParams, Annotation[][] paramAnnotation, boolean isConstructor) {
            this.elementAnnotations = elementAnnotation;
            this.hasParams = hasParams;
            this.paramAnnotations = paramAnnotation;
            this.isConstructor = isConstructor;
        }
    }

    private static final Cache AnnotationCache =
            new Cache(new Computable() {

        @Override
        public AnnotatedElementAnnotationInfo compute(AnnotatedElement annotatedElement) {

            if (annotatedElement instanceof Method) {

                final Method m = (Method) annotatedElement;
                return new AnnotatedElementAnnotationInfo(m.getAnnotations(), true, m.getParameterAnnotations(), false);

            } else if (annotatedElement instanceof Constructor) {

                final Constructor c = (Constructor) annotatedElement;
                return new AnnotatedElementAnnotationInfo(c.getAnnotations(), true, c.getParameterAnnotations(), true);

            } else {
                return new AnnotatedElementAnnotationInfo(annotatedElement.getAnnotations(), false, null, 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(final ServiceLocatorImpl locator, final AnnotatedElement annotated,
            final boolean checkParams, final int position) {

        final AnnotatedElementAnnotationInfo annotationInfo = AnnotationCache.compute(annotated);

        if (checkParams) {

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

        for (Annotation annotation : annotationInfo.elementAnnotations) {
            if (locator.isInjectAnnotation(annotation)) {
                return annotation;
            }
        }

        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);
    }
    
    private static boolean isFinal(Class clazz) {
        int modifiers = clazz.getModifiers();

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

    private static final Cache, String> autoAnalyzerNameCache = new Cache, String>(new Computable, String>() {

        @Override
        public String compute(final Class c) {

            Service s = c.getAnnotation(Service.class);
            if (s == null) return null;

            return s.analyzer();
        }
    });

    /**
     * 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) {
        return autoAnalyzerNameCache.compute(c);
    }

    @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;
                }
                
                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 descScope = (Class)
                    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 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 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);

        //Annotation injectAnnotation = getInjectAnnotation(locator, annotatedGuy, position);

        Class 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 getAllQualifiers(
            AnnotatedElement annotatedGuy,
            String name,
            Collector collector) {

        Named namedQualifier = null;
        Set 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 getAllQualifiers(
            Annotation memberAnnotations[]) {

        HashSet retVal = new HashSet();
        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 getConstructorInjectees(Constructor c, ActiveDescriptor injecteeDescriptor) {
        Type genericTypeParams[] = c.getGenericParameterTypes();
        Annotation paramAnnotations[][] = c.getParameterAnnotations();
        Unqualified unqualified = c.getAnnotation(Unqualified.class);

        List retVal = new LinkedList();

        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 getMethodInjectees(Method c, ActiveDescriptor injecteeDescriptor) {
        Type genericTypeParams[] = c.getGenericParameterTypes();
        Annotation paramAnnotations[][] = c.getParameterAnnotations();
        Unqualified unqualified = c.getAnnotation(Unqualified.class);

        List retVal = new LinkedList();

        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 getFieldAdjustedQualifierAnnotations(Field f) {
        Set 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 getFieldInjectees(Field f, ActiveDescriptor injecteeDescriptor) {
        List retVal = new LinkedList();
        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 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.getName());
            MemberDescriptor md = postConstructCache.get(clazz.getName());
            retVal = (md == null) ? null : md.getMethod(clazz);
        }

        if (!containsKey) {
            getAllMethods(clazz);  // Fills in the cache

            synchronized (lock) {
                MemberDescriptor md = postConstructCache.get(clazz.getName());
                retVal = (md == null) ? null : md.getMethod(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.getName());
            MemberDescriptor md = preDestroyCache.get(clazz.getName());
            retVal = (md == null) ? null : md.getMethod(clazz);
        }

        if (!containsKey) {
            getAllMethods(clazz);  // Fills in the cache

            synchronized (lock) {
                MemberDescriptor md = preDestroyCache.get(clazz.getName());
                retVal = (md == null) ? null : md.getMethod(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 fixAndCheckQualifiers(Annotation qualifiers[], String name) {
        Set retVal = new HashSet();

        Set dupChecker = new HashSet();
        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 cast(Object o) {
        return (T) o;
    }

    private final static Object proxyCreationLock = new Object();
    
    private static  T secureCreate(final Class superclass,
            final Class[] interfaces,
            final MethodHandler callback,
            boolean useJDKProxy) {

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

                    @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(
                                superclass.getClassLoader(),
                                ProxyFactory.class.getClassLoader(),
                                ProxyCtl.class.getClassLoader());
                    }
                });

        if (useJDKProxy) {
            return AccessController.doPrivileged(new PrivilegedAction() {

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

            });

        }


        return AccessController.doPrivileged(new PrivilegedAction() {

            @SuppressWarnings("unchecked")
            @Override
            public T run() {
                synchronized (proxyCreationLock) {
                    ProxyFactory.ClassLoaderProvider originalProvider = ProxyFactory.classLoaderProvider;
                    ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
                        
                        @Override
                        public ClassLoader get(ProxyFactory arg0) {
                            return delegatingLoader;
                        }
                    };
                    
                    try {
                        ProxyFactory proxyFactory = new ProxyFactory();
                        proxyFactory.setInterfaces(interfaces);
                        proxyFactory.setSuperclass(superclass);

                        Class proxyClass = proxyFactory.createClass();

                        try {
                            T proxy = (T) proxyClass.newInstance();

                            ((ProxyObject) proxy).setHandler(callback);

                            return proxy;
                        } catch (Exception e1) {
                            throw new RuntimeException(e1);
                        }
                    }
                    finally {
                        ProxyFactory.classLoaderProvider = originalProvider;
                        
                    }
                }
            }

        });

    }

    /**
     * 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 createService(ActiveDescriptor root,
            Injectee injectee,
            ServiceLocatorImpl locator,
            ServiceHandle handle,
            Class requestedClass) {
        if (root == null) throw new IllegalArgumentException();

        T service = null;

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

        if (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 MemberDescriptor backingMember;
        private final int hashCode;
        private final boolean postConstruct;
        private final boolean preDestroy;

        private MemberKey(Member method, boolean isPostConstruct, boolean isPreDestroy) {
            backingMember = new MemberDescriptor(method);
            hashCode = backingMember.hashCode();
            postConstruct = isPostConstruct;
            preDestroy = isPreDestroy;
        }
        
        private Method getBackingMethod(Class clazz) {
            return backingMember.getMethod(clazz);
        }
        
        private Field getBackingField(Class clazz) {
            return backingMember.getField(clazz);
        }

        private boolean isPostConstruct() {
            return postConstruct;
        }

        private boolean isPreDestroy() {
            return preDestroy;
        }

        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;
            
            return (backingMember.equals(omk.backingMember));
        }
    }

    private static class ScopeInfo {
        private final Annotation scope;
        private final Class annoType;

        private ScopeInfo(Annotation scope, Class annoType) {
            this.scope = scope;
            this.annoType = annoType;
        }

        private Annotation getScope() {
            return scope;
        }

        private Class getAnnoType() {
            return annoType;
        }
    }

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

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

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

    }
    
    private static final HashSet NOT_INTERCEPTED = new HashSet();
    
    static {
        NOT_INTERCEPTED.add(ServiceLocator.class.getName());
        NOT_INTERCEPTED.add(InstanceLifecycleListener.class.getName());
        NOT_INTERCEPTED.add(InjectionResolver.class.getName());
        NOT_INTERCEPTED.add(ErrorService.class.getName());
        NOT_INTERCEPTED.add(ClassAnalyzer.class.getName());
        NOT_INTERCEPTED.add(DynamicConfigurationListener.class.getName());
        NOT_INTERCEPTED.add(DynamicConfigurationService.class.getName());
        NOT_INTERCEPTED.add(InterceptionService.class.getName());
        NOT_INTERCEPTED.add(ValidationService.class.getName());
        NOT_INTERCEPTED.add(Context.class.getName());
    }
    
    private final static Interceptors EMTPY_INTERCEPTORS = new Interceptors() {

        @Override
        public Map> getMethodInterceptors() {
            return null;
        }

        @Override
        public List getConstructorInterceptors() {
            return null;
        }
        
    };
    
    /* package */ static Interceptors getAllInterceptors(
            ServiceLocatorImpl impl,
            ActiveDescriptor descriptor,
            Class clazz,
            Constructor c) {
        if (descriptor == null || clazz == null || isFinal(clazz)) return EMTPY_INTERCEPTORS;
        
        List interceptionServices = impl.getInterceptionServices();
        if (interceptionServices == null || interceptionServices.isEmpty()) return EMTPY_INTERCEPTORS;
        
        // Make sure it is not one of the special services
        for (String contract : descriptor.getAdvertisedContracts()) {
            if (NOT_INTERCEPTED.contains(contract)) return EMTPY_INTERCEPTORS;
        }
        
        final LinkedHashMap> retVal =
                new LinkedHashMap>();
        final ArrayList cRetVal = new ArrayList();
        
        for (InterceptionService interceptionService : interceptionServices) {
            Filter filter = interceptionService.getDescriptorFilter();
            if (filter instanceof IndexedFilter) {
                IndexedFilter indexedFilter = (IndexedFilter) filter;
                
                String indexedContract = indexedFilter.getAdvertisedContract();
                if (indexedContract != null) {
                    if (!descriptor.getAdvertisedContracts().contains(indexedContract)) continue;
                }
                String name = indexedFilter.getName();
                if (name != null) {
                    if (descriptor.getName() == null) continue;
                    if (!descriptor.getName().equals(name)) continue;
                }
            }
            
            if (filter.matches(descriptor)) {
                for (Method method : getAllMethods(clazz)) {
                    if (isFinal(method)) continue;
                    
                    List interceptors = interceptionService.getMethodInterceptors(method);
                    if (interceptors != null && !interceptors.isEmpty()) {
                        List addToMe = retVal.get(method);
                        if (addToMe == null) {
                            addToMe = new ArrayList();
                            retVal.put(method, addToMe);
                        }
                        
                        addToMe.addAll(interceptors);
                    }
                }
                
                List cInterceptors = interceptionService.getConstructorInterceptors(c);
                if (cInterceptors != null && !cInterceptors.isEmpty()) {
                    cRetVal.addAll(cInterceptors);
                }
            }
        }
        
        return new Interceptors() {

            @Override
            public Map> getMethodInterceptors() {
                return retVal;
            }

            @Override
            public List getConstructorInterceptors() {
                return cRetVal;
            }
            
        };
    }
    
    private static void cleanCache() {
        synchronized (lock) {
            Reference> ref;
            while ((ref = deadClasses.poll()) != null) {
            
                ClazzPhantomReference cpr = (ClazzPhantomReference) ref;
            
                phantoms.remove(cpr.clazzName);
                postConstructCache.remove(cpr.clazzName);
                preDestroyCache.remove(cpr.clazzName);
                methodKeyCache.remove(cpr.clazzName);
                fieldCache.remove(cpr.clazzName);
            }
        }
    }
    
    private static class ClazzPhantomReference extends PhantomReference> {
        private final String clazzName;
        
        private ClazzPhantomReference(Class reference) {
            super(reference, deadClasses);
            clazzName = reference.getName();
        }
    }
    
    private static final Map> scalarClasses = new HashMap>();
    static {
        scalarClasses.put(boolean.class.getName(), boolean.class);
        scalarClasses.put(short.class.getName(), short.class);
        scalarClasses.put(int.class.getName(), int.class);
        scalarClasses.put(long.class.getName(), long.class);
        scalarClasses.put(float.class.getName(), float.class);
        scalarClasses.put(double.class.getName(), double.class);
        scalarClasses.put(byte.class.getName(), byte.class);
        scalarClasses.put(char.class.getName(), char.class);
    }
    
    /**
     * This member descriptor must have the same equals
     * and hashCode even if the declaringClass is *different*.
     * This allows us to override a super-classes definition
     * 
     * The other point of this class is to have a scalar only
     * representation of a Field or Method so that this will
     * not keep references to classes that may go away.  However, it
     * must also be highly performant, which is why we keep the
     * declaring class, so that we can quickly find the true
     * class where the method or field is defined quickly without
     * searching the entire class hierarchy
     * 
     * Note that when the SoftReference returns null then it is time
     * to clean the cache, as some class or another we had in the cache
     * is now gone
     * 
     * @author jwells
     *
     */
    private static class MemberDescriptor {
        private final String declaringClass;
        private final String name;
        private final String args[];
        private final boolean isMethod;
        private final boolean isPrivate;
        private final int hashCode;
        
        private SoftReference soft;
        
        private MemberDescriptor(Member member) {
            if (member == null) {
                declaringClass = null;
                name = null;
                args = null;
                isMethod = true;
                isPrivate = false;
                hashCode = 0;
                return;
            }
            
            name = member.getName();
            declaringClass = member.getDeclaringClass().getName();
            isPrivate = isPrivate(member);
            soft = new SoftReference(member);
            
            if (member instanceof Field) {
                isMethod = false;
                
                args = null;
                
                hashCode = name.hashCode();
            }
            else if (member instanceof Method) {
                isMethod = true;
                
                int calcHashCode = 1 ^ name.hashCode();
                
                Method method = (Method) member;
                
                Class mArgs[] = method.getParameterTypes();
                args = new String[mArgs.length];
                for (int lcv = 0; lcv < mArgs.length; lcv++) {
                    args[lcv] = mArgs[lcv].getName();
                    calcHashCode ^= args[lcv].hashCode();
                }
                
                hashCode = calcHashCode;
            }
            else {
                throw new AssertionError("Unknown member type " + member);
            }
        }
        
        @Override
        public int hashCode() {
            return hashCode;
        }
        
        public boolean equals(Object o) {
            if (o == null) return false;
            if (!(o instanceof MemberDescriptor)) return false;
            
            MemberDescriptor oMd = (MemberDescriptor) o;
            if (hashCode != oMd.hashCode) return false; 
            
            if (isMethod != oMd.isMethod) {
                // Fields are not equal to methods
                return false;
            }
            
            if (!isMethod) {
                // Fields do not inherit
                return false;
            }
            
            // Null name check
            if (name == null) {
                if (oMd.name != null) return false;
            }
            else if (oMd.name == null) return false;
            
            if (!name.equals(oMd.name)) return false;
            if (isPrivate || oMd.isPrivate) return false;
            
            if (args == null) return true; // This is a field
            
            if (args.length != oMd.args.length) return false;
            
            for (int lcv = 0; lcv < args.length; lcv++) {
                if (!args[lcv].equals(oMd.args[lcv])) return false;
            }
            
            return true;
        }
        
        private Field getField(final Class clazz) {
            return AccessController.doPrivileged(new PrivilegedAction() {

                @Override
                public Field run() {
                    return internalGetField(clazz);
                }
                
            });
        }
        
        private Field internalGetField(Class clazz) {
            if (name == null || isMethod) return null;
            Field retVal = (Field) soft.get();
            if (retVal == null) {
                cleanCache();
            }
            
            if (USE_SOFT_REFERENCE && (retVal != null) &&
                retVal.getDeclaringClass().isAssignableFrom(clazz)) {
                return retVal;
            }
            
            while (clazz != null) {
                if (clazz.getName().equals(declaringClass)) break;
                
                clazz = clazz.getSuperclass();
            }
            
            if (clazz == null) {
                return null;
            }
            
            try {
                retVal = clazz.getDeclaredField(name);
                soft = new SoftReference(retVal);
                
                return retVal;
            }
            catch (Throwable e) {
                return null;
            }
        }
        
        private Method getMethod(final Class clazz) {
            return AccessController.doPrivileged(new PrivilegedAction() {

                @Override
                public Method run() {
                    return internalGetMethod(clazz);
                }
                
            });
        }
        
        private Method internalGetMethod(Class clazz) {
            if (name == null || !isMethod) return null;
            Method retVal = (Method) soft.get();
            if (retVal == null) {
                cleanCache();
            }
            
            if (USE_SOFT_REFERENCE && (retVal != null) &&
                retVal.getDeclaringClass().isAssignableFrom(clazz)) {
                return retVal;
            }
            
            while (clazz != null) {
                if (clazz.getName().equals(declaringClass)) break;
                
                clazz = clazz.getSuperclass();
            }
            
            if (clazz == null) {
                return null;
            }
            
            ClassLoader loader = clazz.getClassLoader();
            
            Class parameterTypes[] = new Class[args.length];
            
            for (int lcv = 0; lcv < args.length; lcv++) {
                parameterTypes[lcv] = scalarClasses.get(args[lcv]);
                if (parameterTypes[lcv] != null) continue;
                
                if (args[lcv].startsWith("[")) {
                    // Arrays must be handled with forName, not loadClass
                    try {
                        parameterTypes[lcv] = Class.forName(args[lcv], false, loader);
                    }
                    catch (Throwable e) {
                        try {
                            parameterTypes[lcv] = Class.forName(args[lcv], false, ClassLoader.getSystemClassLoader());
                        }
                        catch (Throwable e2) {
                            return null;
                        }
                    }
                }
                else {
                    try {
                        parameterTypes[lcv] = loader.loadClass(args[lcv]);
                    }
                    catch (Throwable e) {
                        try {
                            parameterTypes[lcv] = ClassLoader.getSystemClassLoader().loadClass(args[lcv]);
                        }
                        catch (Throwable e2) {
                            return null;
                        }
                    }
                }
            }
            
            try {
                retVal = clazz.getDeclaredMethod(name, parameterTypes);
                soft = new SoftReference(retVal);
                
                return retVal; 
            }
            catch (Throwable e) {
                return null;
            }
            
        }
        
        public String toString() {
            StringBuffer sb = new StringBuffer("{");
            if (args != null) {
                boolean first = true;
                for (String arg : args) {
                    if (first) {
                        sb.append(arg);
                        first = false;
                    }
                    else {
                        sb.append("," + arg);
                    }
                }
            }
            sb.append("}");
            
            return "MethodDescriptor(" + ((isMethod) ? "method," : "field,") +
                    name + "," +
                    declaringClass + "," +
                    sb.toString() + ")";
        }
    }
    
    /**
     * The return type from getAllInterceptors
     * 
     * @author jwells
     *
     */
    public interface Interceptors {
        /**
         * Gets the method interceptors
         * @return The possibly null set of method interceptors
         */
        public Map> getMethodInterceptors();
        
        /**
         * Gets the constructor interceptors
         * @return The possibly null set of constructor interceptors
         */
        public List getConstructorInterceptors();
    }
}