All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jashmore.sqs.util.annotation.AnnotationUtils Maven / Gradle / Ivy

Go to download

Utility methods for dealing with basic functionality for this library, split out so so it can be consumed by other extensions

There is a newer version: 6.0.0
Show newest version
package com.jashmore.sqs.util.annotation;

import com.google.common.base.Preconditions;

import com.jashmore.sqs.argument.MethodParameter;
import lombok.experimental.UtilityClass;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.function.Function;
import javax.annotation.Nonnull;

/**
 * These annotation utility method are written to get past the problem of CGLib and other proxying libraries that extend the base classes to add extra
 * functionality like Aspect Orientated Programming (AOP) point cuts, think wrapping methods in logs or metrics. It does this by extending the base class
 * and apply the logic in the proxied class. As these proxied classes do not copy the base class method annotations doing a simple
 * {@link Method#getAnnotation(Class)} will return null even though the base class originally had that annotation. Therefore, this provides helper methods
 * to traverse through the list of superclasses until it finds the method with the annotation or it gets to the {@link Object} class.
 *
 * 

This does not support annotations on interfaces as for the use case of SQS Listeners I do not think it makes sense to add the annotation to an * interface and to have many classes implement this interface expecting that they would all match the annotation. Therefore, for simplicity in that * sense this feature is not supported. * *

This also does not support bridge methods as this overly complicates the logic and I also don't see the need for applying generics and extending * these SQS Listener methods with generics parameters. For example, I don't see why we would ever have a class like the following. * *

 * public abstract class SqsListener<T> {
 *     @QueueListener("myQueue")
 *     public abstract void method(@Payload T payload);
 * }
 *
 * public class StringSqsListener<String> {
 *     public void method(String payload) {
 *
 *     }
 * }
 * 
* *

Or another scenario where the logic is encapsulated in the abstract class like the following. * *

 * public abstract class SqsListener<T> {
 *     public void method(@Payload T payload) {
 *         log.info("Message: {}", payload);
 *     }
 * }
 *
 * public class StringSqsListener<String> {
 *     @QueueListener("myQueue")
 *     public void method(@Payload String payload) {
 *         super.method(payload);
 *     }
 * }
 * 
* *

For both of these examples if the goal is to reduce goal duplication, prefer composition over inheritance. E.g. create a service that is generic * but the listener class are still solid classes without applying generics. For more information about Java bridge methods take a look at * Java Bridge Methods. */ @UtilityClass public class AnnotationUtils { /** * Get the annotation on a method by looking on the method of this class as well as traversing the methods for all superclasses of the method, returning * the first annotation that it finds otherwise returning an {@link Optional#empty()} if none of the class methods contain the annotation. * * @param methodToProcess method to find annotations for * @param annotationClass the annotation to find for the method * @param the type of the annotation * @return the found annotation in an {@link Optional} otherwise an {@link Optional#empty()} is returned */ @Nonnull public Optional findMethodAnnotation(@Nonnull final Method methodToProcess, @Nonnull final Class annotationClass) { Preconditions.checkNotNull(methodToProcess, "methodToProcess should not be null"); Preconditions.checkNotNull(annotationClass, "annotationClass should not be null"); return findForEachChainedMethodStack(methodToProcess, method -> Optional.ofNullable(method.getAnnotation(annotationClass))); } /** * Find the annotation for the parameter of a given method by traversing the chain of super classes until it eventually finds a parameter with the * annotation or there are no more classes to traverse. * * @param methodParameterToProcess the method parameter to find the annotation for * @param annotationClass the class of the annotation to find * @param the type of the annotation * @return the found annotation in an {@link Optional} otherwise an {@link Optional#empty()} is returned */ @Nonnull public Optional findParameterAnnotation(@Nonnull final MethodParameter methodParameterToProcess, @Nonnull final Class annotationClass) { return findForEachChainedMethodStack(methodParameterToProcess.getMethod(), method -> Optional.ofNullable(method.getParameters()[methodParameterToProcess.getParameterIndex()].getAnnotation(annotationClass))); } /** * Go through the chain of class hierarchy for the provided method and apply a function to the method to optional find some value. If the visitor * applied to the function does not find it, it will continue up the chain of classes until it has found it or there are no more classes to traverse. * * @param method the method to visit * @param methodVisitor the function that will try and find the value for the method * @param the type of value that is being searched for * @return the found value in an {@link Optional} otherwise an {@link Optional#empty()} is returned */ private Optional findForEachChainedMethodStack(final Method method, final Function> methodVisitor) { final Optional matchedValue = methodVisitor.apply(method); if (matchedValue.isPresent()) { return matchedValue; } final Class methodClass = method.getDeclaringClass(); final Class superClass = methodClass.getSuperclass(); if (superClass == Object.class || superClass == null) { return Optional.empty(); } try { final Method superClassMethod = superClass.getMethod(method.getName(), method.getParameterTypes()); return findForEachChainedMethodStack(superClassMethod, methodVisitor); } catch (final NoSuchMethodException noSuchMethodException) { return Optional.empty(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy