org.springframework.integration.jms.SubscribableJmsChannel.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.jms.SubscribableJmsChannel.java

Source

/*
 * Copyright 2002-2019 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
 *
 *      https://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.jms;

import javax.jms.MessageListener;

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

import org.springframework.context.SmartLifecycle;
import org.springframework.integration.MessageDispatchingException;
import org.springframework.integration.context.IntegrationProperties;
import org.springframework.integration.dispatcher.AbstractDispatcher;
import org.springframework.integration.dispatcher.BroadcastingDispatcher;
import org.springframework.integration.dispatcher.MessageDispatcher;
import org.springframework.integration.dispatcher.RoundRobinLoadBalancingStrategy;
import org.springframework.integration.dispatcher.UnicastingDispatcher;
import org.springframework.integration.support.MessageBuilderFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.listener.AbstractMessageListenerContainer;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.util.Assert;

/**
 * @author Mark Fisher
 * @author Gary Russell
 * @author Artem Bilan
 *
 * @since 2.0
 */
public class SubscribableJmsChannel extends AbstractJmsChannel implements SubscribableChannel, SmartLifecycle {

    private final AbstractMessageListenerContainer container;

    private volatile AbstractDispatcher dispatcher;

    private volatile boolean initialized;

    private volatile Integer maxSubscribers;

    public SubscribableJmsChannel(AbstractMessageListenerContainer container, JmsTemplate jmsTemplate) {
        super(jmsTemplate);
        Assert.notNull(container, "container must not be null");
        this.container = container;
    }

    /**
     * Specify the maximum number of subscribers supported by the
     * channel's dispatcher.
     * @param maxSubscribers The maximum number of subscribers allowed.
     */
    public void setMaxSubscribers(int maxSubscribers) {
        this.maxSubscribers = maxSubscribers;
    }

    @Override
    public boolean subscribe(MessageHandler handler) {
        Assert.state(this.dispatcher != null,
                "'MessageDispatcher' must not be null. This channel might not have been initialized");
        return this.dispatcher.addHandler(handler);
    }

    @Override
    public boolean unsubscribe(MessageHandler handler) {
        Assert.state(this.dispatcher != null,
                "'MessageDispatcher' must not be null. This channel might not have been initialized");
        return this.dispatcher.removeHandler(handler);
    }

    @Override
    public void onInit() {
        if (this.initialized) {
            return;
        }
        super.onInit();
        boolean isPubSub = this.container.isPubSubDomain();
        this.configureDispatcher(isPubSub);
        MessageListener listener = new DispatchingMessageListener(this.getJmsTemplate(), this.dispatcher, this,
                isPubSub, this.getMessageBuilderFactory());
        this.container.setMessageListener(listener);
        if (!this.container.isActive()) {
            this.container.afterPropertiesSet();
        }
        this.initialized = true;
    }

    private void configureDispatcher(boolean isPubSub) {
        if (isPubSub) {
            BroadcastingDispatcher broadcastingDispatcher = new BroadcastingDispatcher(true);
            broadcastingDispatcher.setBeanFactory(this.getBeanFactory());
            this.dispatcher = broadcastingDispatcher;
        } else {
            UnicastingDispatcher unicastingDispatcher = new UnicastingDispatcher();
            unicastingDispatcher.setLoadBalancingStrategy(new RoundRobinLoadBalancingStrategy());
            this.dispatcher = unicastingDispatcher;
        }
        if (this.maxSubscribers == null) {
            this.maxSubscribers = this
                    .getIntegrationProperty(isPubSub ? IntegrationProperties.CHANNELS_MAX_BROADCAST_SUBSCRIBERS
                            : IntegrationProperties.CHANNELS_MAX_UNICAST_SUBSCRIBERS, Integer.class);
        }
        this.dispatcher.setMaxSubscribers(this.maxSubscribers);
    }

    /*
     * SmartLifecycle implementation (delegates to the MessageListener container)
     */

    @Override
    public boolean isAutoStartup() {
        return (this.container != null) && this.container.isAutoStartup();
    }

    @Override
    public int getPhase() {
        return (this.container != null) ? this.container.getPhase() : 0;
    }

    @Override
    public boolean isRunning() {
        return (this.container != null) && this.container.isRunning();
    }

    @Override
    public void start() {
        if (this.container != null) {
            this.container.start();
        }
    }

    @Override
    public void stop() {
        if (this.container != null) {
            this.container.stop();
        }
    }

    @Override
    public void stop(Runnable callback) {
        if (this.container != null) {
            this.container.stop(callback);
        } else {
            callback.run();
        }
    }

    @Override
    public void destroy() {
        if (this.container != null) {
            this.container.destroy();
        }
    }

    private static final class DispatchingMessageListener implements MessageListener {

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

        private final JmsTemplate jmsTemplate;

        private final MessageDispatcher dispatcher;

        private final SubscribableJmsChannel channel;

        private final boolean isPubSub;

        private final MessageBuilderFactory messageBuilderFactory;

        DispatchingMessageListener(JmsTemplate jmsTemplate, MessageDispatcher dispatcher,
                SubscribableJmsChannel channel, boolean isPubSub, MessageBuilderFactory messageBuilderFactory) {
            this.jmsTemplate = jmsTemplate;
            this.dispatcher = dispatcher;
            this.channel = channel;
            this.isPubSub = isPubSub;
            this.messageBuilderFactory = messageBuilderFactory;
        }

        @Override
        public void onMessage(javax.jms.Message message) {
            Message<?> messageToSend = null;
            try {
                MessageConverter converter = this.jmsTemplate.getMessageConverter();
                Object converted = null;
                if (converter != null) {
                    converted = converter.fromMessage(message);
                }
                if (converted != null) {
                    messageToSend = (converted instanceof Message<?>) ? (Message<?>) converted
                            : this.messageBuilderFactory.withPayload(converted).build();
                    this.dispatcher.dispatch(messageToSend);
                } else if (this.logger.isWarnEnabled()) {
                    this.logger.warn("No converter found, or converter returned null for: " + message
                            + ", no Message to dispatch");
                }
            } catch (MessageDispatchingException e) {
                String exceptionMessage = e.getMessage() + " for jms-channel '" + this.channel.getFullChannelName()
                        + "'.";
                if (this.isPubSub) {
                    // log only for backwards compatibility with pub/sub
                    if (this.logger.isWarnEnabled()) {
                        this.logger.warn(exceptionMessage, e);
                    }
                } else {
                    throw new MessageDeliveryException(messageToSend, exceptionMessage, e);
                }
            } catch (Exception e) {
                throw new MessagingException("failed to handle incoming JMS Message", e);
            }
        }

    }

}