org.wso2.andes.kernel.slot.SlotDeliveryWorker.java Source code

Java tutorial

Introduction

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

Source

/*
 * Copyright (c) 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.slot;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.andes.configuration.AndesConfigurationManager;
import org.wso2.andes.configuration.enums.AndesConfiguration;
import org.wso2.andes.kernel.*;
import org.wso2.andes.kernel.MessageFlusher;
import org.wso2.andes.kernel.OnflightMessageTracker;
import org.wso2.andes.server.cluster.coordination.hazelcast.HazelcastAgent;
import org.wso2.andes.thrift.MBThriftClient;
import org.wso2.andes.subscription.SubscriptionStore;

import java.util.*;
import java.util.concurrent.ConcurrentSkipListMap;

/**
 * SlotDelivery worker is responsible of distributing messages to subscribers. Messages will be
 * taken from a slot.
 */
public class SlotDeliveryWorker extends Thread {

    /**
     * keeps storage queue name vs actual destination it represent
     */
    private ConcurrentSkipListMap<String, String> storageQueueNameToDestinationMap;

    private SubscriptionStore subscriptionStore;
    private HashMap<String, Long> localLastProcessedIdMap;
    private static boolean isClusteringEnabled;
    private static Log log = LogFactory.getLog(SlotDeliveryWorker.class);

    /**
     * This map contains slotId to slot hashmap against queue name
     */
    private volatile boolean running;
    private String nodeId;
    private MessageFlusher messageFlusher;
    private SlotDeletionScheduler slotDeletionScheduler;

    private static final long SLOT_DELETION_SCHEDULE_INTERVAL = 15 * 1000;

    public SlotDeliveryWorker() {
        messageFlusher = MessageFlusher.getInstance();
        this.storageQueueNameToDestinationMap = new ConcurrentSkipListMap<String, String>();
        this.subscriptionStore = AndesContext.getInstance().getSubscriptionStore();
        isClusteringEnabled = AndesContext.getInstance().isClusteringEnabled();
        localLastProcessedIdMap = new HashMap<String, Long>();
        slotDeletionScheduler = new SlotDeletionScheduler(SLOT_DELETION_SCHEDULE_INTERVAL);
        /*
        Start slot deleting thread only if clustering is enabled. Otherwise slots assignment will
         not happen
         */
        if (isClusteringEnabled) {
            nodeId = HazelcastAgent.getInstance().getNodeId();
        }
    }

    @Override
    public void run() {
        /**
         * This while loop is necessary since whenever there are messages this thread should
         * deliver them
         */
        running = true;
        while (running) {

            //Iterate through all the queues registered in this thread
            int idleQueueCounter = 0;

            for (String storageQueueName : storageQueueNameToDestinationMap.keySet()) {
                String destinationOfMessagesInQueue = storageQueueNameToDestinationMap.get(storageQueueName);
                Collection<LocalSubscription> subscriptions4Queue;
                try {
                    subscriptions4Queue = subscriptionStore
                            .getActiveLocalSubscribersForQueuesAndTopics(destinationOfMessagesInQueue);
                    if (subscriptions4Queue != null && !subscriptions4Queue.isEmpty()) {
                        //Check in memory buffer in MessageFlusher has room
                        if (messageFlusher.getMessageDeliveryInfo(destinationOfMessagesInQueue)
                                .isMessageBufferFull()) {
                            if (isClusteringEnabled) {
                                long startTime = System.currentTimeMillis();
                                Slot currentSlot = MBThriftClient.getSlot(storageQueueName, nodeId);
                                currentSlot.setDestinationOfMessagesInSlot(destinationOfMessagesInQueue);
                                long endTime = System.currentTimeMillis();

                                if (log.isDebugEnabled()) {
                                    log.debug((endTime - startTime) + " milliSec took to get a slot"
                                            + " from slot manager");
                                }
                                /**
                                 * If the slot is empty
                                 */
                                if (0 == currentSlot.getEndMessageId()) {

                                    /*
                                    If the message buffer in MessageFlusher is not empty
                                     send those messages
                                     */
                                    if (log.isDebugEnabled()) {
                                        log.debug("Recieved an empty slot from slot manager in " + "cluster mode");
                                    }
                                    boolean sentFromMessageBuffer = sendFromMessageBuffer(
                                            destinationOfMessagesInQueue);
                                    if (!sentFromMessageBuffer) {
                                        //No available free slots
                                        idleQueueCounter++;
                                        if (idleQueueCounter == storageQueueNameToDestinationMap.size()) {
                                            try {
                                                if (log.isDebugEnabled()) {
                                                    log.debug("Sleeping Slot Delivery Worker");
                                                }
                                                Thread.sleep(100);
                                            } catch (InterruptedException ignored) {
                                                //Silently ignore
                                            }
                                        }
                                    }
                                } else {
                                    if (log.isDebugEnabled()) {
                                        log.debug("Received slot for queue " + storageQueueName + " " + "is: "
                                                + currentSlot.getStartMessageId() + " - "
                                                + currentSlot.getEndMessageId() + "Thread Id:"
                                                + Thread.currentThread().getId());
                                    }
                                    long firstMsgId = currentSlot.getStartMessageId();
                                    long lastMsgId = currentSlot.getEndMessageId();
                                    //Read messages in the slot
                                    List<AndesMessageMetadata> messagesRead = MessagingEngine.getInstance()
                                            .getMetaDataList(storageQueueName, firstMsgId, lastMsgId);

                                    if (log.isDebugEnabled()) {
                                        StringBuilder messageIDString = new StringBuilder();
                                        for (AndesMessageMetadata metadata : messagesRead) {
                                            messageIDString.append(metadata.getMessageID()).append(" , ");
                                        }
                                        log.debug("Messages Read: " + messageIDString);
                                    }
                                    if (messagesRead != null && !messagesRead.isEmpty()) {
                                        if (log.isDebugEnabled()) {
                                            log.debug("Number of messages read from slot "
                                                    + currentSlot.getStartMessageId() + " - "
                                                    + currentSlot.getEndMessageId() + " is " + messagesRead.size()
                                                    + " queue= " + storageQueueName);
                                        }
                                        MessageFlusher.getInstance().sendMessageToBuffer(messagesRead, currentSlot);
                                        MessageFlusher.getInstance()
                                                .sendMessagesInBuffer(currentSlot.getDestinationOfMessagesInSlot());
                                    } else {
                                        currentSlot.setSlotInActive();
                                        deleteSlot(currentSlot);
                                    }
                                }
                                //Standalone mode
                            } else {
                                long startMessageId = 0;
                                if (localLastProcessedIdMap.get(storageQueueName) != null) {
                                    startMessageId = localLastProcessedIdMap.get(storageQueueName) + 1;
                                }

                                Integer slotWindowSize = AndesConfigurationManager
                                        .readValue(AndesConfiguration.PERFORMANCE_TUNING_SLOTS_SLOT_WINDOW_SIZE);
                                List<AndesMessageMetadata> messagesRead =

                                        MessagingEngine.getInstance().getNextNMessageMetadataFromQueue(
                                                storageQueueName, startMessageId, slotWindowSize);
                                if (log.isDebugEnabled()) {
                                    StringBuilder messageIDString = new StringBuilder();
                                    for (AndesMessageMetadata metadata : messagesRead) {
                                        messageIDString.append(metadata.getMessageID()).append(" , ");
                                    }
                                    log.debug("Messages Read: " + messageIDString);
                                }

                                if (messagesRead == null || messagesRead.isEmpty()) {
                                    log.debug("No messages are read. StorageQ= " + storageQueueName);
                                    boolean sentFromMessageBuffer = sendFromMessageBuffer(
                                            destinationOfMessagesInQueue);
                                    log.debug("Sent messages from buffer = " + sentFromMessageBuffer);
                                    if (!sentFromMessageBuffer) {
                                        idleQueueCounter++;
                                        try {
                                            //There are no messages to read
                                            if (idleQueueCounter == storageQueueNameToDestinationMap.size()) {
                                                if (log.isDebugEnabled()) {
                                                    log.debug("Sleeping Slot Delivery Worker");
                                                }
                                                Thread.sleep(2000);
                                            }
                                        } catch (InterruptedException ignored) {
                                            //Silently ignore
                                        }

                                    }
                                } else {
                                    if (log.isDebugEnabled()) {
                                        log.debug(messagesRead.size() + " "
                                                + "number of messages read from Slot Delivery Worker. StorageQ= "
                                                + storageQueueName);
                                    }
                                    long lastMessageId = messagesRead.get(messagesRead.size() - 1).getMessageID();
                                    log.debug("Last message id read = " + lastMessageId);
                                    localLastProcessedIdMap.put(storageQueueName, lastMessageId);

                                    //Simulate a slot here
                                    Slot currentSlot = new Slot();
                                    currentSlot.setStorageQueueName(storageQueueName);
                                    currentSlot.setDestinationOfMessagesInSlot(destinationOfMessagesInQueue);
                                    currentSlot.setStartMessageId(startMessageId);
                                    currentSlot.setEndMessageId(lastMessageId);

                                    log.debug("sending read messages to flusher << " + currentSlot.toString()
                                            + " >>");
                                    messageFlusher.sendMessageToBuffer(messagesRead, currentSlot);
                                    messageFlusher
                                            .sendMessagesInBuffer(currentSlot.getDestinationOfMessagesInSlot());
                                }
                            }
                        } else {
                            /*If there are messages to be sent in the message
                                        buffer in MessageFlusher send them */
                            if (log.isDebugEnabled()) {
                                log.debug("The queue " + storageQueueName + " has no room. Thus sending "
                                        + "from buffer.");
                            }
                            sendFromMessageBuffer(destinationOfMessagesInQueue);
                        }
                    } else {
                        idleQueueCounter++;
                        if (idleQueueCounter == storageQueueNameToDestinationMap.size()) {
                            try {
                                if (log.isDebugEnabled()) {
                                    log.debug("Sleeping Slot Delivery Worker");
                                }
                                Thread.sleep(100);
                            } catch (InterruptedException ignored) {
                                //Silently ignore
                            }
                        }
                    }
                } catch (AndesException e) {
                    log.error("Error running Message Store Reader " + e.getMessage(), e);
                } catch (ConnectionException e) {
                    log.error("Error occurred while connecting to the thrift coordinator " + e.getMessage(), e);
                    setRunning(false);
                    //Any exception should be caught here. Otherwise SDW thread will stop
                    //and MB node will become useless
                } catch (Exception e) {
                    log.error("Error while running Slot Delivery Worker. ", e);
                }
            }
        }

    }

    /**
     * Send messages from buffer in MessageFlusher if the buffer is not empty
     *
     * @param msgDestination queue/topic message is addressed to
     * @return whether the messages are sent from message buffer or not
     * @throws AndesException
     */
    private boolean sendFromMessageBuffer(String msgDestination) throws AndesException {
        boolean sentFromMessageBuffer = false;
        if (!messageFlusher.isMessageBufferEmpty(msgDestination)) {
            messageFlusher.sendMessagesInBuffer(msgDestination);
            sentFromMessageBuffer = true;
        }
        return sentFromMessageBuffer;
    }

    /**
     * Add a queue to queue list of this SlotDeliveryWorkerThread
     *
     * @param storageQueueName queue name of the newly added queue
     */
    public void addQueueToThread(String storageQueueName, String destination) {
        getStorageQueueNameToDestinationMap().put(storageQueueName, destination);
    }

    /**
     * Get queue list belongs to this thread
     *
     * @return queue list
     */
    public ConcurrentSkipListMap<String, String> getStorageQueueNameToDestinationMap() {
        return storageQueueNameToDestinationMap;
    }

    /**
     * @return Whether the worker thread is in running state or not
     */
    public boolean isRunning() {
        return running;
    }

    /**
     * Set state of the worker thread
     *
     * @param running new state of the worker
     */
    public void setRunning(boolean running) {
        this.running = running;
    }

    public void deleteSlot(Slot slot) {

        if (isClusteringEnabled) {
            String nodeID = HazelcastAgent.getInstance().getNodeId();
            slotDeletionScheduler.scheduleSlotDeletion(slot, nodeID);
        } else {
            OnflightMessageTracker.getInstance().releaseAllMessagesOfSlotFromTracking(slot);
        }

    }
}