io.cereebro.snitch.detect.annotation.AnnotationRelationshipDetector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cereebro-snitch Show documentation
Show all versions of cereebro-snitch Show documentation
Cereebro Snitch Spring Boot AutoConfiguration
/*
* Copyright © 2017 the original authors (http://cereebro.io)
*
* 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 io.cereebro.snitch.detect.annotation;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.CollectionUtils;
import io.cereebro.core.Relationship;
import io.cereebro.core.RelationshipDetector;
import lombok.extern.slf4j.Slf4j;
/**
* Abstract class implementing {@link RelationshipDetector} for
* {@link Annotation}. The child class have to declare the type of annotation to
* detect.
*
* @author lwarrot
*
* @param
* Annotation to detect.
*/
@Slf4j
public abstract class AnnotationRelationshipDetector
implements RelationshipDetector, ApplicationContextAware {
private ConfigurableApplicationContext applicationContext;
private final Class annotation;
public AnnotationRelationshipDetector(Class annotation) {
this.annotation = Objects.requireNonNull(annotation, "Annotation class required");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
if (applicationContext instanceof ConfigurableApplicationContext) {
this.applicationContext = ConfigurableApplicationContext.class.cast(applicationContext);
}
}
@Override
public Set detect() {
Set result = new HashSet<>();
if (applicationContext != null) {
Set annotatedClassRels = detectAnnotatedClasses();
LOGGER.debug("Found {} bean classes annotated with {}", annotatedClassRels.size(), annotation);
result.addAll(annotatedClassRels);
Set annotatedFactoryMethodRels = detectAnnotatedFactoryMethods();
LOGGER.debug("Found {} bean factory methods annotated with {}", annotatedFactoryMethodRels.size(),
annotation);
result.addAll(annotatedFactoryMethodRels);
}
return result;
}
/**
* Detected relationships in annotated bean classes.
*
* @return Relationships detected from annotated classes.
*/
protected Set detectAnnotatedClasses() {
Set result = new HashSet<>();
String[] annotatedBeans = applicationContext.getBeanNamesForAnnotation(annotation);
for (String beanName : annotatedBeans) {
T anno = applicationContext.findAnnotationOnBean(beanName, annotation);
result.addAll(extractFromAnnotation(anno));
}
return result;
}
/**
* Detect relationships in annotated {@link Bean @Bean} Factory methods.
*
* @return Relationships detected from factory methods.
*/
protected Set detectAnnotatedFactoryMethods() {
Set result = new HashSet<>();
/* retrieve all beans declared in the application context */
String[] annotateBeans = applicationContext.getBeanDefinitionNames();
ConfigurableBeanFactory factory = applicationContext.getBeanFactory();
for (String beanName : annotateBeans) {
/* ... and get the bean definition of each declared beans */
Optional metadata = getMethodMetadata(factory.getMergedBeanDefinition(beanName));
if (metadata.isPresent()) {
Set rel = detectMethodMetadata(metadata.get());
result.addAll(rel);
}
}
return result;
}
protected Optional getMethodMetadata(BeanDefinition beanDefinition) {
if (beanDefinition instanceof AnnotatedBeanDefinition) {
return Optional.of(AnnotatedBeanDefinition.class.cast(beanDefinition).getFactoryMethodMetadata());
}
return Optional.empty();
}
protected Set detectMethodMetadata(final MethodMetadata metadata) {
/*
* ... get the metadata of the current definition bean for the
* annotation DependencyHint. In this case, we retrieve the annotation
* on the annotated method @Bean. The map can be null.
*/
Map hintData = metadata.getAnnotationAttributes(annotation.getName());
/*
* ... get the Hint directly from the class (Target = ElementType.TYPE)
*/
Optional methodAnnotation = getAnnotation(metadata);
Set rel = new HashSet<>();
if (!CollectionUtils.isEmpty(hintData)) {
rel.addAll(extractFromAnnotationAttributes(hintData));
} else if (methodAnnotation.isPresent()) {
rel.addAll(extractFromAnnotation(methodAnnotation.get()));
}
return rel;
}
/**
* Extract a Relationship from a typed annotation. Used when the annotation
* is set on a class.
*
* @param annotation
* Target annotation.
* @return Relationship
*/
protected abstract Set extractFromAnnotation(T annotation);
/**
* Extract a Relationship from the annotation represented as a Map. Used
* when the annotation is set on a {@link Bean} factory method instead of a
* class.
*
* @param annotationAttributes
* Target annotation attributes map.
* @return Relationship
*/
protected abstract Set extractFromAnnotationAttributes(Map annotationAttributes);
/**
* Get the annotation from the class instead of the {@link Bean} method.
*
* @param metadata
* Method metadata.
* @return An optional target annotation.
*/
protected Optional getAnnotation(MethodMetadata metadata) {
try {
return Optional.ofNullable(Class.forName(metadata.getReturnTypeName()).getDeclaredAnnotation(annotation));
} catch (ClassNotFoundException e) {
LOGGER.error("Could not load class : " + metadata.getReturnTypeName(), e);
}
return Optional.empty();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy