Java tutorial
/* * 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.rabbit; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.beans.factory.DisposableBean; import org.springframework.context.Lifecycle; import org.springframework.http.MediaType; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter; import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint; import org.springframework.integration.amqp.support.DefaultAmqpHeaderMapper; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.context.IntegrationObjectSupport; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.core.SubscribableChannel; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.mapping.AbstractHeaderMapper; import org.springframework.integration.x.channel.registry.ChannelRegistry; import org.springframework.integration.x.channel.registry.ChannelRegistrySupport; import org.springframework.util.Assert; /** * A {@link ChannelRegistry} implementation backed by RabbitMQ. * * @author Mark Fisher * @author Gary Russell */ public class RabbitChannelRegistry extends ChannelRegistrySupport implements DisposableBean { private final Log logger = LogFactory.getLog(this.getClass()); private final RabbitAdmin rabbitAdmin; private final RabbitTemplate rabbitTemplate = new RabbitTemplate(); private final ConnectionFactory connectionFactory; private volatile Integer concurrentConsumers; private final List<Lifecycle> lifecycleBeans = Collections.synchronizedList(new ArrayList<Lifecycle>()); private final DefaultAmqpHeaderMapper mapper; public RabbitChannelRegistry(ConnectionFactory connectionFactory) { Assert.notNull(connectionFactory, "connectionFactory must not be null"); this.connectionFactory = connectionFactory; this.rabbitTemplate.setConnectionFactory(connectionFactory); this.rabbitTemplate.afterPropertiesSet(); this.rabbitAdmin = new RabbitAdmin(connectionFactory); this.rabbitAdmin.afterPropertiesSet(); this.mapper = new DefaultAmqpHeaderMapper(); this.mapper.setRequestHeaderNames(new String[] { AbstractHeaderMapper.STANDARD_REQUEST_HEADER_NAME_PATTERN, ORIGINAL_CONTENT_TYPE_HEADER }); } @Override public void createInbound(final String name, MessageChannel moduleInputChannel, final Collection<MediaType> acceptedMediaTypes, boolean aliasHint) { if (logger.isInfoEnabled()) { logger.info("declaring queue for inbound: " + name); } this.rabbitAdmin.declareQueue(new Queue(name)); SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer( this.connectionFactory); if (this.concurrentConsumers != null) { listenerContainer.setConcurrentConsumers(this.concurrentConsumers); } listenerContainer.setQueueNames(name); listenerContainer.afterPropertiesSet(); AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(listenerContainer); DirectChannel bridgeToModuleChannel = new DirectChannel(); bridgeToModuleChannel.setBeanName(name + ".bridge"); adapter.setOutputChannel(bridgeToModuleChannel); adapter.setHeaderMapper(this.mapper); adapter.setBeanName("inbound." + name); adapter.afterPropertiesSet(); this.lifecycleBeans.add(adapter); ReceivingHandler convertingBridge = new ReceivingHandler(acceptedMediaTypes); convertingBridge.setOutputChannel(moduleInputChannel); convertingBridge.setBeanName(name + ".convert.bridge"); convertingBridge.afterPropertiesSet(); bridgeToModuleChannel.subscribe(convertingBridge); adapter.start(); } @Override public void createOutbound(final String name, MessageChannel moduleOutputChannel, boolean aliasHint) { Assert.isInstanceOf(SubscribableChannel.class, moduleOutputChannel); MessageHandler handler = new CompositeHandler(name); EventDrivenConsumer consumer = new EventDrivenConsumer((SubscribableChannel) moduleOutputChannel, handler); consumer.setBeanName("outbound." + name); consumer.afterPropertiesSet(); this.lifecycleBeans.add(consumer); consumer.start(); } @Override public void tap(String tapModule, final String name, MessageChannel tapModuleInputChannel) { SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer( this.connectionFactory); if (this.concurrentConsumers != null) { listenerContainer.setConcurrentConsumers(this.concurrentConsumers); } Queue queue = this.rabbitAdmin.declareQueue(); Binding binding = BindingBuilder.bind(queue).to(new FanoutExchange("tap." + name)); this.rabbitAdmin.declareBinding(binding); listenerContainer.setQueues(queue); listenerContainer.afterPropertiesSet(); AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(listenerContainer); DirectChannel bridgeToTapChannel = new DirectChannel(); bridgeToTapChannel.setBeanName(tapModule + ".bridge"); adapter.setOutputChannel(bridgeToTapChannel); adapter.setHeaderMapper(this.mapper); adapter.setBeanName("tap." + name); adapter.setComponentName(tapModule + "." + "tapAdapter"); adapter.afterPropertiesSet(); this.lifecycleBeans.add(adapter); // TODO: media types need to be passed in by Tap. Collection<MediaType> acceptedMediaTypes = Collections.singletonList(MediaType.ALL); ReceivingHandler convertingBridge = new ReceivingHandler(acceptedMediaTypes); convertingBridge.setOutputChannel(tapModuleInputChannel); convertingBridge.setBeanName(name + ".convert.bridge"); convertingBridge.afterPropertiesSet(); bridgeToTapChannel.subscribe(convertingBridge); adapter.start(); } @Override public void deleteInbound(String name) { synchronized (this.lifecycleBeans) { Iterator<Lifecycle> iterator = this.lifecycleBeans.iterator(); while (iterator.hasNext()) { Lifecycle endpoint = iterator.next(); if (endpoint instanceof AmqpInboundChannelAdapter) { String componentName = ((IntegrationObjectSupport) endpoint).getComponentName(); if (("inbound." + name).equals(componentName) || (name + ".tapAdapter").equals(componentName)) { ((AmqpInboundChannelAdapter) endpoint).stop(); iterator.remove(); } } } } } @Override public void deleteOutbound(String name) { synchronized (this.lifecycleBeans) { Iterator<Lifecycle> iterator = this.lifecycleBeans.iterator(); while (iterator.hasNext()) { Lifecycle endpoint = iterator.next(); if (endpoint instanceof EventDrivenConsumer && ("outbound." + name).equals(((IntegrationObjectSupport) endpoint).getComponentName())) { ((EventDrivenConsumer) endpoint).stop(); iterator.remove(); return; } } } } @Override public void destroy() { for (Lifecycle bean : this.lifecycleBeans) { try { bean.stop(); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("failed to stop adapter", e); } } } } private class CompositeHandler extends AbstractMessageHandler { private final AmqpOutboundEndpoint queue; private final AmqpOutboundEndpoint tap; private CompositeHandler(String name) { if (logger.isInfoEnabled()) { logger.info("declaring queue for outbound: " + name); } rabbitAdmin.declareQueue(new Queue(name)); rabbitAdmin.declareExchange(new FanoutExchange("tap." + name)); this.queue = new AmqpOutboundEndpoint(rabbitTemplate); queue.setRoutingKey(name); // uses default exchange queue.setHeaderMapper(mapper); queue.afterPropertiesSet(); this.tap = new AmqpOutboundEndpoint(rabbitTemplate); tap.setExchangeName("tap." + name); tap.setHeaderMapper(mapper); tap.afterPropertiesSet(); } @Override protected void handleMessageInternal(Message<?> message) throws Exception { // TODO: rabbit wire data pluggable format? Message<?> messageToSend = transformOutboundIfNecessary(message, MediaType.APPLICATION_OCTET_STREAM); this.tap.handleMessage(messageToSend); this.queue.handleMessage(messageToSend); } } private class ReceivingHandler extends AbstractReplyProducingMessageHandler { private final Collection<MediaType> acceptedMediaTypes; public ReceivingHandler(Collection<MediaType> acceptedMediaTypes) { this.acceptedMediaTypes = acceptedMediaTypes; } @Override protected Object handleRequestMessage(Message<?> requestMessage) { return transformInboundIfNecessary(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; } }; }