Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
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.
/*
* 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 extends Annotation> getInjectionResolverType(ActiveDescriptor> desc) {
for (Type advertisedType : desc.getContractTypes()) {
Class> rawClass = ReflectionHelper.getRawClass(advertisedType);
if (!InjectionResolver.class.equals(rawClass)) continue;
// Found the InjectionResolver
if (!(advertisedType instanceof ParameterizedType)) {
return null;
}
Type firstType = getFirstTypeArgument(advertisedType);
if (!(firstType instanceof Class)) {
return null;
}
Class> retVal = (Class>) firstType;
if (!Annotation.class.isAssignableFrom(retVal)) {
return null;
}
return (Class extends Annotation>) retVal;
}
return null;
}
/**
* This method returns the class associated with the type of the
* factory
*
* @param factoryClass The non-null factory class. May not be null
* @return the CLASS version of what the factory produces. Will
* not be null
* @throws MultiException if there was an error analyzing the class
*/
private static Class> getFactoryProductionClass(Class> factoryClass) {
Type factoryProvidedType = getFactoryProductionType(factoryClass);
Class> retVal = ReflectionHelper.getRawClass(factoryProvidedType);
if (retVal != null) return retVal;
throw new MultiException(new AssertionError("Could not find true produced type of factory " + factoryClass.getName()));
}
/**
* This method returns the type produced by a factory class
*
* @param factoryClass The non-null factory class. May not be null
* @return the type version of what the factory produces. Will
* not be null
* @throws MultiException if there was an error analyzing the class
*/
public static Type getFactoryProductionType(Class> factoryClass) {
Set 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 extends Annotation> scope;
String name;
Boolean proxy = null;
Boolean proxyForSameScope = null;
String analyzerName;
// Qualifiers naming dance
qualifiers = ReflectionHelper.getQualifierAnnotations(clazz);
name = ReflectionHelper.getNameFromAllQualifiers(qualifiers, clazz);
qualifiers = getAllQualifiers(clazz, name, collector); // Fixes the @Named qualifier if it has no value
contracts = getAutoAdvertisedTypes(clazz);
ScopeInfo scopeInfo = getScopeInfo(clazz, null, collector);
scope = scopeInfo.getAnnoType();
analyzerName = getAutoAnalyzerName(clazz);
creator = new ClazzCreator(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 extends Annotation> scope) {
return scope.isAnnotationPresent(Proxiable.class);
}
/**
* Returns true if this scope is unproxiable
*
* @param scope The scope annotation to test
* @return true if this must be proxied
*/
public static boolean isUnproxiableScope(Class extends Annotation> scope) {
if (scope.isAnnotationPresent(Unproxiable.class)) return true;
return false;
}
static Cache, Boolean> proxiableAnnotationCache =
new Cache, Boolean>(new Computable, Boolean>(){
@Override
public Boolean compute(Class extends Annotation> 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 extends Annotation> 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 extends Annotation> descScope = (Class extends Annotation>)
loadClass(defaultScope.getScope(), defaultScope, collector);
if (descScope != null) {
return new ScopeInfo(null, descScope);
}
}
return new ScopeInfo(null, PerLookup.class);
}
/**
* Returns the scope of this thing
*
* @param fromThis The annotated class or producer method
* @param defaultScope The default scope if none other can be found
* @return The scope of this class or producer method. If no scope is
* found will return the dependent scope
*/
public static Class extends Annotation> getScopeAnnotationType(
Class> fromThis,
Descriptor defaultScope) {
Collector collector = new Collector();
ScopeInfo si = getScopeInfo(fromThis, defaultScope, collector);
collector.throwIfErrors();
return si.getAnnoType();
}
/**
* Returns the scope of this thing
*
* @param annotatedGuy The annotated class or producer method
* @param defaultScope The default scope if none other can be found
* @param collector The error collector
* @return The scope of this class or producer method. If no scope is
* found will return the dependent scope
*/
public static Class extends Annotation> getScopeAnnotationType(
AnnotatedElement annotatedGuy,
Descriptor defaultScope,
Collector collector) {
ScopeInfo si = getScopeInfo(annotatedGuy, defaultScope, collector);
return si.getAnnoType();
}
/**
* This returns the scope annotation on this class *itself*, and no other
* classes (like, not subclasses).
*/
private static Annotation internalGetScopeAnnotationType(
AnnotatedElement annotatedGuy,
Collector collector) {
boolean epicFail = false;
Annotation retVal = null;
for (Annotation annotation : annotatedGuy.getDeclaredAnnotations()) {
if (annotation.annotationType().isAnnotationPresent(Scope.class)) {
if (retVal != null) {
collector.addThrowable(new IllegalArgumentException("The type " + annotatedGuy +
" may not have more than one scope. It has at least " +
Pretty.clazz(retVal.annotationType()) +
" and " + Pretty.clazz(annotation.annotationType())));
epicFail = true;
continue;
}
retVal = annotation;
}
}
if (epicFail) return null;
return retVal;
}
/**
* Returns an injection resolver for the injectee
*
* @param locator The locator to use when finding the resolver
* @param injectee Injectee from which the annotation should be extracted
* @return Injection resolver used to resolve the injection for the injectee
* @throws IllegalStateException If we could not find a valid resolver
*/
public static InjectionResolver> getInjectionResolver(
ServiceLocatorImpl locator, Injectee injectee) throws IllegalStateException {
return getInjectionResolver(locator, injectee.getParent(), injectee.getPosition());
}
private static InjectionResolver> getInjectionResolver(
ServiceLocatorImpl locator, AnnotatedElement annotatedGuy, int position) throws IllegalStateException {
boolean methodOrConstructor = annotatedGuy instanceof Method || annotatedGuy instanceof Constructor>;
Annotation injectAnnotation = getInjectAnnotation(locator, annotatedGuy, methodOrConstructor, position);
//Annotation injectAnnotation = getInjectAnnotation(locator, annotatedGuy, position);
Class extends Annotation> injectType = (injectAnnotation == null) ?
Inject.class : injectAnnotation.annotationType();
InjectionResolver> retVal = locator.getInjectionResolver(injectType);
if (retVal == null) {
// Not possible to get here, we only are here if we already found a resolver
throw new IllegalStateException("There is no installed injection resolver for " +
Pretty.clazz(injectType) + " for type " + annotatedGuy);
}
return retVal;
}
/**
* Returns an injection resolver for this AnnotatedElement. The method cannot be used for constructors
* or methods.
*
* @param locator The locator to use when finding the resolver
* @param annotatedGuy The annotated class or producer method
* @return The scope of this class or producer method. If no scope is
* found will return the dependent scope
* @throws IllegalStateException If we could not find a valid resolver
*/
public static InjectionResolver> getInjectionResolver(
ServiceLocatorImpl locator, AnnotatedElement annotatedGuy) throws IllegalStateException {
if (annotatedGuy instanceof Method || annotatedGuy instanceof Constructor>) {
throw new IllegalArgumentException("Annotated element '" + annotatedGuy + "' cannot be Method neither Constructor.");
}
return getInjectionResolver(locator, annotatedGuy, -1);
}
private final static String PROVIDE_METHOD = "provide";
/**
* This method will retrieve the provide method from a Factory
*
* @param clazz This class must implement factory
* @return The provide method from this class
*/
public static Method getFactoryProvideMethod(Class> clazz) {
try {
return clazz.getMethod(PROVIDE_METHOD);
} catch (NoSuchMethodException e) {
return null;
}
}
/**
* Returns the default name if one can be found. Will only work on
* classes and methods
*
* @param parent The parent annotated element
* @param collector For errors
* @return null if there is no default name (no Named)
*/
public static String getDefaultNameFromMethod(Method parent, Collector collector) {
Named named = parent.getAnnotation(Named.class);
if (named == null) {
return null;
}
if (named.value() == null || named.value().equals("")) {
collector.addThrowable(new IllegalArgumentException(
"@Named on the provide method of a factory must have an explicit value"));
}
return named.value();
}
/**
* Returns the full set of qualifier annotations on this class
*
* @param annotatedGuy The element we are searching for qualifiers
* @param name The name this element must have
* @param collector The error collector
* @return A non-null but possibly empty set of qualifiers
*/
public static Set 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 extends Annotation> annoType;
private ScopeInfo(Annotation scope, Class extends Annotation> annoType) {
this.scope = scope;
this.annoType = annoType;
}
private Annotation getScope() {
return scope;
}
private Class extends Annotation> getAnnoType() {
return annoType;
}
}
private static class MethodInterceptorInvocationHandler implements InvocationHandler {
private final 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 extends Class>> 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();
}
}