/* * Copyright 2002-2015 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.aop; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.integration.annotation.Publisher; import org.springframework.messaging.handler.annotation.Header; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * An {@link PublisherMetadataSource} implementation that retrieves the channel * name and expression strings from an annotation. * * @author Mark Fisher * @author Artem Bilan * @author Gareth Chapman * @since 2.0 */ public class MethodAnnotationPublisherMetadataSource implements PublisherMetadataSource { private final Set<Class<? extends Annotation>> annotationTypes; private volatile String channelAttributeName = "channel"; private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); public MethodAnnotationPublisherMetadataSource() { this(Collections.<Class<? extends Annotation>>singleton(Publisher.class)); } public MethodAnnotationPublisherMetadataSource(Set<Class<? extends Annotation>> annotationTypes) { Assert.notEmpty(annotationTypes, "annotationTypes must not be empty"); this.annotationTypes = annotationTypes; } public void setChannelAttributeName(String channelAttributeName) { Assert.hasText(channelAttributeName, "channelAttributeName must not be empty"); this.channelAttributeName = channelAttributeName; } public String getChannelName(Method method) { String channelName = this.getAnnotationValue(method, this.channelAttributeName, String.class); if (channelName == null) { channelName = this.getAnnotationValue(method.getDeclaringClass(), this.channelAttributeName, String.class); } return (StringUtils.hasText(channelName) ? channelName : null); } public String getPayloadExpression(Method method) { String payloadExpression = null; Annotation methodPayloadAnnotation = AnnotationUtils.findAnnotation(method, Payload.class); if (methodPayloadAnnotation != null) { payloadExpression = getAnnotationValue(methodPayloadAnnotation, null, String.class); if (!StringUtils.hasText(payloadExpression)) { payloadExpression = "#" + PublisherMetadataSource.RETURN_VALUE_VARIABLE_NAME; } } Annotation[][] annotationArray = method.getParameterAnnotations(); for (int i = 0; i < annotationArray.length; i++) { Annotation[] parameterAnnotations = annotationArray[i]; for (Annotation currentAnnotation : parameterAnnotations) { if (Payload.class.equals(currentAnnotation.annotationType())) { Assert.state(payloadExpression == null, "@Payload can be used at most once on a @Publisher method, " + "either at method-level or on a single parameter"); Assert.state("".equals(AnnotationUtils.getValue(currentAnnotation)), "@Payload on a parameter for a @Publisher method may not contain an expression"); payloadExpression = "#" + PublisherMetadataSource.ARGUMENT_MAP_VARIABLE_NAME + "[" + i + "]"; } } } if (payloadExpression == null || payloadExpression.contains("#" + PublisherMetadataSource.RETURN_VALUE_VARIABLE_NAME)) { Assert.isTrue(!void.class.equals(method.getReturnType()), "When defining @Publisher on a void-returning method, an explicit payload " + "expression that does not rely upon a #return value is required."); } return payloadExpression; } public Map<String, String> getHeaderExpressions(Method method) { Map<String, String> headerExpressions = new HashMap<String, String>(); String[] parameterNames = this.parameterNameDiscoverer.getParameterNames(method); Annotation[][] annotationArray = method.getParameterAnnotations(); for (int i = 0; i < annotationArray.length; i++) { Annotation[] parameterAnnotations = annotationArray[i]; for (Annotation currentAnnotation : parameterAnnotations) { if (Header.class.equals(currentAnnotation.annotationType())) { String name = getAnnotationValue(currentAnnotation, null, String.class); if (!StringUtils.hasText(name)) { name = parameterNames[i]; } headerExpressions.put(name, "#" + PublisherMetadataSource.ARGUMENT_MAP_VARIABLE_NAME + "[" + i + "]"); } } } return headerExpressions; } private <T> T getAnnotationValue(Method method, String attributeName, Class<T> expectedType) { T value = null; for (Class<? extends Annotation> annotationType : this.annotationTypes) { Annotation annotation = AnnotatedElementUtils.findMergedAnnotation(method, annotationType); if (annotation != null) { if (value != null) { throw new IllegalStateException( "method [" + method + "] contains more than one publisher annotation"); } value = this.getAnnotationValue(annotation, attributeName, expectedType); } } return value; } private <T> T getAnnotationValue(Class<?> clazz, String attributeName, Class<T> expectedType) { T value = null; for (Class<? extends Annotation> annotationType : this.annotationTypes) { Annotation annotation = AnnotatedElementUtils.findMergedAnnotation(clazz, annotationType); if (annotation != null) { if (value != null) { throw new IllegalStateException( "class [" + clazz + "] contains more than one publisher annotation"); } value = this.getAnnotationValue(annotation, attributeName, expectedType); } } return value; } @SuppressWarnings("unchecked") private <T> T getAnnotationValue(Annotation annotation, String attributeName, Class<T> expectedType) { T value = null; Object valueAsObject = (attributeName == null) ? AnnotationUtils.getValue(annotation) : AnnotationUtils.getValue(annotation, attributeName); if (valueAsObject != null) { if (expectedType.isAssignableFrom(valueAsObject.getClass())) { value = (T) valueAsObject; } else { throw new IllegalArgumentException("expected type [" + expectedType.getName() + "] for attribute '" + attributeName + "' on publisher annotation [" + annotation.annotationType() + "], but actual type was [" + valueAsObject.getClass() + "]"); } } return value; } }