org.wso2.andes.kernel.MessageFlusher.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.andes.kernel.MessageFlusher.java

Source

/*
*  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
*  WSO2 Inc. licenses this file to you 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.wso2.andes.kernel;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.andes.amqp.AMQPUtils;
import org.wso2.andes.configuration.AndesConfigurationManager;
import org.wso2.andes.configuration.enums.AndesConfiguration;
import org.wso2.andes.configuration.util.TopicMessageDeliveryStrategy;
import org.wso2.andes.kernel.disruptor.delivery.DisruptorBasedFlusher;
import org.wso2.andes.kernel.subscription.AndesSubscription;
import org.wso2.andes.kernel.subscription.StorageQueue;
import org.wso2.andes.mqtt.utils.MQTTUtils;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * <code>MessageFlusher</code> Handles the task of polling the user queues and flushing the
 * messages to subscribers There will be one Flusher per Queue Per Node
 */
public class MessageFlusher {

    private static Log log = LogFactory.getLog(MessageFlusher.class);

    private final DisruptorBasedFlusher flusherExecutor;

    /**
     * Message flusher for queue message delivery. Depending on the behaviour of the strategy
     * conditions to push messages to subscribers vary.
     */
    private MessageDeliveryStrategy queueMessageFlusher;

    /**
     * Message flusher for topic message delivery. Depending on the behaviour of the strategy
     * conditions to push messages to subscribers vary.
     */
    private MessageDeliveryStrategy topicMessageFlusher;

    /**
     * Head of the delivery message responsibility chain
     */
    private DeliveryResponsibility deliveryResponsibilityHead;

    private static MessageFlusher messageFlusher = new MessageFlusher();

    /**
     * Get Singleton object
     * @return MessageFlusher of the broker
     */
    public static MessageFlusher getInstance() {
        return messageFlusher;
    }

    private MessageFlusher() {

        flusherExecutor = new DisruptorBasedFlusher();

        //set queue message flusher
        this.queueMessageFlusher = new FlowControlledQueueMessageDeliveryImpl();

        //set topic message flusher
        TopicMessageDeliveryStrategy topicMessageDeliveryStrategy = AndesConfigurationManager
                .readValue(AndesConfiguration.PERFORMANCE_TUNING_TOPIC_MESSAGE_DELIVERY_STRATEGY);

        if (topicMessageDeliveryStrategy.equals(TopicMessageDeliveryStrategy.DISCARD_ALLOWED)
                || topicMessageDeliveryStrategy.equals(TopicMessageDeliveryStrategy.DISCARD_NONE)) {

            this.topicMessageFlusher = new NoLossBurstTopicMessageDeliveryImpl();

        } else if (topicMessageDeliveryStrategy.equals(TopicMessageDeliveryStrategy.SLOWEST_SUB_RATE)) {

            this.topicMessageFlusher = new SlowestSubscriberTopicMessageDeliveryImpl();
        }

        initializeDeliveryResponsibilityComponents();

    }

    /**
     * Initialize the delivery filter chain
     */
    private void initializeDeliveryResponsibilityComponents() {
        //assign the head of the handler chain
        deliveryResponsibilityHead = new PurgedMessageHandler();
        ExpiredMessageHandler expiredMessageHandler = new ExpiredMessageHandler();
        //link the second handler to the head
        deliveryResponsibilityHead.setNextDeliveryFilter(expiredMessageHandler);
        //link the third handler
        expiredMessageHandler.setNextDeliveryFilter(new DeliveryMessageHandler());

        int preDeliveryDeletionTaskScheduledPeriod = AndesConfigurationManager
                .readValue(AndesConfiguration.PERFORMANCE_TUNING_PRE_DELIVERY_EXPIRY_DELETION_INTERVAL);
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("ExpiryMessageDeletionTask-%d")
                .build();
        //executor service for pre delivery deletion task
        ScheduledExecutorService expiryMessageDeletionTaskScheduler = Executors
                .newSingleThreadScheduledExecutor(namedThreadFactory);
        //pre-delivery deletion task initialization
        PreDeliveryExpiryMessageDeletionTask preDeliveryExpiryMessageDeletionTask = new PreDeliveryExpiryMessageDeletionTask();
        //Set the expiry message deletion task to the expired message handler
        expiredMessageHandler.setExpiryMessageDeletionTask(preDeliveryExpiryMessageDeletionTask);
        //schedule the task at the specified intervals
        expiryMessageDeletionTaskScheduler.scheduleAtFixedRate(preDeliveryExpiryMessageDeletionTask,
                preDeliveryDeletionTaskScheduledPeriod, preDeliveryDeletionTaskScheduledPeriod, TimeUnit.SECONDS);

    }

    /**
     * Check whether there are active subscribers and send
     *
     * @param storageQueue storage queue of messages
     * @return how many messages sent
     * @throws AndesException
     */
    public int sendMessagesToSubscriptions(StorageQueue storageQueue) throws AndesException {
        int noOfSentMessages;
        if ((!storageQueue.isDurable())
                && (storageQueue.getMessageRouter().getName().equals(AMQPUtils.TOPIC_EXCHANGE_NAME)
                        || storageQueue.getMessageRouter().getName().equals(MQTTUtils.MQTT_EXCHANGE_NAME))) {

            noOfSentMessages = topicMessageFlusher.deliverMessageToSubscriptions(storageQueue);

        } else {
            noOfSentMessages = queueMessageFlusher.deliverMessageToSubscriptions(storageQueue);
        }

        return noOfSentMessages;
    }

    /**
     * Schedule to deliver message for the subscription
     *
     * @param subscription subscription to send
     * @param message message to send
     */
    public void scheduleMessageForSubscription(AndesSubscription subscription,
            final DeliverableAndesMetadata message) throws AndesException {
        deliverMessageAsynchronously(subscription, message);
    }

    /**
     * Submit the messages to a thread pool to deliver asynchronously
     *
     * @param subscription local subscription
     * @param message      metadata of the message
     */
    public void deliverMessageAsynchronously(AndesSubscription subscription, DeliverableAndesMetadata message)
            throws AndesException {

        deliveryResponsibilityHead.handleDeliveryMessage(subscription, message);
    }

    /**
     * Stop disruptor based message delivery. This will process all taken in events
     * and then shutdown. Make sure to stop all incoming events to outbound disruptor
     * before calling this method.
     */
    public void stopMessageFlusher() {
        flusherExecutor.stop();
    }

    public DisruptorBasedFlusher getFlusherExecutor() {
        return flusherExecutor;
    }

}