/* * Copyright 2014-2016 the original author or authors. * * 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.springframework.integration.util; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.springframework.aop.support.AopUtils; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.integration.annotation.Payloads; import org.springframework.messaging.MessagingException; import org.springframework.messaging.handler.annotation.Header; import org.springframework.messaging.handler.annotation.Headers; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** * Utility methods to support annotation processing. * * @author Gary Russell * @author Dave Syer * @author Gunnar Hillert * @author Soby Chacko * @author Artem Bilan * @since 4.0 */ public final class MessagingAnnotationUtils { /** * Get the attribute value from the annotation hierarchy, returning the first non-empty * value closest to the annotated method. While traversing up the hierarchy, for string-valued * attributes, an empty string is ignored. For array-valued attributes, an empty * array is ignored. * The overridden attribute must be the same type. * @param annotations The meta-annotations in order (closest first). * @param name The attribute name. * @param requiredType The expected type. * @param <T> The type. * @return The value. */ @SuppressWarnings("unchecked") public static <T> T resolveAttribute(List<Annotation> annotations, String name, Class<T> requiredType) { for (Annotation annotation : annotations) { if (annotation != null) { Object value = AnnotationUtils.getValue(annotation, name); if (value != null && value.getClass() == requiredType && hasValue(value)) { return (T) value; } } } return null; } public static boolean hasValue(Object value) { return value != null && (!(value instanceof String) || (StringUtils.hasText((String) value))) && (!value.getClass().isArray() || ((Object[]) value).length > 0); } public static Method findAnnotatedMethod(Object target, final Class<? extends Annotation> annotationType) { final AtomicReference<Method> reference = new AtomicReference<Method>(); ReflectionUtils.doWithMethods(getTargetClass(target), method -> reference.compareAndSet(null, method), method -> ReflectionUtils.USER_DECLARED_METHODS.matches(method) && AnnotatedElementUtils.isAnnotated(method, annotationType.getName())); return reference.get(); } /** * Find the one of {@link Payload}, {@link Header} or {@link Headers} annotation from * the provided {@code annotations} array. Optionally also detects {@link Payloads}. * @param annotations the annotations to scan. * @param payloads true if @Payloads should be detected. * @return the matched annotation or {@code null}. * @throws MessagingException if more than one of {@link Payload}, {@link Header} * or {@link Headers} annotations are presented. */ public static Annotation findMessagePartAnnotation(Annotation[] annotations, boolean payloads) { if (annotations == null || annotations.length == 0) { return null; } Annotation match = null; for (Annotation annotation : annotations) { Class<? extends Annotation> type = annotation.annotationType(); if (type.equals(Payload.class) || type.equals(Header.class) || type.equals(Headers.class) || (payloads && type.equals(Payloads.class))) { if (match != null) { throw new MessagingException("At most one parameter annotation can be provided " + "for message mapping, but found two: [" + match.annotationType().getName() + "] and [" + annotation.annotationType().getName() + "]"); } match = annotation; } } return match; } private static Class<?> getTargetClass(Object targetObject) { Class<?> targetClass = targetObject.getClass(); if (AopUtils.isAopProxy(targetObject)) { targetClass = AopUtils.getTargetClass(targetObject); } else if (ClassUtils.isCglibProxyClass(targetClass)) { Class<?> superClass = targetObject.getClass().getSuperclass(); if (!Object.class.equals(superClass)) { targetClass = superClass; } } return targetClass; } private MessagingAnnotationUtils() { } }