org.glassfish.hk2.utilities.AbstractActiveDescriptor Maven / Gradle / Ivy
/*
* 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.glassfish.hk2.utilities;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import jakarta.inject.Named;
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.Injectee;
import org.glassfish.hk2.utilities.reflection.ReflectionHelper;
/**
* This class can be used as a starting point for those writing their own
* ActiveDescriptor. It also has some helper methods to deal with metadata
* and adding and removing contracts and qualifiers, which can be helpful
* when customizing the implementation
*
* @author jwells
* @param The type returned from the cache
*/
public abstract class AbstractActiveDescriptor extends DescriptorImpl implements ActiveDescriptor {
/**
* For serialization
*/
private static final long serialVersionUID = 7080312303893604939L;
private final static Set EMPTY_QUALIFIER_SET = Collections.emptySet();
private Set advertisedContracts = new LinkedHashSet();
private Annotation scopeAnnotation;
private Class extends Annotation> scope;
private Set qualifiers;
private Long factoryServiceId;
private Long factoryLocatorId;
private boolean isReified = true;
private transient boolean cacheSet = false;
private transient T cachedValue;
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock rLock = rwLock.readLock();
private final Lock wLock = rwLock.writeLock();
/**
* For serialization
*/
public AbstractActiveDescriptor() {
super();
scope = null;
}
/**
* Creates a NON reified ActiveDescriptor based on a copy of the given
* baseDescriptor. The values from the baseDescriptor will be copied deeply
*
* @param baseDescriptor The non-null base descriptor to copy values from
*/
protected AbstractActiveDescriptor(Descriptor baseDescriptor) {
super(baseDescriptor);
isReified = false;
scope = null;
}
/**
* This constructor must be called with the information about
* this descriptor
*
* @param advertisedContracts The contracts that should be
* advertised by this descriptor (may not be null, but may be
* empty)
* @param scope The scope of this descriptor (may not be null)
* @param name The name of this descriptor (may be null)
* @param qualifiers The qualifiers of this descriptor (may not
* be null, but may be empty)
* @param ranking The ranking for this descriptor
* @param proxy whether the descriptor should be proxied
* @param proxyForSameScope whether or not to proxy this descriptor
* for other services in the same scope
* @param analyzerName the name of the service used to analyze this class
* @param metadata Metadata to add to this descriptor
*/
protected AbstractActiveDescriptor(
Set advertisedContracts,
Class extends Annotation> scope,
String name,
Set qualifiers,
DescriptorType descriptorType,
DescriptorVisibility descriptorVisibility,
int ranking,
Boolean proxy,
Boolean proxyForSameScope,
String analyzerName,
Map> metadata) {
super();
this.scope = scope;
this.advertisedContracts.addAll(advertisedContracts);
if (qualifiers != null && !qualifiers.isEmpty()) {
this.qualifiers = new LinkedHashSet();
this.qualifiers.addAll(qualifiers);
}
setRanking(ranking);
setDescriptorType(descriptorType);
setDescriptorVisibility(descriptorVisibility);
setName(name); // This MUST be called after the qualifiers have already been set
setProxiable(proxy);
setProxyForSameScope(proxyForSameScope);
if (scope != null) {
setScope(scope.getName());
}
for (Type t : advertisedContracts) {
Class> raw = ReflectionHelper.getRawClass(t);
if (raw == null) continue;
addAdvertisedContract(raw.getName());
}
if (qualifiers != null) {
for (Annotation q : qualifiers) {
addQualifier(q.annotationType().getName());
}
}
setClassAnalysisName(analyzerName);
if (metadata == null) return;
for (Map.Entry> entry : metadata.entrySet()) {
String key = entry.getKey();
List values = entry.getValue();
for (String value : values) {
addMetadata(key, value);
}
}
}
private void removeNamedQualifier() {
try {
wLock.lock();
if (qualifiers == null) return;
for (Annotation qualifier : qualifiers) {
if (qualifier.annotationType().equals(Named.class)) {
removeQualifierAnnotation(qualifier);
return;
}
}
} finally {
wLock.unlock();
}
}
public void setImplementationType(Type t) {
throw new AssertionError("Can not set type of " + getClass().getName() + " descriptor");
}
/**
* Sets the name of this descriptor. Will remove any existing Named
* qualifier and add a Named qualifier for this name
*/
@Override
public void setName(String name) {
try {
wLock.lock();
super.setName(name);
removeNamedQualifier();
if (name == null) return;
addQualifierAnnotation(new NamedImpl(name));
} finally {
wLock.unlock();
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.SingleCache#getCache()
*/
@Override
public T getCache() {
try {
rLock.lock();
return cachedValue;
} finally {
rLock.unlock();
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.SingleCache#isCacheSet()
*/
@Override
public boolean isCacheSet() {
try {
rLock.lock();
return cacheSet;
} finally {
rLock.unlock();
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.SingleCache#setCache(java.lang.Object)
*/
@Override
public void setCache(T cacheMe) {
try {
wLock.lock();
cachedValue = cacheMe;
cacheSet = true;
} finally {
wLock.unlock();
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.SingleCache#releaseCache()
*/
@Override
public void releaseCache() {
try {
wLock.lock();
cacheSet = false;
cachedValue = null;
} finally {
wLock.unlock();
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#isReified()
*/
@Override
public boolean isReified() {
try {
rLock.lock();
return isReified;
} finally {
rLock.unlock();
}
}
/**
* This method is called to change the state of the
* reification of this descriptor
*
* @param reified true if this descriptor should appear reified,
* false otherwise
*/
public void setReified(boolean reified) {
try {
wLock.lock();
isReified = reified;
} finally {
wLock.unlock();
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#getContractTypes()
*/
@Override
public Set getContractTypes() {
try {
rLock.lock();
return Collections.unmodifiableSet(advertisedContracts);
} finally {
rLock.unlock();
}
}
/**
* Adds an advertised contract to the set of contracts advertised by this descriptor
* @param addMe The contract to add. May not be null
*/
public void addContractType(Type addMe) {
try {
wLock.lock();
if (addMe == null) {
return;
}
advertisedContracts.add(addMe);
Class> rawClass = ReflectionHelper.getRawClass(addMe);
if (rawClass == null) {
return;
}
addAdvertisedContract(rawClass.getName());
} finally {
wLock.unlock();
}
}
/**
* Removes an advertised contract from the set of contracts advertised by this descriptor
* @param removeMe The contract to remove. May not be null
* @return true if removeMe was removed from the set
*/
public boolean removeContractType(Type removeMe) {
try {
wLock.lock();
if (removeMe == null) return false;
boolean retVal = advertisedContracts.remove(removeMe);
Class> rawClass = ReflectionHelper.getRawClass(removeMe);
if (rawClass == null) return retVal;
return removeAdvertisedContract(rawClass.getName());
} finally {
wLock.unlock();
}
}
@Override
public Annotation getScopeAsAnnotation() {
return scopeAnnotation;
}
/**
* Sets the scope as an {@link Annotation} implementation.
* This method will also modify the scope as a Class
* and the underlying scope as a String
*
* @param scopeAnnotation The scope as an {@link Annotation}. May
* not be null
*/
public void setScopeAsAnnotation(Annotation scopeAnnotation) {
this.scopeAnnotation = scopeAnnotation;
if (scopeAnnotation != null) {
setScopeAnnotation(scopeAnnotation.annotationType());
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#getScopeAnnotation()
*/
@Override
public Class extends Annotation> getScopeAnnotation() {
return scope;
}
/**
* Sets the scope annotation for this descriptor
*
* @param scopeAnnotation The non-null scope annotation for this service
*/
public void setScopeAnnotation(Class extends Annotation> scopeAnnotation) {
this.scope = scopeAnnotation;
setScope(this.scope.getName());
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#getQualifierAnnotations()
*/
@Override
public Set getQualifierAnnotations() {
try {
rLock.lock();
if (qualifiers == null) return EMPTY_QUALIFIER_SET;
return Collections.unmodifiableSet(qualifiers);
} finally {
rLock.unlock();
}
}
/**
* Adds the given string to the list of qualifiers
*
* @param addMe The fully qualified class name of the qualifier to add. May not be null
*/
public void addQualifierAnnotation(Annotation addMe) {
try {
wLock.lock();
if (addMe == null) return;
if (qualifiers == null) qualifiers = new LinkedHashSet();
qualifiers.add(addMe);
addQualifier(addMe.annotationType().getName());
} finally {
wLock.unlock();
}
}
/**
* Removes the given qualifier from the list of qualifiers
*
* @param removeMe The fully qualifier class name of the qualifier to remove. May not be null
* @return true if the given qualifier was removed
*/
public boolean removeQualifierAnnotation(Annotation removeMe) {
try {
wLock.lock();
if (removeMe == null) return false;
if (qualifiers == null) return false;
boolean retVal = qualifiers.remove(removeMe);
removeQualifier(removeMe.annotationType().getName());
return retVal;
} finally {
wLock.unlock();
}
}
@Override
public Long getFactoryServiceId() {
return factoryServiceId;
}
@Override
public Long getFactoryLocatorId() {
return factoryLocatorId;
}
/**
* Sets the locator and serviceId for the factory. This
* descriptor must be of type PROVIDE_METHOD
*
* @param locatorId The locatorId of the factory associated with
* this method
* @param serviceId The serviceId of the factory associated with
* this method
*/
public void setFactoryId(Long locatorId, Long serviceId) {
if (!getDescriptorType().equals(DescriptorType.PROVIDE_METHOD)) {
throw new IllegalStateException("The descriptor type must be PROVIDE_METHOD");
}
factoryServiceId = serviceId;
factoryLocatorId = locatorId;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#getInjectees()
*/
@Override
public List getInjectees() {
return Collections.emptyList();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.api.ActiveDescriptor#dispose(java.lang.Object, org.glassfish.hk2.api.ServiceHandle)
*/
@Override
public void dispose(T instance) {
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object o) {
return super.equals(o);
}
}