/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and/or its affiliates, and individual
* contributors by the @authors tag. See the copyright.txt in the
* distribution for a full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.solder.core;
import java.beans.Introspector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AnnotatedConstructor;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedParameter;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.inject.Named;
import org.apache.deltaspike.core.api.exclude.annotation.Exclude;
import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder;
import org.jboss.solder.logging.Logger;
import org.jboss.solder.core.Exact;
import org.jboss.solder.core.FullyQualified;
import org.jboss.solder.core.Requires;
import org.jboss.solder.core.VersionLoggerUtil;
//import org.jboss.solder.core.Veto;
import org.jboss.solder.literal.NamedLiteral;
import org.jboss.solder.properties.Properties;
import org.jboss.solder.reflection.Reflections;
//import org.jboss.solder.reflection.annotated.AnnotatedTypeBuilder;
/**
* Extension to install the "core" extensions. Core extensions are those that
* add additional abilities to CDI applications via annotations.
*
* @author Stuart Douglas
* @author Pete Muir
* @author Dan Allen
* @author Gavin King
* @author Jozef Hartinger
*/
public class CoreExtension implements Extension {
private final Collection<Bean<?>> additionalBeans;
static final Logger log = Logger.getLogger(CoreExtension.class);
//this must be public for the service loader to work properly
public CoreExtension() {
this.additionalBeans = new ArrayList<Bean<?>>();
}
void beforeBeanDiscovery(@Observes final BeforeBeanDiscovery bbd) {
VersionLoggerUtil.logVersionInformation(this.getClass(),
"Solder %s (build id: %s)",
"org.jboss.solder.Version");
}
<X> void processAnnotatedType(@Observes final ProcessAnnotatedType<X> pat, BeanManager beanManager) {
final AnnotatedType<X> annotatedType = pat.getAnnotatedType();
final Class<X> javaClass = annotatedType.getJavaClass();
final Package pkg = javaClass.getPackage();
// Support for @Veto
// todo: evalute removal of veto support
/*
if (annotatedType.isAnnotationPresent(Exclude.class) || (pkg != null && pkg.isAnnotationPresent(Exclude.class))) {
pat.veto();
log.info("Preventing " + javaClass + " from being installed as bean due to @Veto annotation");
return;
}
*/
// Support for @Requires
Set<String> requiredClasses = new HashSet<String>();
// package-level @Requires
if (pkg != null && pkg.isAnnotationPresent(Requires.class)) {
String[] packageRequiredClasses = pkg.getAnnotation(Requires.class).value();
requiredClasses.addAll(Arrays.asList(packageRequiredClasses));
}
// class-level @Requires
if (annotatedType.isAnnotationPresent(Requires.class)) {
String[] typeRequiredClasses = annotatedType.getAnnotation(Requires.class).value();
requiredClasses.addAll(Arrays.asList(typeRequiredClasses));
}
if (!requiredClasses.isEmpty()) {
for (String i : requiredClasses) {
try {
Reflections.classForName(i, javaClass.getClassLoader());
} catch (ClassNotFoundException e) {
log.info("Preventing " + javaClass + " from being installed as required class " + i + " could not be found");
pat.veto();
} catch (LinkageError e) {
// LinkageError is a superclass of NoClassDefFoundError
log.info("Preventing " + javaClass + " from being installed as a linkage error occurred loading required class " + i + ". The linkage error was " + e.toString());
pat.veto();
}
}
}
AnnotatedTypeBuilder<X> builder = null;
// support for @Named packages
Named namedFromPackage = null;
if (pkg != null && pkg.isAnnotationPresent(Named.class) && !annotatedType.isAnnotationPresent(Named.class)) {
builder = initializeBuilder(builder, annotatedType);
namedFromPackage = new NamedLiteral();
builder.addToClass(namedFromPackage);
}
FullyQualified qualifiedOnPackage = null;
if (pkg != null) {
qualifiedOnPackage = pkg.getAnnotation(FullyQualified.class);
}
// support for @FullyQualified bean names on type (respect @Named if added by previous operation)
if ((namedFromPackage != null || annotatedType.isAnnotationPresent(Named.class)) &&
(qualifiedOnPackage != null || annotatedType.isAnnotationPresent(FullyQualified.class))) {
builder = initializeBuilder(builder, annotatedType);
String name = (namedFromPackage != null ? namedFromPackage.value() : annotatedType.getAnnotation(Named.class).value());
if (name.length() == 0) {
name = deriveBeanNameForType(javaClass);
}
Package targetPackage = resolveTargetPackage(annotatedType.getAnnotation(FullyQualified.class), qualifiedOnPackage, pkg);
builder.removeFromClass(Named.class); // add w/o remove was failing in cases
builder.addToClass(new NamedLiteral(qualify(targetPackage, name)));
}
// support for @Exact fields
// support for @FullyQualified @Named producer fields
for (AnnotatedField<? super X> f : annotatedType.getFields()) {
if (f.isAnnotationPresent(Exact.class)) {
Class<?> type = f.getAnnotation(Exact.class).value();
builder = initializeBuilder(builder, annotatedType);
builder.overrideFieldType(f, type);
}
if (f.isAnnotationPresent(Produces.class) && f.isAnnotationPresent(Named.class) &&
(qualifiedOnPackage != null || f.isAnnotationPresent(FullyQualified.class))) {
String name = f.getAnnotation(Named.class).value();
if (name.length() == 0) {
name = f.getJavaMember().getName();
}
Package targetPackage = resolveTargetPackage(f.getAnnotation(FullyQualified.class), qualifiedOnPackage, pkg);
builder.removeFromField(f, Named.class); // add w/o remove was failing in cases
builder.addToField(f, new NamedLiteral(qualify(targetPackage, name)));
}
}
// support for @Exact method parameters
// support for @FullyQualified @Named producer methods
for (AnnotatedMethod<? super X> m : annotatedType.getMethods()) {
for (AnnotatedParameter<? super X> p : m.getParameters()) {
if (p.isAnnotationPresent(Exact.class)) {
Class<?> type = p.getAnnotation(Exact.class).value();
builder = initializeBuilder(builder, annotatedType);
builder.overrideParameterType(p, type);
}
}
if (m.isAnnotationPresent(Produces.class) && m.isAnnotationPresent(Named.class) &&
(qualifiedOnPackage != null || m.isAnnotationPresent(FullyQualified.class))) {
String name = m.getAnnotation(Named.class).value();
if (name.length() == 0) {
if (Properties.isProperty(m.getJavaMember())) {
name = Properties.createProperty(m.getJavaMember()).getName();
} else {
name = m.getJavaMember().getName();
}
}
Package targetPackage = resolveTargetPackage(m.getAnnotation(FullyQualified.class), qualifiedOnPackage, pkg);
builder.removeFromMethod(m, Named.class); // add w/o remove was failing in cases
builder.addToMethod(m, new NamedLiteral(qualify(targetPackage, name)));
}
}
// support for @Exact constructor parameters
for (AnnotatedConstructor<X> c : annotatedType.getConstructors()) {
for (AnnotatedParameter<? super X> p : c.getParameters()) {
if (p.isAnnotationPresent(Exact.class)) {
Class<?> type = p.getAnnotation(Exact.class).value();
builder = initializeBuilder(builder, annotatedType);
builder.overrideParameterType(p, type);
}
}
}
if (builder != null) {
pat.setAnnotatedType(builder.create());
}
}
void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager beanManager) {
failIfWeldExtensionsDetected(beanManager);
for (Bean<?> bean : additionalBeans) {
abd.addBean(bean);
}
}
private void failIfWeldExtensionsDetected(BeanManager beanManager) {
for (Iterator<Bean<?>> extensions = beanManager.getBeans(Extension.class).iterator(); extensions.hasNext();) {
if (extensions.next().getBeanClass().getName().equals("org.jboss.weld.extensions.core.CoreExtension")) {
throw new IllegalStateException("Both Weld Extensions and Solder libraries detected on the classpath. " +
"If you're migrating to Solder, please remove Weld Extensions from the deployment.");
}
}
}
private <X> AnnotatedTypeBuilder<X> initializeBuilder(final AnnotatedTypeBuilder<X> currentBuilder, final AnnotatedType<X> source) {
if (currentBuilder == null) {
return new AnnotatedTypeBuilder<X>().readFromType(source);
}
return currentBuilder;
}
private String qualify(final Package pkg, final String name) {
return pkg.getName() + "." + name;
}
private Package resolveTargetPackage(final FullyQualified qualifiedOnElement, final FullyQualified qualifiedOnPackage, final Package currentPackage) {
FullyQualified qualified = qualifiedOnElement != null ? qualifiedOnElement : qualifiedOnPackage;
if (qualified.value() == Class.class) {
return currentPackage;
} else {
return qualified.value().getPackage();
}
}
private String deriveBeanNameForType(Class<?> type) {
return Introspector.decapitalize(type.getSimpleName());
}
}