Refactored from @HeaderAttribute and @HeaderProperty to a single @Header annotation now that MessageHeaders contains a single map (INT-306).

This commit is contained in:
Mark Fisher
2008-07-18 17:33:13 +00:00
parent a3b1c59d10
commit 9487837186
7 changed files with 84 additions and 160 deletions

View File

@@ -24,7 +24,6 @@ import org.springframework.core.GenericTypeResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.integration.ConfigurationException;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageHandlingException;
import org.springframework.integration.message.MessageHeaders;
@@ -35,17 +34,16 @@ import org.springframework.util.StringUtils;
/**
* A {@link MessageMapper} implementation for annotated handler methods.
* Method parameters are matched against the Message payload as well as its
* header attributes and properties. If a method parameter is annotated with
* {@link HeaderAttribute @HeaderAttribute} or {@link HeaderProperty @HeaderProperty},
* the annotation's value will be used as an attribute/property key. If such an
* annotation contains no value, then the parameter name will be used as long as
* headers. If a method parameter is annotated with {@link Header @Header},
* the annotation's value will be used as a header name. If such an annotation
* contains no value, then the parameter name will be used as long as
* the information is available in the class file (requires compilation with
* debug settings for parameter names). If neither annotation is present, then
* the parameter will typically match the Message payload. However, if a Map or
* Properties object is expected, and the paylaod is not itself assignable to
* that type, then the MessageHeader attributes will be passed in the case of
* a Map-typed parameter, or the MessageHeader properties will be passed in the
* case of a Properties-typed parameter.
* that type, then the MessageHeaders' values will be passed in the case of
* a Map-typed parameter, or the MessageHeaders' String-based values will be
* passed in the case of a Properties-typed parameter.
*
* @author Mark Fisher
*/
@@ -79,27 +77,16 @@ public class AnnotationMethodMessageMapper implements MessageMapper {
MethodParameter methodParam = new MethodParameter(this.method, i);
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(methodParam, this.method.getDeclaringClass());
Object[] paramAnns = methodParam.getParameterAnnotations();
String attributeName = null;
String propertyName = null;
for (int j = 0; j < paramAnns.length; j++) {
Object paramAnn = paramAnns[j];
if (HeaderAttribute.class.isInstance(paramAnn)) {
HeaderAttribute headerAttribute = (HeaderAttribute) paramAnn;
attributeName = this.resolveParameterNameIfNecessary(headerAttribute.value(), methodParam);
parameterMetadata[i] = new MethodParameterMetadata(HeaderAttribute.class, attributeName, headerAttribute.required());
}
else if (HeaderProperty.class.isInstance(paramAnn)) {
HeaderProperty headerProperty = (HeaderProperty) paramAnn;
propertyName = this.resolveParameterNameIfNecessary(headerProperty.value(), methodParam);
parameterMetadata[i] = new MethodParameterMetadata(HeaderProperty.class, propertyName, headerProperty.required());
Object[] paramAnnotations = methodParam.getParameterAnnotations();
String headerName = null;
for (int j = 0; j < paramAnnotations.length; j++) {
if (Header.class.isInstance(paramAnnotations[j])) {
Header headerAnnotation = (Header) paramAnnotations[j];
headerName = this.resolveParameterNameIfNecessary(headerAnnotation.value(), methodParam);
parameterMetadata[i] = new MethodParameterMetadata(Header.class, headerName, headerAnnotation.required());
}
}
if (attributeName != null && propertyName != null) {
throw new ConfigurationException("The @HeaderAttribute and @HeaderProperty annotations " +
"are mutually exclusive. They should not both be provided on the same parameter.");
}
if (attributeName == null && propertyName == null) {
if (headerName == null) {
parameterMetadata[i] = new MethodParameterMetadata(methodParam.getParameterType(), null, false);
}
}
@@ -121,19 +108,11 @@ public class AnnotationMethodMessageMapper implements MessageMapper {
for (int i = 0; i < this.parameterMetadata.length; i++) {
MethodParameterMetadata metadata = this.parameterMetadata[i];
Class<?> expectedType = metadata.type;
if (expectedType.equals(HeaderAttribute.class)) {
if (expectedType.equals(Header.class)) {
Object value = message.getHeaders().get(metadata.key);
if (value == null && metadata.required) {
throw new MessageHandlingException(message,
"required attribute '" + metadata.key + "' not available");
}
args[i] = value;
}
else if (expectedType.equals(HeaderProperty.class)) {
Object value = message.getHeaders().get(metadata.key);
if (value == null && metadata.required) {
throw new MessageHandlingException(message,
"required property '" + metadata.key + "' not available");
"required header '" + metadata.key + "' not available");
}
args[i] = value;
}

View File

@@ -24,17 +24,17 @@ import java.lang.annotation.Target;
/**
* Annotation indicating that a method parameter's value should be
* retrieved from an attribute in the message header. The value of
* the annotation provides the attribute key, and the optional
* 'required' property specifies whether the attribute value must
* be available within the header.
* retrieved from the message headers. The value of the annotation
* provides the header name, and the optional 'required' property
* specifies whether the attribute value must be available within
* the header. The default value for 'required' is <code>true</code>.
*
* @author Mark Fisher
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HeaderAttribute {
public @interface Header {
String value() default "";

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2002-2008 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.handler.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation indicating that a method parameter's value should be
* retrieved from a property in the message header. The value of
* the annotation provides the property key, and the optional
* 'required' property specifies whether the property value must
* be available within the header.
*
* @author Mark Fisher
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HeaderProperty {
String value() default "";
boolean required() default true;
}

View File

@@ -26,7 +26,7 @@ import org.springframework.core.Ordered;
import org.springframework.integration.ConfigurationException;
/**
* An base class for adapters that invoke a specified method and target object.
* A base class for adapters that invoke a specified method and target object.
* Either a {@link Method} reference or a 'methodName' may be provided, but both
* are not necessary. In fact, while preference is given to a {@link Method}
* reference if available, an Exception will be thrown if a non-matching