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

Java tutorial

Introduction

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

Source

/*
 * Copyright 2013 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 static org.springframework.xd.module.ModuleType.processor;
import static org.springframework.xd.module.ModuleType.sink;
import static org.springframework.xd.module.ModuleType.source;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Properties;

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

import org.springframework.aop.framework.Advised;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.interceptor.WireTap;
import org.springframework.integration.x.bus.MessageBus;
import org.springframework.messaging.MessageChannel;
import org.springframework.util.CollectionUtils;
import org.springframework.xd.dirt.container.XDContainer;
import org.springframework.xd.module.BeanDefinitionAddingPostProcessor;
import org.springframework.xd.module.DeploymentMetadata;
import org.springframework.xd.module.Module;
import org.springframework.xd.module.ModuleType;
import org.springframework.xd.module.Plugin;

/**
 * @author Mark Fisher
 * @author Gary Russell
 * @author David Turanski
 * @author Jennifer Hickey
 * @author Glenn Renfro
 */
public class StreamPlugin implements Plugin {

    protected final Log logger = LogFactory.getLog(this.getClass());

    private static final String CONTEXT_CONFIG_ROOT = XDContainer.XD_CONFIG_ROOT + "plugins/stream/";

    private static final String TAP_CHANNEL_PREFIX = "tap:";

    private static final String MESSAGE_BUS = CONTEXT_CONFIG_ROOT + "message-bus.xml";

    private final static String CONTENT_TYPE_BEAN_NAME = "accepted-content-types";

    private final static Collection<MediaType> DEFAULT_ACCEPTED_CONTENT_TYPES = Collections
            .singletonList(MediaType.ALL);

    @Override
    public void preProcessModule(Module module) {
        ModuleType type = module.getType();
        DeploymentMetadata md = module.getDeploymentMetadata();
        if (source == type || processor == type || sink == type) {
            Properties properties = new Properties();
            properties.setProperty("xd.stream.name", md.getGroup());
            properties.setProperty("xd.module.index", String.valueOf(md.getIndex()));
            module.addProperties(properties);
        }
    }

    @Override
    public void postProcessModule(Module module) {
        MessageBus bus = findMessageBus(module);
        bindConsumer(module, bus);
        bindProducers(module, bus);
    }

    private MessageBus findMessageBus(Module module) {
        MessageBus messageBus = null;
        try {
            messageBus = module.getComponent(MessageBus.class);
        } catch (Exception e) {
            logger.error("No MessageBus in context, cannot wire/unwire channels: " + e.getMessage());
        }
        return messageBus;
    }

    private void bindConsumer(Module module, MessageBus bus) {
        DeploymentMetadata md = module.getDeploymentMetadata();
        MessageChannel channel = module.getComponent("input", MessageChannel.class);
        if (channel != null) {
            if (isChannelPubSub(md.getInputChannelName())) {
                bus.bindPubSubConsumer(md.getInputChannelName(), channel, getAcceptedMediaTypes(module));
            } else {
                bus.bindConsumer(md.getInputChannelName(), channel, getAcceptedMediaTypes(module),
                        md.isAliasedInput());
            }
        }
    }

    private void bindProducers(Module module, MessageBus bus) {
        DeploymentMetadata md = module.getDeploymentMetadata();
        MessageChannel channel = module.getComponent("output", MessageChannel.class);
        if (channel != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("binding output channel [" + md.getOutputChannelName() + "] for " + module);
            }
            if (isChannelPubSub(md.getOutputChannelName())) {
                bus.bindPubSubProducer(md.getOutputChannelName(), channel);
            } else {
                bus.bindProducer(md.getOutputChannelName(), channel, md.isAliasedOutput());
            }

            // TODO remove this once addInterceptor is an interface method in SI
            Object rawChannel = extractTarget(channel);

            // Create the tap channel now for possible future use (tap:mystream.mymodule)
            if (rawChannel instanceof AbstractMessageChannel) {
                String tapChannelName = getTapChannelName(module);
                DirectChannel tapChannel = new DirectChannel();
                tapChannel.setBeanName(tapChannelName + ".tap.bridge");
                ((AbstractMessageChannel) rawChannel).addInterceptor(new WireTap(tapChannel));
                bus.bindPubSubProducer(tapChannelName, tapChannel);
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("output channel is not an AbstractMessageChannel. Tap will not be created.");
                }
            }
        }
    }

    @Override
    public void beforeShutdown(Module module) {
        MessageBus bus = findMessageBus(module);
        if (bus != null) {
            unbindConsumer(module, bus);
            unbindProducers(module, bus);
        }
    }

    @Override
    public void removeModule(Module module) {
    }

    private void unbindConsumer(Module module, MessageBus bus) {
        MessageChannel inputChannel = module.getComponent("input", MessageChannel.class);
        if (inputChannel != null) {
            bus.unbindConsumer(module.getDeploymentMetadata().getInputChannelName(), inputChannel);
        }
    }

    private void unbindProducers(Module module, MessageBus bus) {
        MessageChannel outputChannel = module.getComponent("output", MessageChannel.class);
        if (outputChannel != null) {
            bus.unbindProducer(module.getDeploymentMetadata().getOutputChannelName(), outputChannel);
        }
        bus.unbindProducers(getTapChannelName(module));
    }

    private String getTapChannelName(Module module) {
        return TAP_CHANNEL_PREFIX + module.getDeploymentMetadata().getGroup() + "." + module.getName();
    }

    private boolean isChannelPubSub(String channelName) {
        return channelName != null && channelName.startsWith(TAP_CHANNEL_PREFIX);
    }

    private Collection<MediaType> getAcceptedMediaTypes(Module module) {
        Collection<?> acceptedTypes = module.getComponent(CONTENT_TYPE_BEAN_NAME, Collection.class);

        if (CollectionUtils.isEmpty(acceptedTypes)) {
            return DEFAULT_ACCEPTED_CONTENT_TYPES;
        } else {
            Collection<MediaType> acceptedMediaTypes = new ArrayList<MediaType>(acceptedTypes.size());
            for (Object acceptedType : acceptedTypes) {
                if (acceptedType instanceof String) {
                    acceptedMediaTypes.add(MediaType.valueOf((String) acceptedType));
                } else if (acceptedType instanceof MediaType) {
                    acceptedMediaTypes.add((MediaType) acceptedType);
                } else {
                    throw new IllegalArgumentException("Unrecognized MediaType :" + acceptedType);
                }
            }
            return Collections.unmodifiableCollection(acceptedMediaTypes);
        }
    }

    @Override
    public void preProcessSharedContext(ConfigurableApplicationContext context) {
        context.addBeanFactoryPostProcessor(new BeanDefinitionAddingPostProcessor(context.getEnvironment(),
                new ClassPathResource(MESSAGE_BUS)));
    }

    // TODO please get me out of this class, preferably by deleting when SI has addInterceptor in an interface
    private Object extractTarget(Object bean) {
        if (!(bean instanceof Advised)) {
            return bean;
        }
        Advised advised = (Advised) bean;
        if (advised.getTargetSource() == null) {
            return null;
        }
        try {
            return extractTarget(advised.getTargetSource().getTarget());
        } catch (Exception e) {
            logger.error("Could not extract target from output channel. Tap will not be created.", e);
            return null;
        }
    }
}