org.jvnet.hk2.internal.SystemDescriptor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
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.
The newest version!
/*
* Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.jvnet.hk2.internal;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.glassfish.hk2.api.ActiveDescriptor;
import org.glassfish.hk2.api.Descriptor;
import org.glassfish.hk2.api.DescriptorType;
import org.glassfish.hk2.api.DescriptorVisibility;
import org.glassfish.hk2.api.ErrorInformation;
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.InstanceLifecycleEvent;
import org.glassfish.hk2.api.InstanceLifecycleEventType;
import org.glassfish.hk2.api.InstanceLifecycleListener;
import org.glassfish.hk2.api.MultiException;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.Proxiable;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.Unproxiable;
import org.glassfish.hk2.api.ValidationService;
import org.glassfish.hk2.utilities.BuilderHelper;
import org.glassfish.hk2.utilities.DescriptorImpl;
import org.glassfish.hk2.utilities.reflection.ParameterizedTypeImpl;
import org.glassfish.hk2.utilities.reflection.Pretty;
import org.glassfish.hk2.utilities.reflection.ReflectionHelper;
import org.glassfish.hk2.utilities.reflection.ScopeInfo;
/**
* @author jwells
* @param The type from the cache
*/
public class SystemDescriptor implements ActiveDescriptor, Closeable {
private final Descriptor baseDescriptor;
private final Long id;
private final ActiveDescriptor activeDescriptor;
private final ServiceLocatorImpl sdLocator;
private volatile boolean reified;
private boolean reifying = false; // Am I currently reifying
private boolean preAnalyzed = false;
private volatile boolean closed = false;
private final Object cacheLock = new Object();
private boolean cacheSet = false;
private T cachedValue;
// These are used when we are doing the reifying ourselves
private Class implClass;
private Annotation scopeAnnotation;
private Class scope;
private Set contracts;
private Set qualifiers;
private Creator creator;
private Long factoryLocatorId;
private Long factoryServiceId;
private Type implType;
private final HashMap validationServiceCache =
new HashMap();
private final List instanceListeners =
new LinkedList();
private final Set myLists = new HashSet();
private int singletonGeneration = Integer.MAX_VALUE;
/* package */ @SuppressWarnings("unchecked")
SystemDescriptor(Descriptor baseDescriptor, boolean requiresDeepCopy, ServiceLocatorImpl locator, Long serviceId) {
if (requiresDeepCopy) {
this.baseDescriptor = BuilderHelper.deepCopyDescriptor(baseDescriptor);
}
else {
this.baseDescriptor = baseDescriptor;
}
this.sdLocator = locator;
this.id = serviceId;
if (baseDescriptor instanceof ActiveDescriptor) {
ActiveDescriptor active = (ActiveDescriptor) baseDescriptor;
if (active.isReified()) {
activeDescriptor = active;
reified = true;
if (active instanceof AutoActiveDescriptor) {
((AutoActiveDescriptor) active).setHK2Parent(this);
}
}
else {
activeDescriptor = null;
preAnalyzed = true;
implClass = active.getImplementationClass();
implType = active.getImplementationType();
scopeAnnotation = active.getScopeAsAnnotation();
scope = active.getScopeAnnotation();
contracts = Collections.unmodifiableSet(active.getContractTypes());
qualifiers = Collections.unmodifiableSet(active.getQualifierAnnotations());
}
}
else {
activeDescriptor = null;
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getImplementation()
*/
@Override
public String getImplementation() {
return baseDescriptor.getImplementation();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getAdvertisedContracts()
*/
@Override
public Set getAdvertisedContracts() {
return baseDescriptor.getAdvertisedContracts();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getScope()
*/
@Override
public String getScope() {
return baseDescriptor.getScope();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getNames()
*/
@Override
public String getName() {
return baseDescriptor.getName();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getQualifiers()
*/
@Override
public Set getQualifiers() {
return baseDescriptor.getQualifiers();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getDescriptorType()
*/
@Override
public DescriptorType getDescriptorType() {
return baseDescriptor.getDescriptorType();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getDescriptorType()
*/
@Override
public DescriptorVisibility getDescriptorVisibility() {
return baseDescriptor.getDescriptorVisibility();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getMetadata()
*/
@Override
public Map> getMetadata() {
return baseDescriptor.getMetadata();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getLoader()
*/
@Override
public HK2Loader getLoader() {
return baseDescriptor.getLoader();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getRanking()
*/
@Override
public int getRanking() {
return baseDescriptor.getRanking();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#isProxiable()
*/
@Override
public Boolean isProxiable() {
return baseDescriptor.isProxiable();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#isProxyForSameScope()
*/
@Override
public Boolean isProxyForSameScope() {
return baseDescriptor.isProxyForSameScope();
}
@Override
public String getClassAnalysisName() {
return baseDescriptor.getClassAnalysisName();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#setRanking(int)
*/
@Override
public int setRanking(int ranking) {
// do NOT change this without the write lock, can cause
// all sorts of problems with ConcurrentModificationExceptions
return sdLocator.unsortIndexes(ranking, this, myLists);
}
/* package */ int setRankWithLock(int ranking) {
return baseDescriptor.setRanking(ranking);
}
/* package */ void addList(IndexedListData indexedList) {
myLists.add(indexedList);
}
/* package */ void removeList(IndexedListData indexedList) {
myLists.remove(indexedList);
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getServiceId()
*/
@Override
public Long getServiceId() {
return id;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.SingleCache#getCache()
*/
@Override
public T getCache() {
return cachedValue;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.SingleCache#isCacheSet()
*/
@Override
public boolean isCacheSet() {
return cacheSet;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.SingleCache#setCache(java.lang.Object)
*/
@Override
public void setCache(T cacheMe) {
synchronized (cacheLock) {
cachedValue = cacheMe;
cacheSet = true;
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.SingleCache#releaseCache()
*/
@Override
public void releaseCache() {
synchronized (cacheLock) {
cacheSet = false;
cachedValue = null;
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#isReified()
*/
@Override
public boolean isReified() {
// This is safe because once a descriptor is
// reified it is never un-reified
if (reified) return true;
synchronized (this) {
return reified;
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#getImplementationClass()
*/
@Override
public Class getImplementationClass() {
checkState();
if (activeDescriptor != null) {
return activeDescriptor.getImplementationClass();
}
return implClass;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#getImplementationClass()
*/
@Override
public Type getImplementationType() {
checkState();
if (activeDescriptor != null) {
return activeDescriptor.getImplementationType();
}
return implType;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#getContractTypes()
*/
@Override
public Set getContractTypes() {
checkState();
if (activeDescriptor != null) {
return activeDescriptor.getContractTypes();
}
return contracts;
}
@Override
public Annotation getScopeAsAnnotation() {
checkState();
return scopeAnnotation;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#getScopeAnnotation()
*/
@Override
public Class getScopeAnnotation() {
checkState();
if (activeDescriptor != null) {
return activeDescriptor.getScopeAnnotation();
}
return scope;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#getQualifierAnnotations()
*/
@Override
public Set getQualifierAnnotations() {
checkState();
if (activeDescriptor != null) {
return activeDescriptor.getQualifierAnnotations();
}
return qualifiers;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#getInjectees()
*/
@Override
public List getInjectees() {
checkState();
if (activeDescriptor != null) {
return activeDescriptor.getInjectees();
}
return creator.getInjectees();
}
public Long getFactoryServiceId() {
if (activeDescriptor != null) {
return activeDescriptor.getFactoryServiceId();
}
return factoryServiceId;
}
public Long getFactoryLocatorId() {
if (activeDescriptor != null) {
return activeDescriptor.getFactoryLocatorId();
}
return factoryLocatorId;
}
/* package */ void setFactoryIds(Long factoryLocatorId, Long factoryServiceId) {
this.factoryLocatorId = factoryLocatorId;
this.factoryServiceId = factoryServiceId;
}
/* package */ void invokeInstanceListeners(InstanceLifecycleEvent event) {
for (InstanceLifecycleListener listener : instanceListeners) {
listener.lifecycleEvent(event);
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#create(org.glassfish.hk2.api.ServiceHandle)
*/
@Override
public T create(ServiceHandle root) {
checkState();
try {
T retVal;
if (activeDescriptor != null) {
if (!(activeDescriptor instanceof AutoActiveDescriptor)) {
// An auto-active descriptor will do the event in its create method
invokeInstanceListeners(new InstanceLifecycleEventImpl(InstanceLifecycleEventType.PRE_PRODUCTION, null, this));
}
retVal = activeDescriptor.create(root);
if (!(activeDescriptor instanceof AutoActiveDescriptor)) {
// An auto-active descriptor will do the event in its create method
invokeInstanceListeners(new InstanceLifecycleEventImpl(InstanceLifecycleEventType.POST_PRODUCTION, retVal, this));
}
}
else {
retVal = creator.create(root, this);
}
return retVal;
}
catch (Throwable re) {
if (!(re instanceof MultiException)) {
re = new MultiException(re);
}
MultiException reported = (MultiException) re;
if (!reported.getReportToErrorService()) {
// Specifically told to NOT report this error to error handlers
throw (RuntimeException) re;
}
LinkedList errorHandlers = sdLocator.getErrorHandlers();
for (ErrorService es : errorHandlers) {
ErrorInformation ei = new ErrorInformationImpl(ErrorType.SERVICE_CREATION_FAILURE,
this,
null,
reported);
try {
es.onFailure(ei);
}
catch (Throwable th) {
// ignored
}
}
throw (RuntimeException) re;
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#dispose(java.lang.Object, org.glassfish.hk2.api.ServiceHandle)
*/
@Override
public void dispose(T instance) {
checkState();
InstanceLifecycleEventImpl event = new InstanceLifecycleEventImpl(
InstanceLifecycleEventType.PRE_DESTRUCTION,
instance, this);
// invoke listeners BEFORE destroying the instance
invokeInstanceListeners(event);
try {
if (activeDescriptor != null) {
activeDescriptor.dispose(instance);
return;
}
creator.dispose(instance);
}
catch (Throwable re) {
if (!(re instanceof MultiException)) {
re = new MultiException(re);
}
MultiException reported = (MultiException) re;
if (!reported.getReportToErrorService()) {
// Specifically told to NOT report this error to error handlers
throw (RuntimeException) re;
}
LinkedList errorHandlers = sdLocator.getErrorHandlers();
for (ErrorService es : errorHandlers) {
ErrorInformation ei = new ErrorInformationImpl(ErrorType.SERVICE_DESTRUCTION_FAILURE,
this,
null,
reported);
try {
es.onFailure(ei);
}
catch (Throwable th) {
// ignored
}
}
throw (RuntimeException) re;
}
}
private void checkState() {
if (reified) return;
synchronized(this) {
if (!reified) throw new IllegalStateException();
}
}
private ActiveDescriptor getFactoryDescriptor(Method provideMethod,
Type factoryProvidedType,
ServiceLocatorImpl locator,
Collector collector) {
if (factoryServiceId != null && factoryLocatorId != null) {
// In this case someone has told us the exact factory they want
final Long fFactoryServiceId = factoryServiceId;
final Long fFactoryLocatorId = factoryLocatorId;
ActiveDescriptor retVal = locator.getBestDescriptor(new IndexedFilter() {
@Override
public boolean matches(Descriptor d) {
if (d.getServiceId().longValue() != fFactoryServiceId.longValue()) return false;
if (d.getLocatorId().longValue() != fFactoryLocatorId.longValue()) return false;
return true;
}
@Override
public String getAdvertisedContract() {
return Factory.class.getName();
}
@Override
public String getName() {
return null;
}
});
if (retVal == null) {
collector.addThrowable(new IllegalStateException("Could not find a pre-determined factory service for " +
factoryProvidedType));
}
return retVal;
}
List> factoryHandles = locator.getAllServiceHandles(
new ParameterizedTypeImpl(Factory.class, factoryProvidedType));
ServiceHandle factoryHandle = null;
for (ServiceHandle candidate : factoryHandles) {
if (qualifiers.isEmpty()) {
// We do this before we reify in order to ensure we don't reify too much
factoryHandle = candidate;
break;
}
ActiveDescriptor descriptorUnderTest = candidate.getActiveDescriptor();
try {
descriptorUnderTest = locator.reifyDescriptor(descriptorUnderTest);
}
catch (MultiException me) {
collector.addThrowable(me);
continue;
}
Method candidateMethod = Utilities.getFactoryProvideMethod(
descriptorUnderTest.getImplementationClass());
Set candidateQualifiers =
Utilities.getAllQualifiers(
candidateMethod,
Utilities.getDefaultNameFromMethod(candidateMethod, collector),
collector);
if (ReflectionHelper.annotationContainsAll(candidateQualifiers, qualifiers)) {
factoryHandle = candidate;
break;
}
}
if (factoryHandle == null) {
collector.addThrowable(new IllegalStateException("Could not find a factory service for " +
factoryProvidedType));
return null;
}
ActiveDescriptor retVal = factoryHandle.getActiveDescriptor();
factoryServiceId = retVal.getServiceId();
factoryLocatorId = retVal.getLocatorId();
return retVal;
}
/* package */ void reify(Class implClass, Collector collector) {
if (reified) return;
synchronized(this) {
if (reified) return;
while (reifying) {
try {
this.wait();
}
catch (InterruptedException e) {
collector.addThrowable(e);
return;
}
}
if (reified) return;
reifying = true;
}
try {
// This call can NOT hold the SystemDescriptor lock
// because this method could be called with the ServiceLocatorImpl
// lock held, and if the other thread was also trying to
// reify this descriptor that could lead to a deadlock
internalReify(implClass, collector);
}
finally {
synchronized (this) {
reifying = false;
this.notifyAll();
if (!collector.hasErrors()) {
reified = true;
}
else {
collector.addThrowable(new IllegalArgumentException("Errors were discovered while reifying " + this));
}
}
}
}
/**
* The service locator must hold its lock for this cal
*
* @param implClass The impl class to reify
* @param collector An error collector for errors
*/
private void internalReify(Class implClass, Collector collector) {
if (!preAnalyzed) {
this.implClass = implClass;
this.implType = implClass;
}
else {
if (!implClass.equals(this.implClass)) {
collector.addThrowable(new IllegalArgumentException(
"During reification a class mistmatch was found " + implClass.getName() +
" is not the same as " + this.implClass.getName()));
}
}
if (getDescriptorType().equals(DescriptorType.CLASS)) {
if (!preAnalyzed) {
qualifiers = Collections.unmodifiableSet(
Utilities.getAllQualifiers(implClass,
baseDescriptor.getName(),
collector));
}
ClazzCreator myClazzCreator = new ClazzCreator(sdLocator, implClass);
myClazzCreator.initialize(this, collector);
creator = myClazzCreator;
if (!preAnalyzed) {
ScopeInfo si = Utilities.getScopeAnnotationType(implClass, baseDescriptor, collector);
scopeAnnotation = si.getScope();
scope = si.getAnnoType();
contracts = Collections.unmodifiableSet(ReflectionHelper.getTypeClosure(implClass,
baseDescriptor.getAdvertisedContracts()));
}
}
else {
Utilities.checkFactoryType(implClass, collector);
// For a factory base stuff off of the method, not the class
Method provideMethod = Utilities.getFactoryProvideMethod(implClass);
if (provideMethod == null) {
collector.addThrowable(new IllegalArgumentException("Could not find the provide method on the class " + implClass.getName()));
// Do not continue, all is lost
return;
}
if (!preAnalyzed) {
qualifiers = Collections.unmodifiableSet(
Utilities.getAllQualifiers(
provideMethod,
Utilities.getDefaultNameFromMethod(provideMethod, collector),
collector));
}
Type factoryProvidedType = provideMethod.getGenericReturnType();
if (factoryProvidedType instanceof TypeVariable) {
factoryProvidedType = Utilities.getFactoryProductionType(implClass);
}
ActiveDescriptor factoryDescriptor = getFactoryDescriptor(provideMethod,
factoryProvidedType,
sdLocator,
collector);
if (factoryDescriptor != null) {
creator = new FactoryCreator(sdLocator, factoryDescriptor);
}
if (!preAnalyzed) {
ScopeInfo si = Utilities.getScopeAnnotationType(provideMethod, baseDescriptor, collector);
scopeAnnotation = si.getScope();
scope = si.getAnnoType();
contracts = Collections.unmodifiableSet(ReflectionHelper.getTypeClosure(
factoryProvidedType,
baseDescriptor.getAdvertisedContracts()));
}
}
// Check the scope
if ((baseDescriptor.getScope() == null) && (scope == null)) {
scope = PerLookup.class;
}
if (baseDescriptor.getScope() != null && scope != null) {
String scopeName = scope.getName();
if (!scopeName.equals(baseDescriptor.getScope())) {
collector.addThrowable(new IllegalArgumentException("The scope name given in the descriptor (" +
baseDescriptor.getScope() +
") did not match the scope annotation on the class (" + scope.getName() +
") in class " + Pretty.clazz(implClass)));
}
}
if (scope.isAnnotationPresent(Proxiable.class) && scope.isAnnotationPresent(Unproxiable.class)) {
collector.addThrowable(new IllegalArgumentException("The scope " + scope.getName() +
" is marked both @Proxiable and @Unproxiable"));
}
if ((isProxiable() != null) && isProxiable().booleanValue() && Utilities.isUnproxiableScope(scope)) {
collector.addThrowable(new IllegalArgumentException("The descriptor is in an Unproxiable scope but has " +
" isProxiable set to true"));
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.Descriptor#getLocatorId()
*/
@Override
public Long getLocatorId() {
return sdLocator.getLocatorId();
}
/* (non-Javadoc)
* @see org.jvnet.hk2.internal.Closeable#close()
*/
@Override
public boolean close() {
if (closed) return true;
synchronized (this) {
if (closed) return true;
closed = true;
return false;
}
}
/* (non-Javadoc)
* @see org.jvnet.hk2.internal.Closeable#isClosed()
*/
@Override
public boolean isClosed() {
// This is safe because once a descriptor is
// closed it is never opened
return closed;
}
/**
* Gets the decision of the filter from this service. May use
* a cache
*
* @param service The service to get the isValidating decision from
* @return true if this validation service should validate this descriptor
*/
/* package */ boolean isValidating(ValidationService service) {
Boolean cachedResult = validationServiceCache.get(service);
if (cachedResult != null) {
return cachedResult.booleanValue();
}
boolean decision = true;
try {
decision = BuilderHelper.filterMatches(this, service.getLookupFilter());
}
catch (Throwable th) {
// If the filter fails we assume the decision is true
}
if (decision) {
validationServiceCache.put(service, Boolean.TRUE);
}
else {
validationServiceCache.put(service, Boolean.FALSE);
}
return decision;
}
/* package */ void reupInstanceListeners(List listeners) {
instanceListeners.clear();
for (InstanceLifecycleListener listener : listeners) {
Filter filter = listener.getFilter();
if (BuilderHelper.filterMatches(this, filter)) {
instanceListeners.add(listener);
}
}
}
/* package */ Class getPreAnalyzedClass() {
return implClass;
}
/* package */ int getSingletonGeneration() {
return singletonGeneration;
}
/* package */ void setSingletonGeneration(int gen) {
singletonGeneration = gen;
}
@Override
public int hashCode() {
int low32 = id.intValue();
int high32 = (int) (id.longValue() >> 32);
int locatorLow32 = (int) sdLocator.getLocatorId();
int locatorHigh32 = (int) (sdLocator.getLocatorId() >> 32);
return low32 ^ high32 ^ locatorLow32 ^ locatorHigh32;
}
@SuppressWarnings({ "rawtypes" })
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof SystemDescriptor)) return false;
SystemDescriptor sd = (SystemDescriptor) o;
if (!sd.getServiceId().equals(id)) return false;
return sd.getLocatorId().equals(sdLocator.getLocatorId());
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("SystemDescriptor(");
DescriptorImpl.pretty(sb, this);
sb.append("\n\treified=" + reified);
sb.append(")");
return sb.toString();
}
}