org.springframework.xd.dirt.plugins.stream.ModuleTypeConversionSupport.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.xd.dirt.plugins.stream.ModuleTypeConversionSupport.java

Source

/*
 * Copyright 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.xd.dirt.plugins.stream;

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

import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MimeType;
import org.springframework.util.StringUtils;
import org.springframework.xd.dirt.integration.bus.converter.CompositeMessageConverterFactory;
import org.springframework.xd.dirt.integration.bus.converter.ConversionException;
import org.springframework.xd.dirt.integration.bus.converter.MessageConverterUtils;
import org.springframework.xd.dirt.plugins.ModuleConfigurationException;
import org.springframework.xd.module.core.Module;
import org.springframework.xd.module.core.SimpleModule;

/**
 * Support class that configures the message converters for the module input/output channel.
 *
 * @author David Turanski
 * @author Ilayaperumal Gopinathan
 */
public class ModuleTypeConversionSupport {

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

    private final CompositeMessageConverterFactory converterFactory;

    private static final String INPUT_TYPE = "inputType";

    private static final String OUTPUT_TYPE = "outputType";

    public ModuleTypeConversionSupport(CompositeMessageConverterFactory converterFactory) {
        this.converterFactory = converterFactory;
    }

    /**
     * Return {@link org.springframework.util.MimeType} for the module's input channel.
     *
     * @param module the underlying module
     * @return MimeType for the module's input channel, null if not set
     */
    public static MimeType getInputMimeType(Module module) {
        String contentTypeString = module.getProperties().getProperty(INPUT_TYPE);
        return getMimeType(contentTypeString, module);
    }

    /**
     * Return {@link org.springframework.util.MimeType} for the module's output channel.
     *
     * @param module the underlying module
     * @return MimeType for the module's output channel, null if not set
     */
    public static MimeType getOutputMimeType(Module module) {
        String contentTypeString = module.getProperties().getProperty(OUTPUT_TYPE);
        return getMimeType(contentTypeString, module);
    }

    /**
     * Return {@link org.springframework.util.MimeType} for the given content type string.
     *
     * @param contentTypeString the content type string
     * @param module the underlying module
     * @return the MimeType for the content type string
     */
    private static MimeType getMimeType(String contentTypeString, Module module) {
        MimeType mimeType = null;
        if (StringUtils.hasText(contentTypeString)) {
            try {
                mimeType = resolveContentType(contentTypeString, module);
            } catch (ClassNotFoundException cfe) {
                throw new IllegalArgumentException("Could not find the class required for " + contentTypeString,
                        cfe);
            }
        }
        return mimeType;
    }

    /**
     * Configure message converters for the given module's input channel.
     *
     * @param module the underlying module
     */
    protected void configureModuleInputChannelMessageConverters(Module module) {
        MimeType contentType = getInputMimeType(module);
        if (contentType != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("configuring message converters for module " + module.getName() + "'s input channel.  "
                        + "Type is " + contentType);
            }
            Assert.isTrue((module instanceof SimpleModule),
                    "Module should be an instance of " + SimpleModule.class.getName());
            SimpleModule sm = (SimpleModule) module;
            try {
                AbstractMessageChannel channel = getChannel(module, true);
                configureMessageConverters(channel, contentType, sm.getApplicationContext().getClassLoader());
            } catch (Exception e) {
                throw new ModuleConfigurationException(e.getMessage(), e);
            }
        }
    }

    /**
     * Configure message converters for the given module's output channel.
     *
     * @param module the underlying module
     */
    protected void configureModuleOutputChannelMessageConverters(Module module) {
        MimeType contentType = getOutputMimeType(module);
        if (contentType != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("configuring message converters for module " + module.getName()
                        + "'s output channel.  " + "Type is " + contentType);
            }
            SimpleModule sm = (SimpleModule) module;
            try {
                AbstractMessageChannel channel = getChannel(module, false);
                configureMessageConverters(channel, contentType, sm.getApplicationContext().getClassLoader());
            } catch (Exception e) {
                throw new ModuleConfigurationException(e.getMessage(), e);
            }
        }
    }

    /**
     * Configure the message converters for the given message channel and content type.
     * Detect the data types from the provided class loader.
     *
     * @param channel the message channel to configure the message converters
     * @param contentType the content type to use
     * @param classLoader the classloader to search the dataType(s) classes
     */
    public void configureMessageConverters(AbstractMessageChannel channel, MimeType contentType,
            ClassLoader classLoader) {
        CompositeMessageConverter converters = null;
        try {
            converters = converterFactory.newInstance(contentType);
        } catch (ConversionException e) {
            throw new ModuleConfigurationException(
                    e.getMessage() + "(" + channel.getComponentName() + "Type=" + contentType + ")");
        }

        Class<?> dataType = MessageConverterUtils.getJavaTypeForContentType(contentType, classLoader);
        if (dataType == null) {
            throw new ModuleConfigurationException(
                    "Content type is not supported for " + channel.getComponentName() + "Type=" + contentType);
        } else {
            channel.setDatatypes(dataType);
            channel.setMessageConverter(converters);
        }

    }

    // Workaround for when the channel is proxied and can't be cast directly to AbstractMessageChannel
    private AbstractMessageChannel getChannel(Module module, boolean isInput) throws Exception {
        String name = isInput ? "input" : "output";
        Object channel = module.getComponent(name, Object.class);

        if (AopUtils.isJdkDynamicProxy(channel)) {
            return (AbstractMessageChannel) (((Advised) channel).getTargetSource().getTarget());
        }

        return (AbstractMessageChannel) channel;
    }

    /**
     * Resolve the {@link org.springframework.util.MimeType} to use for the given content type
     * specified for the module input/output.
     *
     * @param type the content type
     * @param module the underlying module
     * @return the MimeType
     * @throws ClassNotFoundException
     * @throws LinkageError
     */
    public static MimeType resolveContentType(String type, Module module)
            throws ClassNotFoundException, LinkageError {
        if (!type.contains("/")) {
            Class<?> javaType = resolveJavaType(type, module);
            return MessageConverterUtils.javaObjectMimeType(javaType);
        }
        return MimeType.valueOf(type);
    }

    private static Class<?> resolveJavaType(String type, Module module)
            throws ClassNotFoundException, LinkageError {
        Assert.isTrue((module instanceof SimpleModule),
                "Module should be an instance of " + SimpleModule.class.getName());
        SimpleModule sm = (SimpleModule) module;
        return ClassUtils.forName(type, sm.getApplicationContext().getClassLoader());
    }
}