blob: 3911dd7be7d79ece8344d7a83ce749a3ca5097b2 [file] [log] [blame]
/**
* 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.apache.aries.cdi.container.internal.util;
import static java.util.Arrays.asList;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import static org.apache.aries.cdi.container.internal.util.Reflection.getRawType;
import java.lang.annotation.Annotation;
import java.lang.reflect.Executable;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.decorator.Decorator;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.ConversationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.NormalScope;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Stereotype;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMember;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedParameter;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.ProcessBean;
import javax.enterprise.inject.spi.ProcessManagedBean;
import javax.enterprise.inject.spi.ProcessProducerField;
import javax.enterprise.inject.spi.ProcessProducerMethod;
import javax.enterprise.inject.spi.ProcessSessionBean;
import javax.enterprise.inject.spi.ProcessSyntheticBean;
import javax.inject.Named;
import javax.inject.Qualifier;
import javax.inject.Scope;
import javax.interceptor.Interceptor;
import org.apache.aries.cdi.container.internal.annotated.CachingAnnotated;
import org.apache.aries.cdi.extension.spi.annotation.AdaptedService;
import org.osgi.service.cdi.ServiceScope;
import org.osgi.service.cdi.annotations.Service;
import org.osgi.service.cdi.annotations.ServiceInstance;
public class Annotates {
private Annotates() {
// no instances
}
private static final Predicate<Annotation> isBeanDefining = annotation ->
{
// sun.reflect.annotation.AnnotationParser.annotationForMap is a proxy so locally cache the type
final Class<? extends Annotation> annotationType = annotation.annotationType();
return ApplicationScoped.class.isAssignableFrom(annotationType) ||
ConversationScoped.class.isAssignableFrom(annotationType) ||
Decorator.class.isAssignableFrom(annotationType) ||
Dependent.class.isAssignableFrom(annotationType) ||
Interceptor.class.isAssignableFrom(annotationType) ||
RequestScoped.class.isAssignableFrom(annotationType) ||
SessionScoped.class.isAssignableFrom(annotationType) ||
Stereotype.class.isAssignableFrom(annotationType);
};
private static final Predicate<Annotation> isQualifier = annotation ->
{
final Class<? extends Annotation> annotationType = annotation.annotationType();
return !annotationType.equals(Qualifier.class) &&
annotationType.isAnnotationPresent(Qualifier.class);
};
private static final Predicate<Annotation> isScope = annotation ->
{
final Class<? extends Annotation> type = annotation.annotationType();
return type.isAnnotationPresent(Scope.class) ||
type.isAnnotationPresent(NormalScope.class);
};
public static Map<String, Object> componentProperties(Annotated annotated) {
return Maps.merge(annotated.getAnnotations());
}
@SuppressWarnings("unchecked")
public static <X> Class<X> declaringClass(Object instance) {
Class<?> declaringClass = null;
if (instance instanceof AnnotatedMember) {
AnnotatedMember<?> af = (AnnotatedMember<?>)instance;
declaringClass = af.getDeclaringType().getJavaClass();
}
else if (instance instanceof AnnotatedParameter) {
AnnotatedParameter<?> ap = (AnnotatedParameter<?>)instance;
Parameter javaParameter = ap.getJavaParameter();
Executable executable = javaParameter.getDeclaringExecutable();
declaringClass = executable.getDeclaringClass();
}
else if (instance instanceof AnnotatedType) {
AnnotatedType<?> annotatedType = (AnnotatedType<?>)instance;
declaringClass = annotatedType.getJavaClass();
}
else if (instance instanceof Annotated) {
Annotated annotated = (Annotated)instance;
declaringClass = getRawType(annotated.getBaseType());
}
else if (instance instanceof ProcessManagedBean) {
ProcessManagedBean<?> bean = (ProcessManagedBean<?>)instance;
declaringClass = bean.getAnnotatedBeanClass().getJavaClass();
}
else if (instance instanceof ProcessSessionBean) {
ProcessSessionBean<?> bean = (ProcessSessionBean<?>)instance;
declaringClass = bean.getAnnotatedBeanClass().getJavaClass();
}
else if (instance instanceof ProcessProducerMethod) {
ProcessProducerMethod<?, ?> producer = (ProcessProducerMethod<?, ?>)instance;
declaringClass = producer.getAnnotatedProducerMethod().getDeclaringType().getJavaClass();
}
else if (instance instanceof ProcessProducerField) {
ProcessProducerField<?, ?> producer = (ProcessProducerField<?, ?>)instance;
declaringClass = producer.getAnnotatedProducerField().getDeclaringType().getJavaClass();
}
else if (instance instanceof ProcessSyntheticBean) {
ProcessSyntheticBean<?> synthetic = (ProcessSyntheticBean<?>)instance;
declaringClass = synthetic.getBean().getBeanClass();
}
else if (instance instanceof ProcessBean) {
ProcessBean<?> processBean = (ProcessBean<?>)instance;
declaringClass = processBean.getBean().getBeanClass();
}
return (Class<X>)declaringClass;
}
public static Set<Annotation> qualifiers(Annotated annotated) {
return collect(annotated, isQualifier);
}
public static Set<Annotation> collect(Annotated annotated, Predicate<Annotation> predicate) {
CachingAnnotated cachingAnnotated = CachingAnnotated.class.isInstance(annotated) ?
CachingAnnotated.class.cast(annotated) : null;
List<Annotation> cached = cachingAnnotated == null ? null : cachingAnnotated.getCollectedAnnotations();
if (cached != null) {
return cached.stream().filter(predicate).collect(Collectors.toSet());
}
List<Annotation> collected = collect(annotated.getAnnotations());
if (cachingAnnotated != null) {
cachingAnnotated.setCollectedAnnotations(collected);
}
return collected.stream().filter(predicate).collect(Collectors.toSet());
}
private static List<Annotation> collect(Collection<Annotation> annotations) {
return annotations.stream()
.flatMap(it -> Stream.concat(
Stream.of(it), Stream.of(it.annotationType().getAnnotations()))
.filter(a -> !a.annotationType().getName().startsWith("java.lang.annotation."))
.distinct())
.distinct()
.collect(toList());
}
private static List<Annotation> inherit(Collection<Annotation> annotations) {
List<Annotation> list = new ArrayList<>();
for (Annotation a1 : annotations) {
for (Annotation a2 : collect(Arrays.asList(a1.annotationType().getAnnotations()))) {
if (list.contains(a2)) continue;
list.add(a2);
}
}
return list;
}
public static List<Class<?>> serviceClasses(Annotated annotated) {
List<Class<?>> serviceTypes = new ArrayList<>();
List<java.lang.reflect.AnnotatedType> ats = new ArrayList<>();
if (annotated instanceof AnnotatedType) {
Class<?> annotatedClass = ((AnnotatedType<?>)annotated).getJavaClass();
ofNullable(annotatedClass.getAnnotatedSuperclass()).ifPresent(at -> ats.add(at));
ats.addAll(asList(annotatedClass.getAnnotatedInterfaces()));
for (java.lang.reflect.AnnotatedType at : ats) {
ofNullable(at.getAnnotation(Service.class)).ifPresent(
service -> {
if (service.value().length > 0) {
throw new IllegalArgumentException(
String.format(
"@Service on type_use must not specify a value: %s",
annotatedClass));
}
Type type = at.getType();
if (!(type instanceof Class)) {
throw new IllegalArgumentException(
String.format(
"@Service on type_use must only be specified on non-generic types: %s",
annotatedClass));
}
serviceTypes.add((Class<?>)type);
}
);
}
ofNullable(
annotated.getAnnotation(AdaptedService.class)
).map(AdaptedService::value).map(Stream::of).orElseGet(Stream::empty).forEach(serviceTypes::add);
Service service = annotated.getAnnotation(Service.class);
if (service == null) {
return serviceTypes;
}
if (!serviceTypes.isEmpty()) {
throw new IllegalArgumentException(
String.format(
"@Service must not be applied to type and type_use: %s",
annotated));
}
if (service.value().length > 0) {
serviceTypes.addAll(Arrays.asList(service.value()));
}
else if (annotatedClass.getInterfaces().length > 0) {
serviceTypes.addAll(Arrays.asList(annotatedClass.getInterfaces()));
}
else {
serviceTypes.add(annotatedClass);
}
}
else if (annotated instanceof AnnotatedMethod) {
Service service = annotated.getAnnotation(Service.class);
if (service == null) {
return serviceTypes;
}
Class<?> returnType = ((AnnotatedMethod<?>)annotated).getJavaMember().getReturnType();
if (service.value().length > 0) {
serviceTypes.addAll(Arrays.asList(service.value()));
}
else if (returnType.getInterfaces().length > 0) {
serviceTypes.addAll(Arrays.asList(returnType.getInterfaces()));
}
else {
serviceTypes.add(returnType);
}
}
else if (annotated instanceof AnnotatedField) {
Service service = annotated.getAnnotation(Service.class);
if (service == null) {
return serviceTypes;
}
Class<?> fieldType = ((AnnotatedField<?>)annotated).getJavaMember().getType();
if (service.value().length > 0) {
serviceTypes.addAll(Arrays.asList(service.value()));
}
else if (fieldType.getInterfaces().length > 0) {
serviceTypes.addAll(Arrays.asList(fieldType.getInterfaces()));
}
else {
serviceTypes.add(fieldType);
}
}
return serviceTypes;
}
public static List<String> serviceClassNames(Annotated annotated) {
return serviceClasses(annotated).stream().map(Class::getName).sorted().collect(Collectors.toList());
}
public static ServiceScope serviceScope(Annotated annotated) {
ServiceInstance serviceInstance = annotated.getAnnotation(ServiceInstance.class);
if (serviceInstance != null) {
return serviceInstance.value();
}
return ServiceScope.SINGLETON;
}
public static String beanName(Annotated annotated) {
return collect(annotated.getAnnotations()).stream().filter(Named.class::isInstance).map(Named.class::cast).findFirst().map(
named -> {
if (named.value().isEmpty()) {
if (annotated instanceof AnnotatedMethod) {
AnnotatedMethod<?> annotatedMethod = (AnnotatedMethod<?>)annotated;
String name = annotatedMethod.getJavaMember().getName();
if (name.startsWith("get")) {
name = name.substring(3);
}
else if (name.startsWith("is")) {
name = name.substring(2);
}
char c[] = name.toCharArray();
c[0] = Character.toLowerCase(c[0]);
return new String(c);
}
else if (annotated instanceof AnnotatedField) {
AnnotatedField<?> annotatedField = (AnnotatedField<?>)annotated;
return annotatedField.getJavaMember().getName();
}
else {
char c[] = Reflection.getRawType(annotated.getBaseType()).getSimpleName().toCharArray();
c[0] = Character.toLowerCase(c[0]);
return new String(c);
}
}
return named.value();
}
).orElse(null);
}
public static Class<? extends Annotation> beanScope(Annotated annotated) {
return beanScope(annotated, Dependent.class);
}
public static Class<? extends Annotation> beanScope(Annotated annotated, Class<? extends Annotation> defaultValue) {
CachingAnnotated cachingAnnotated = CachingAnnotated.class.isInstance(annotated) ?
CachingAnnotated.class.cast(annotated) : null;
Class<? extends Annotation> cached = cachingAnnotated == null ? null : cachingAnnotated.getBeanScope();
if (cached == null) {
cached = collect(annotated.getAnnotations()).stream().filter(isScope).map(Annotation::annotationType).findFirst().orElse(null);
if (cachingAnnotated != null) {
cachingAnnotated.setBeanScope(cached);
}
}
return cached == null ? defaultValue : cached;
}
public static boolean hasBeanDefiningAnnotations(AnnotatedType<?> annotatedType) {
// only on classes, not on producers
return !collect(annotatedType, isBeanDefining).isEmpty();
}
}