org.springframework.integration.config.MessagingGatewayRegistrar.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.config.MessagingGatewayRegistrar.java

Source

/*
 * Copyright 2014 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.config;

import java.beans.Introspector;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.config.annotation.MessagingAnnotationUtils;
import org.springframework.integration.gateway.GatewayMethodMetadata;
import org.springframework.integration.gateway.GatewayProxyFactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * The {@link ImportBeanDefinitionRegistrar} to parse {@link MessagingGateway} and its {@code service-interface}
 * and to register {@link BeanDefinition} {@link GatewayProxyFactoryBean}.
 *
 * @author Artem Bilan
 * @author Gary Russell
 * @since 4.0
 */
public class MessagingGatewayRegistrar implements ImportBeanDefinitionRegistrar {

    private static final Log logger = LogFactory.getLog(MessagingGatewayRegistrar.class);

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
            BeanDefinitionRegistry registry) {
        if (importingClassMetadata != null
                && importingClassMetadata.isAnnotated(MessagingGateway.class.getName())) {
            Assert.isTrue(importingClassMetadata.isInterface(),
                    "@MessagingGateway can only be specified on an interface");
            List<MultiValueMap<String, Object>> valuesHierarchy = captureMetaAnnotationValues(
                    importingClassMetadata);
            Map<String, Object> annotationAttributes = importingClassMetadata
                    .getAnnotationAttributes(MessagingGateway.class.getName());
            replaceEmptyOverrides(valuesHierarchy, annotationAttributes);
            annotationAttributes.put("serviceInterface", importingClassMetadata.getClassName());

            BeanDefinitionReaderUtils.registerBeanDefinition(this.parse(annotationAttributes), registry);
        }
    }

    public BeanDefinitionHolder parse(Map<String, Object> gatewayAttributes) {

        String defaultPayloadExpression = (String) gatewayAttributes.get("defaultPayloadExpression");

        @SuppressWarnings("unchecked")
        Map<String, Object>[] defaultHeaders = (Map<String, Object>[]) gatewayAttributes.get("defaultHeaders");

        String defaultRequestChannel = (String) gatewayAttributes.get("defaultRequestChannel");
        String defaultReplyChannel = (String) gatewayAttributes.get("defaultReplyChannel");
        String errorChannel = (String) gatewayAttributes.get("errorChannel");
        String asyncExecutor = (String) gatewayAttributes.get("asyncExecutor");
        String mapper = (String) gatewayAttributes.get("mapper");

        boolean hasMapper = StringUtils.hasText(mapper);
        boolean hasDefaultPayloadExpression = StringUtils.hasText(defaultPayloadExpression);
        Assert.state(!hasMapper || !hasDefaultPayloadExpression,
                "'defaultPayloadExpression' is not allowed when a 'mapper' is provided");

        boolean hasDefaultHeaders = !ObjectUtils.isEmpty(defaultHeaders);
        Assert.state(!hasMapper || !hasDefaultHeaders,
                "'defaultHeaders' are not allowed when a 'mapper' is provided");

        BeanDefinitionBuilder gatewayProxyBuilder = BeanDefinitionBuilder
                .genericBeanDefinition(GatewayProxyFactoryBean.class);

        if (hasDefaultHeaders || hasDefaultPayloadExpression) {
            BeanDefinitionBuilder methodMetadataBuilder = BeanDefinitionBuilder
                    .genericBeanDefinition(GatewayMethodMetadata.class);
            if (hasDefaultPayloadExpression) {
                methodMetadataBuilder.addPropertyValue("payloadExpression", defaultPayloadExpression);
            }
            Map<String, Object> headerExpressions = new ManagedMap<String, Object>();
            for (Map<String, Object> header : defaultHeaders) {
                String headerValue = (String) header.get("value");
                String headerExpression = (String) header.get("expression");
                boolean hasValue = StringUtils.hasText(headerValue);

                if (!(hasValue ^ StringUtils.hasText(headerExpression))) {
                    throw new BeanDefinitionStoreException(
                            "exactly one of 'value' or 'expression' " + "is required on a gateway's header.");
                }

                BeanDefinition expressionDef = new RootBeanDefinition(
                        hasValue ? LiteralExpression.class : ExpressionFactoryBean.class);
                expressionDef.getConstructorArgumentValues()
                        .addGenericArgumentValue(hasValue ? headerValue : headerExpression);

                headerExpressions.put((String) header.get("name"), expressionDef);
            }
            methodMetadataBuilder.addPropertyValue("headerExpressions", headerExpressions);
            gatewayProxyBuilder.addPropertyValue("globalMethodMetadata", methodMetadataBuilder.getBeanDefinition());
        }

        if (StringUtils.hasText(defaultRequestChannel)) {
            gatewayProxyBuilder.addPropertyReference("defaultRequestChannel", defaultRequestChannel);
        }
        if (StringUtils.hasText(defaultReplyChannel)) {
            gatewayProxyBuilder.addPropertyReference("defaultReplyChannel", defaultReplyChannel);
        }
        if (StringUtils.hasText(errorChannel)) {
            gatewayProxyBuilder.addPropertyReference("errorChannel", errorChannel);
        }
        if (StringUtils.hasText(asyncExecutor)) {
            gatewayProxyBuilder.addPropertyReference("asyncExecutor", asyncExecutor);
        }
        if (StringUtils.hasText(mapper)) {
            gatewayProxyBuilder.addPropertyReference("mapper", mapper);
        }

        gatewayProxyBuilder.addPropertyValue("defaultRequestTimeout",
                gatewayAttributes.get("defaultRequestTimeout"));
        gatewayProxyBuilder.addPropertyValue("defaultReplyTimeout", gatewayAttributes.get("defaultReplyTimeout"));
        gatewayProxyBuilder.addPropertyValue("methodMetadataMap", gatewayAttributes.get("methods"));

        String serviceInterface = (String) gatewayAttributes.get("serviceInterface");
        if (!StringUtils.hasText(serviceInterface)) {
            serviceInterface = "org.springframework.integration.gateway.RequestReplyExchanger";
        }
        String id = (String) gatewayAttributes.get("name");
        if (!StringUtils.hasText(id)) {
            id = Introspector.decapitalize(serviceInterface.substring(serviceInterface.lastIndexOf(".") + 1));
        }

        gatewayProxyBuilder.addConstructorArgValue(serviceInterface);

        return new BeanDefinitionHolder(gatewayProxyBuilder.getBeanDefinition(), id);
    }

    /**
     * TODO until SPR-11710 will be resolved.
     * Captures the meta-annotation attribute values, in order.
     * @param importingClassMetadata The importing class metadata
     * @return The captured values.
     */
    private List<MultiValueMap<String, Object>> captureMetaAnnotationValues(
            AnnotationMetadata importingClassMetadata) {
        Set<String> directAnnotations = importingClassMetadata.getAnnotationTypes();
        List<MultiValueMap<String, Object>> valuesHierarchy = new ArrayList<MultiValueMap<String, Object>>();
        // Need to grab the values now; see SPR-11710
        for (String ann : directAnnotations) {
            Set<String> chain = importingClassMetadata.getMetaAnnotationTypes(ann);
            if (chain.contains(MessagingGateway.class.getName())) {
                for (String meta : chain) {
                    valuesHierarchy.add(importingClassMetadata.getAllAnnotationAttributes(meta));
                }
            }
        }
        return valuesHierarchy;
    }

    /**
     * TODO until SPR-11709 will be resolved.
     * For any empty values, traverses back up the meta-annotation hierarchy to
     * see if a value has been overridden to empty, and replaces the first such value found.
     * @param valuesHierarchy The values hierarchy in order.
     * @param annotationAttributes The current attribute values.
     */
    private void replaceEmptyOverrides(List<MultiValueMap<String, Object>> valuesHierarchy,
            Map<String, Object> annotationAttributes) {
        for (Entry<String, Object> entry : annotationAttributes.entrySet()) {
            Object value = entry.getValue();
            if (!MessagingAnnotationUtils.hasValue(value)) {
                // see if we overrode a value that was higher in the annotation chain
                for (MultiValueMap<String, Object> metaAttributesMap : valuesHierarchy) {
                    Object newValue = metaAttributesMap.getFirst(entry.getKey());
                    if (MessagingAnnotationUtils.hasValue(newValue)) {
                        annotationAttributes.put(entry.getKey(), newValue);
                        break;
                    }
                }
            }
        }
    }

}