org.infinispan.cdi.util.defaultbean.DefaultBeanExtension Maven / Gradle / Ivy
package org.infinispan.cdi.util.defaultbean;
import org.infinispan.cdi.util.AnyLiteral;
import org.infinispan.cdi.util.DefaultLiteral;
import org.infinispan.cdi.util.HierarchyDiscovery;
import org.infinispan.cdi.util.Reflections;
import org.infinispan.cdi.util.Synthetic;
import org.infinispan.cdi.util.annotatedtypebuilder.AnnotatedTypeBuilder;
import org.infinispan.cdi.util.logging.Log;
import org.infinispan.commons.logging.LogFactory;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Registers beans annotated @DefaultBean
*
* During the ProcessAnnotatedTypePhase beans and producers annotated with @Default
* have a synthetic qualifier added to them and existing qualifiers removed.
*
* During the ProcessBean phases these default beans are stored for later use
*
* If no alternative bean was observed for each of the default beans then the
* stored bean is added as a forwarding bean with it's original set of
* qualifiers
*
* @author Stuart Douglas
*/
public class DefaultBeanExtension implements Extension {
private static final String QUALIFIER_NAMEPSACE = "org.infinispan.cdi.defaultbean";
private static final String PRODUCER_QUALIFIER_NAMEPSACE = "org.infinispan.cdi.defaultbean.producer";
private static final Log log = LogFactory.getLog(DefaultBeanExtension.class, Log.class);
private boolean beanDiscoveryOver = false;
/**
* list of all beans in the system
*/
private final List> processedBeans = new LinkedList>();
;
/**
* stores type and qualifier information for default beans
*/
private final Map beanTypeInformation = new HashMap();
/**
* all default managed beans
*/
private final Map> defaultManagedBeans = new HashMap>();
/**
* all default producer beans
*/
private final Map> defaultProducerMethods = new HashMap>();
/**
* all default producer fields
*/
private final Map> defaultProducerFields = new HashMap>();
/**
* map of producer method/field qualifiers to default bean synthetic
* qualifiers
*/
private final Map producerToDeclaringDefaultBean = new HashMap();
/**
* map of producer method info to the annotated method
*/
private final Map> producerAnnotatedMethods = new HashMap>();
/**
* map of producer methods to the annotated field
*/
private final Map> producerAnnotatedFields = new HashMap>();
private final Synthetic.Provider syntheticProvider = new Synthetic.Provider(QUALIFIER_NAMEPSACE);
private final Synthetic.Provider producerSyntheticProvider = new Synthetic.Provider(PRODUCER_QUALIFIER_NAMEPSACE);
private final Set deploymentProblems = new HashSet();
void processAnnotatedType(@Observes ProcessAnnotatedType event, BeanManager beanManager) {
boolean defaultBean = false;
AnnotatedType tp = event.getAnnotatedType();
AnnotatedTypeBuilder builder = null;
Synthetic declaringBeanSyntheticQualifier = null;
Set declaringBeanQualifiers = null;
if (event.getAnnotatedType().isAnnotationPresent(DefaultBean.class)) {
Set qualifiers = new HashSet();
defaultBean = true;
builder = new AnnotatedTypeBuilder().readFromType(tp);
for (Annotation a : tp.getAnnotations()) {
// remove the qualifiers
if (beanManager.isQualifier(a.annotationType())) {
qualifiers.add(a);
builder.removeFromClass(a.annotationType());
}
}
postProcessQualifierSet(qualifiers);
builder.addToClass(new DefaultBeanInformation.Literal(qualifiers));
declaringBeanQualifiers = new HashSet(qualifiers);
declaringBeanSyntheticQualifier = syntheticProvider.get();
// store the qualifiers for later
beanTypeInformation.put(declaringBeanSyntheticQualifier, new DefaultBeanType(qualifiers, tp.getAnnotation(DefaultBean.class).value()));
builder.addToClass(declaringBeanSyntheticQualifier);
}
final Set producers = new HashSet();
// now look for producer methods
// if this bean is a default bean then all producers are default beans
// otherwise the annotation needs to be present
for (AnnotatedMethod super X> m : tp.getMethods()) {
if (m.isAnnotationPresent(Produces.class) && (defaultBean || m.isAnnotationPresent(DefaultBean.class))) {
if (declaringBeanQualifiers == null) {
declaringBeanQualifiers = new HashSet(Reflections.getQualifiers(beanManager, tp.getAnnotations()));
if (declaringBeanQualifiers.isEmpty()) {
declaringBeanQualifiers.add(DefaultLiteral.INSTANCE);
}
}
if (builder == null) {
builder = new AnnotatedTypeBuilder().readFromType(tp);
}
Set qualifiers = new HashSet();
for (Annotation a : m.getAnnotations()) {
// remove the qualifiers
if (beanManager.isQualifier(a.annotationType())) {
qualifiers.add(a);
builder.removeFromMethod(m, a.annotationType());
}
}
postProcessQualifierSet(qualifiers);
builder.addToMethod(m, new DefaultBeanInformation.Literal(qualifiers));
Synthetic syntheticQualifier = producerSyntheticProvider.get();
// store the qualifiers for later
Type type = null;
// if the type is not explicity set then we infer it
if (m.isAnnotationPresent(DefaultBean.class)) {
type = m.getAnnotation(DefaultBean.class).value();
} else {
type = m.getJavaMember().getGenericReturnType();
}
beanTypeInformation.put(syntheticQualifier, new DefaultBeanType(qualifiers, type));
builder.addToMethod(m, syntheticQualifier);
producerToDeclaringDefaultBean.put(syntheticQualifier, new DefaultBeanQualifiers(declaringBeanSyntheticQualifier, declaringBeanQualifiers));
producers.add(syntheticQualifier);
}
}
//now look for disposer methods
if (!producers.isEmpty()) {
for (AnnotatedMethod super X> m : tp.getMethods()) {
for (AnnotatedParameter super X> p : m.getParameters()) {
if (p.isAnnotationPresent(Disposes.class)) {
Set type = p.getTypeClosure();
Set qualifiers = new HashSet();
for (final Annotation annotation : p.getAnnotations()) {
if (beanManager.isQualifier(annotation.annotationType())) {
qualifiers.add(annotation);
}
}
postProcessQualifierSet(qualifiers);
for (final Synthetic producer : producers) {
final DefaultBeanType beanType = beanTypeInformation.get(producer);
Set types = new HierarchyDiscovery(beanType.getType()).getTypeClosure();
if (Reflections.matches(type, types)) {
if (beanType.getQualifiers().equals(qualifiers)) {
for (final Annotation annotation : p.getAnnotations()) {
if (beanManager.isQualifier(annotation.annotationType())) {
builder.removeFromMethodParameter(m.getJavaMember(), p.getPosition(), annotation.annotationType());
}
}
builder.addToMethodParameter(m.getJavaMember(), p.getPosition(), producer);
}
}
}
}
}
}
}
for (AnnotatedField super X> f : tp.getFields()) {
if (f.isAnnotationPresent(Produces.class) && (defaultBean || f.isAnnotationPresent(DefaultBean.class))) {
if (declaringBeanQualifiers == null) {
declaringBeanQualifiers = new HashSet(Reflections.getQualifiers(beanManager, tp.getAnnotations()));
if (declaringBeanQualifiers.isEmpty()) {
declaringBeanQualifiers.add(DefaultLiteral.INSTANCE);
}
}
// we do not support producer fields on normal scoped beans
// as proxies prevent us from reading the fields
for (Annotation i : tp.getAnnotations()) {
if (beanManager.isNormalScope(i.annotationType())) {
deploymentProblems.add(new RuntimeException("Default producer fields are not supported on normal scoped beans. Field: " + f + " Declaring Bean: " + tp));
}
}
if (builder == null) {
builder = new AnnotatedTypeBuilder().readFromType(tp);
}
Set qualifiers = new HashSet();
for (Annotation a : f.getAnnotations()) {
// remove the qualifiers
if (beanManager.isQualifier(a.annotationType())) {
qualifiers.add(a);
builder.removeFromField(f, a.annotationType());
}
}
postProcessQualifierSet(qualifiers);
builder.addToField(f, new DefaultBeanInformation.Literal(qualifiers));
Synthetic syntheticQualifier = producerSyntheticProvider.get();
// store the qualifiers for later
Type type = null;
if (f.isAnnotationPresent(DefaultBean.class)) {
type = f.getAnnotation(DefaultBean.class).value();
} else {
type = f.getJavaMember().getGenericType();
}
beanTypeInformation.put(syntheticQualifier, new DefaultBeanType(qualifiers, type));
builder.addToField(f, syntheticQualifier);
producerToDeclaringDefaultBean.put(syntheticQualifier, new DefaultBeanQualifiers(declaringBeanSyntheticQualifier, declaringBeanQualifiers));
}
}
if (builder != null) {
event.setAnnotatedType(builder.create());
}
}
void processBean(@Observes ProcessBean event) {
// after the bean discovery is over we don't need to do any more
// processing
if (beanDiscoveryOver) {
return;
}
Bean b = event.getBean();
processedBeans.add(b);
Synthetic qualifier = null;
for (Annotation a : b.getQualifiers()) {
if (a instanceof Synthetic) {
Synthetic sa = (Synthetic) a;
if (sa.namespace().equals(QUALIFIER_NAMEPSACE)) {
qualifier = sa;
break;
}
}
}
if (qualifier != null) {
defaultManagedBeans.put(qualifier, b);
}
}
void processProducer(@Observes ProcessProducerMethod event) {
if (beanDiscoveryOver) {
return;
}
Bean b = event.getBean();
Synthetic qualifier = handleProducerBean(b);
if (qualifier != null) {
// store producer method information
defaultProducerMethods.put(qualifier, event.getBean());
AnnotatedMethod method = event.getAnnotatedProducerMethod();
AnnotatedMethod disposerMethod = null;
if (event.getAnnotatedDisposedParameter() != null) {
disposerMethod = (AnnotatedMethod) event.getAnnotatedDisposedParameter().getDeclaringCallable();
}
producerAnnotatedMethods.put(qualifier, new DefaultProducerMethodInfo(method, disposerMethod));
}
}
void processProducer(@Observes ProcessProducerField event) {
if (beanDiscoveryOver) {
return;
}
Bean b = event.getBean();
Synthetic qualifier = handleProducerBean(b);
if (qualifier != null) {
defaultProducerFields.put(qualifier, event.getBean());
producerAnnotatedFields.put(qualifier, event.getAnnotatedProducerField());
}
}
Synthetic handleProducerBean(Bean b) {
Synthetic qualifier = null;
for (Annotation a : b.getQualifiers()) {
if (a instanceof Synthetic) {
Synthetic sa = (Synthetic) a;
if (sa.namespace().equals(PRODUCER_QUALIFIER_NAMEPSACE)) {
qualifier = sa;
break;
}
}
}
return qualifier;
}
void afterBeanDiscovery(@Observes AfterBeanDiscovery event, BeanManager manager) {
beanDiscoveryOver = true;
// first check for duplicate default bean definitions
Map> duplicateDetectionMap = new HashMap>();
for (Entry e : beanTypeInformation.entrySet()) {
Bean> bean = defaultManagedBeans.get(e.getKey());
if (bean == null) {
bean = defaultProducerMethods.get(e.getKey());
}
if (bean == null) {
bean = defaultProducerFields.get(e.getKey());
}
if (duplicateDetectionMap.containsKey(e.getValue())) {
Bean> other = duplicateDetectionMap.get(e.getValue());
deploymentProblems.add(new RuntimeException("Two default beans with the same type and qualifiers: Type: " + e.getValue().getType() + " Qualifiers: " + e.getValue().getQualifiers() + " Beans are " + other.toString() + " and " + bean.toString()));
}
duplicateDetectionMap.put(e.getValue(), bean);
}
// loop over all installed beans and see if they match any default beans
if (beanTypeInformation.size() > 0) {
for (Bean> processedBean : processedBeans) {
Iterator> it = beanTypeInformation.entrySet().iterator();
while (it.hasNext()) {
Entry definition = it.next();
if (definition.getValue().matches(processedBean)) {
Synthetic qual = definition.getKey();
Bean> bean = null;
// remove the default bean from the beans to be installed
bean = defaultManagedBeans.remove(qual);
if (bean == null) {
bean = defaultProducerMethods.remove(qual);
}
if (bean == null) {
bean = defaultProducerFields.remove(qual);
}
log.info("Preventing install of default bean " + bean);
it.remove();
}
}
}
}
Set allDefaultBeanQualifiers = new HashSet(defaultManagedBeans.keySet());
allDefaultBeanQualifiers.addAll(defaultProducerFields.keySet());
allDefaultBeanQualifiers.addAll(defaultProducerMethods.keySet());
for (Synthetic qual : allDefaultBeanQualifiers) {
final DefaultBeanType beanInfo = beanTypeInformation.get(qual);
final HashSet types = new HashSet();
types.add(Object.class);
types.add(beanInfo.getType());
final Set qualifiers = new HashSet(beanInfo.getQualifiers());
if (defaultManagedBeans.containsKey(qual)) {
Bean> db = DefaultManagedBean.of(defaultManagedBeans.get(qual), beanInfo.getType(), types, qualifiers, manager);
log.debug("Installing default managed bean " + db);
event.addBean(db);
fireBeanInstalledEvent(db, manager);
} else if (defaultProducerMethods.containsKey(qual)) {
Synthetic declaringDefaultBean = this.producerToDeclaringDefaultBean.get(qual).getSyntheticQualifier();
Set declaringBeanQualifiers;
if (declaringDefaultBean != null && !beanTypeInformation.containsKey(declaringDefaultBean)) {
// this is a default producer method that was declared on a
// default bean that has been replaced
declaringBeanQualifiers = Collections.singleton((Annotation) declaringDefaultBean);
} else {
declaringBeanQualifiers = this.producerToDeclaringDefaultBean.get(qual).getQualifiers();
}
DefaultProducerMethodInfo> info = producerAnnotatedMethods.get(qual);
Bean> db = createDefaultProducerMethod(defaultProducerMethods.get(qual), qual, beanInfo, types, qualifiers, declaringBeanQualifiers, info, manager);
log.debug("Installing default producer bean " + db);
event.addBean(db);
fireBeanInstalledEvent(db, manager);
} else if (defaultProducerFields.containsKey(qual)) {
Synthetic declaringDefaultBean = this.producerToDeclaringDefaultBean.get(qual).getSyntheticQualifier();
Set declaringBeanQualifiers;
if (declaringDefaultBean != null && !beanTypeInformation.containsKey(declaringDefaultBean)) {
// this is a default producer method that was declared on a
// default bean that has been replaced
declaringBeanQualifiers = Collections.singleton((Annotation) declaringDefaultBean);
} else {
declaringBeanQualifiers = this.producerToDeclaringDefaultBean.get(qual).getQualifiers();
}
Bean> db = DefaultProducerField.of(defaultProducerFields.get(qual), beanInfo.getType(), types, qualifiers, declaringBeanQualifiers, producerAnnotatedFields.get(qual), manager);
log.debug("Installing default producer bean " + db);
event.addBean(db);
fireBeanInstalledEvent(db, manager);
}
}
for (Throwable e : deploymentProblems) {
event.addDefinitionError(e);
}
}
private void fireBeanInstalledEvent(Bean> bean, BeanManager beanManager) {
beanManager.fireEvent(new DefaultBeanHolder(bean), InstalledLiteral.INSTANCE);
}
void afterDeploymentValidation(@Observes AfterDeploymentValidation event) {
this.processedBeans.clear();
this.beanTypeInformation.clear();
this.producerToDeclaringDefaultBean.clear();
this.producerSyntheticProvider.clear();
this.producerAnnotatedFields.clear();
this.producerAnnotatedMethods.clear();
}
private static class DefaultBeanType {
private final Set qualifiers;
private final Type type;
public DefaultBeanType(Set qualifiers, Type type) {
this.qualifiers = new HashSet(qualifiers);
this.type = type;
}
public Set getQualifiers() {
return qualifiers;
}
public Type getType() {
return type;
}
private boolean matches(Bean> bean) {
if (bean.getTypes().contains(type)) {
for (Annotation a : qualifiers) {
if (!bean.getQualifiers().contains(a)) {
return false;
}
}
return true;
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((qualifiers == null) ? 0 : qualifiers.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DefaultBeanType other = (DefaultBeanType) obj;
if (qualifiers == null) {
if (other.qualifiers != null)
return false;
} else if (!qualifiers.equals(other.qualifiers))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
}
private static class ObserverMethodInfo {
private final Set observerParameterQualifier;
private final Set declaringBeanQualfiers;
private final AnnotatedMethod annotatedMethod;
private final Synthetic defaultBeanSynthetic;
private ObserverMethod> delegate;
public static ObserverMethodInfo of(Set observerParameterQualifier, Set declaringBeanQualfiers, AnnotatedMethod annotatedMethod, Synthetic defaultBeanSynthetic) {
return new ObserverMethodInfo(observerParameterQualifier, declaringBeanQualfiers, annotatedMethod, defaultBeanSynthetic);
}
public ObserverMethodInfo(Set observerParameterQualifier, Set declaringBeanQualfiers, AnnotatedMethod annotatedMethod, Synthetic defaultBeanSynthetic) {
this.observerParameterQualifier = observerParameterQualifier;
this.declaringBeanQualfiers = declaringBeanQualfiers;
this.annotatedMethod = annotatedMethod;
this.defaultBeanSynthetic = defaultBeanSynthetic;
}
public Set getObserverParameterQualifier() {
return observerParameterQualifier;
}
public AnnotatedMethod getAnnotatedMethod() {
return annotatedMethod;
}
public ObserverMethod> getDelegate() {
return delegate;
}
public void setDelegate(ObserverMethod> delegate) {
this.delegate = delegate;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((declaringBeanQualfiers == null) ? 0 : declaringBeanQualfiers.hashCode());
result = prime * result + ((observerParameterQualifier == null) ? 0 : observerParameterQualifier.hashCode());
result = prime * result + ((defaultBeanSynthetic == null) ? 0 : defaultBeanSynthetic.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ObserverMethodInfo> other = (ObserverMethodInfo>) obj;
if (annotatedMethod == null) {
if (other.annotatedMethod != null)
return false;
} else if (!annotatedMethod.equals(other.annotatedMethod))
return false;
if (declaringBeanQualfiers == null) {
if (other.declaringBeanQualfiers != null)
return false;
} else if (!declaringBeanQualfiers.equals(other.declaringBeanQualfiers))
return false;
if (observerParameterQualifier == null) {
if (other.observerParameterQualifier != null)
return false;
} else if (!observerParameterQualifier.equals(other.observerParameterQualifier))
return false;
if (defaultBeanSynthetic == null) {
if (other.defaultBeanSynthetic != null)
return false;
} else if (!defaultBeanSynthetic.equals(other.defaultBeanSynthetic))
return false;
return true;
}
}
private final class DefaultProducerMethodInfo {
private final AnnotatedMethod producerMethod;
private final AnnotatedMethod disposerMethod;
public DefaultProducerMethodInfo(AnnotatedMethod producerMethod, AnnotatedMethod disposerMethod) {
this.producerMethod = producerMethod;
this.disposerMethod = disposerMethod;
}
public AnnotatedMethod getProducerMethod() {
return producerMethod;
}
public AnnotatedMethod getDisposerMethod() {
return disposerMethod;
}
}
private DefaultProducerMethod createDefaultProducerMethod(Bean originalBean, Annotation qualifier, DefaultBeanType beanInfo, Set types, Set qualifiers, Set declaringBeanQualifiers, DefaultProducerMethodInfo info, BeanManager beanManager) {
return DefaultProducerMethod.of(originalBean, beanInfo.getType(), types, qualifiers, declaringBeanQualifiers, info.getProducerMethod(), info.getDisposerMethod(), beanManager);
}
private static class DefaultBeanQualifiers {
private Synthetic syntheticQualifier;
private Set qualifiers;
public DefaultBeanQualifiers(Synthetic syntheticQualifier, Set qualifiers) {
this.syntheticQualifier = syntheticQualifier;
this.qualifiers = qualifiers;
}
public Synthetic getSyntheticQualifier() {
return syntheticQualifier;
}
public Set getQualifiers() {
return qualifiers;
}
}
private Set postProcessQualifierSet(Set qualifiers) {
if (qualifiers.isEmpty()) {
qualifiers.add(DefaultLiteral.INSTANCE);
}
qualifiers.add(AnyLiteral.INSTANCE);
return qualifiers;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy