org.springframework.integration.x.gemfire.GemFireMessageBus.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.x.gemfire.GemFireMessageBus.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.integration.x.gemfire;

import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.distributed.DistributedMember;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.http.MediaType;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.x.bus.Binding;
import org.springframework.integration.x.bus.MessageBusSupport;
import org.springframework.integration.x.bus.serializer.MultiTypeCodec;
import org.springframework.messaging.*;
import org.springframework.util.Assert;

import java.util.Collection;
import java.util.Map;

import static com.gemstone.gemfire.cache.partition.PartitionRegionHelper.getPrimaryMemberForKey;
import static com.gemstone.gemfire.cache.partition.PartitionRegionHelper.isPartitionedRegion;

/**
 * @author Charlie Black
 */
public class GemFireMessageBus extends MessageBusSupport implements DisposableBean {
    public static final String PREFIX = "__spring_xd_";
    public static final String PARTITION_KEY = "partition_key";
    public static final String DESTINATION_REGION = "destination_region";
    private final Log logger = LogFactory.getLog(this.getClass());

    public GemFireMessageBus(MultiTypeCodec<Object> codec) {
        setCodec(codec);
    }

    //incoming queue
    @Override
    public void bindConsumer(String name, MessageChannel moduleInputChannel,
            Collection<MediaType> acceptedMediaTypes, boolean aliasHint) {
        String messageChannelName = String.format("%s%s.queue", PREFIX, name);
        MessageProducerSupport messageProducerSupport = GemFireRPCMessaging
                .createMessageProducer(messageChannelName);
        doRegisterConsumer(name, moduleInputChannel, acceptedMediaTypes, messageProducerSupport);
    }

    //incomming topic
    @Override
    public void bindPubSubConsumer(String name, MessageChannel inputChannel,
            Collection<MediaType> acceptedMediaTypes) {
        String messageChannelName = String.format("%s%s.topic", PREFIX, name);
        MessageProducerSupport messageProducerSupport = GemFireRPCMessaging
                .createMessageProducer(messageChannelName);
        doRegisterConsumer(name, inputChannel, acceptedMediaTypes, messageProducerSupport);

    }

    //outgoing queue
    @Override
    public void bindProducer(String name, MessageChannel outputChannel, boolean aliasHint) {
        doRegisterProducer(name, outputChannel, new QueueMessageSender(String.format("%s%s.queue", PREFIX, name)));
    }

    //outgoing topic
    @Override
    public void bindPubSubProducer(String name, MessageChannel outputChannel) {
        doRegisterProducer(name, outputChannel, new TopicMessageSender(String.format("%s%s.topic", PREFIX, name)));
    }

    @Override
    public void destroy() throws Exception {
        stopBindings();
    }

    private void doRegisterConsumer(String name, MessageChannel moduleInputChannel,
            final Collection<MediaType> acceptedMediaTypes, MessageProducerSupport adapter) {
        DirectChannel bridgeToModuleChannel = new DirectChannel();
        bridgeToModuleChannel.setBeanName(name + ".bridge");
        adapter.setOutputChannel(bridgeToModuleChannel);
        adapter.setBeanName("inbound." + name);
        adapter.afterPropertiesSet();
        addBinding(Binding.forConsumer(adapter, moduleInputChannel));
        GemFireRPCListener convertingBridge = new GemFireRPCListener(acceptedMediaTypes);
        convertingBridge.setOutputChannel(moduleInputChannel);
        convertingBridge.setBeanName(name + ".convert.bridge");
        convertingBridge.afterPropertiesSet();
        bridgeToModuleChannel.subscribe(convertingBridge);
        adapter.start();
    }

    private void doRegisterProducer(final String name, MessageChannel moduleOutputChannel, MessageHandler handler) {
        Assert.isInstanceOf(SubscribableChannel.class, moduleOutputChannel);
        EventDrivenConsumer consumer = new EventDrivenConsumer((SubscribableChannel) moduleOutputChannel, handler);
        consumer.setBeanName("outbound." + name);
        consumer.afterPropertiesSet();
        addBinding(Binding.forProducer(moduleOutputChannel, consumer));
        consumer.start();
    }

    private class QueueMessageSender implements MessageHandler {
        private String name;

        private QueueMessageSender(String name) {
            this.name = name;
        }

        @Override
        public void handleMessage(Message<?> message) throws MessagingException {
            DistributedMember member = null;
            MessageHeaders messageHeaders = message.getHeaders();
            if (logger.isDebugEnabled()) {
                logger.debug("GemFireMessageBus.handleMessage");
                for (Map.Entry<String, Object> entry : messageHeaders.entrySet()) {
                    logger.debug(String.format("entry.getKey() = %s\n\tentry.getValue() = %s", entry.getKey(),
                            entry.getValue()));
                }
            }
            Object destinationRegionName = messageHeaders.get(DESTINATION_REGION);
            if (destinationRegionName != null) {
                Region region = CacheFactory.getAnyInstance().getRegion((String) destinationRegionName);
                Object key = messageHeaders.get(PARTITION_KEY);
                if (key != null && region != null) {
                    if (isPartitionedRegion(region)) {
                        member = getPrimaryMemberForKey(region, key);
                    }
                }
            }
            if (member == null) {
                //TODO : Maybe come up with some kind of round robin approach.  For now lets focus on NO Network HOPs.
                member = CacheFactory.getAnyInstance().getDistributedSystem().getDistributedMember();
            }
            GemFireRPCMessaging.send(member, name, message);
        }
    }

    private class TopicMessageSender implements MessageHandler {
        private String name;

        private TopicMessageSender(String name) {
            this.name = name;
        }

        @Override
        public void handleMessage(Message<?> message) throws MessagingException {
            GemFireRPCMessaging.send(name, message);
        }
    }

    private class GemFireRPCListener extends AbstractReplyProducingMessageHandler {
        private Collection<MediaType> acceptedMediaTypes;

        private GemFireRPCListener(Collection<MediaType> acceptedMediaTypes) {
            this.acceptedMediaTypes = acceptedMediaTypes;
        }

        @Override
        protected Object handleRequestMessage(Message<?> requestMessage) {
            return transformPayloadForConsumerIfNecessary(requestMessage, acceptedMediaTypes);
        }

        @Override
        protected boolean shouldCopyRequestHeaders() {
            /*
             * we've already copied the headers so no need for the ARPMH to do it, and we don't want the content-type
            * restored if absent.
            */
            return false;
        }
    }

}