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

Java tutorial

Introduction

Here is the source code for org.wso2.andes.kernel.MessageCountFlusher.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;

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

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Updates message count in batches. Can be used as a scheduled task as well to update message counts
 */
public class MessageCountFlusher implements Runnable {

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

    /**
     * Map to keep message count difference not flushed to disk of each queue
     */
    private Map<String, AtomicInteger> messageCountDifferenceMap;

    /**
     * message count will be flushed to DB when count difference reach this val
     */
    private int messageCountFlushNumberGap;

    /**
     * Reference for AndesContextStore
     */
    private AndesContextStore contextStore;

    /**
     * Creates a message count flusher object to store message counts in store
     * @param contextStore AndesContextStore
     * @param messageCountFlushNumberGap batch size of the count update for a given queue.
     */
    public MessageCountFlusher(AndesContextStore contextStore, int messageCountFlushNumberGap) {
        this.contextStore = contextStore;
        this.messageCountFlushNumberGap = messageCountFlushNumberGap;
        messageCountDifferenceMap = new ConcurrentHashMap<String, AtomicInteger>();
    }

    /**
     * Running this task as a scheduled task will flush all pending message count update request to DB at given
     * time intervals
     */
    @Override
    public void run() {
        for (Map.Entry<String, AtomicInteger> entry : messageCountDifferenceMap.entrySet()) {
            flushToStore(entry.getKey(), entry.getValue());
        }
    }

    /**
     * increment message count of queue. Flush if difference is in tab
     *
     * @param queueName   name of the queue to increment count
     * @param incrementBy increment count by this value
     */
    public void incrementQueueCount(String queueName, int incrementBy) {

        if (log.isDebugEnabled()) {
            log.debug("Increment count by " + incrementBy + " for queue " + queueName);
        }
        updateQueueCount(queueName, incrementBy);
    }

    /**
     * decrement queue count. Flush if difference is in tab
     *
     * @param queueName   name of the queue to decrement count
     * @param decrementBy decrement count by this value, This should be a positive value
     */
    public void decrementQueueCount(String queueName, int decrementBy) {
        if (log.isDebugEnabled()) {
            log.debug("Decrement count by " + decrementBy + " for queue " + queueName);
        }
        updateQueueCount(queueName, -decrementBy);
    }

    /**
     * Increment or decrement the queue count by given delta. Count update is reflected in store when either the count
     * update batch size (messageCountFlushNumberGap) is reached or scheduled message count update task is triggered
     *
     * @param queueName name of the queue to update the queue count
     * @param delta     value to be decrement or increment. Positive value to increment and vice versa
     */
    private void updateQueueCount(String queueName, int delta) {
        AtomicInteger difference = messageCountDifferenceMap.get(queueName);
        if (null == difference) {
            difference = new AtomicInteger(0);
            messageCountDifferenceMap.put(queueName, difference);
        }

        // Decrement value and get the current value
        int newDifference = difference.addAndGet(delta);
        // We use the new value at the time we decrement and make decisions on that value.
        // we flush this value to store in 100 message tabs.
        if (newDifference % messageCountFlushNumberGap == 0) {
            // "newDifference" is already updated in "difference"  as well
            flushToStore(queueName, difference);
        }
    }

    /**
     * Update the store with new message counts
     *
     * @param queueName  count is updated in this queue
     * @param difference count is updated by this value in store
     */
    private void flushToStore(String queueName, AtomicInteger difference) {
        int count = 0;
        try {
            // Get the current count and make decisions from that value.
            // If not, within the execution of method some other thread might update the value and subsequent reads
            // of the value will be different leading to unpredictable behaviour.
            // We get the current count and reset the value at the same time so that this call is going to
            // take care of the current count only. Any update happen during the method execution will be reflected
            // in subsequent calls to this method.
            count = difference.getAndSet(0);

            if (count > 0) {
                if (log.isDebugEnabled()) {
                    log.debug("Increment store count by " + count + " for queue " + queueName);
                }
                contextStore.incrementMessageCountForQueue(queueName, count);
            } else if (count < 0) {
                if (log.isDebugEnabled()) {
                    log.debug("Decrement store count by " + count + " for queue " + queueName);
                }
                contextStore.incrementMessageCountForQueue(queueName, count);
            }
        } catch (AndesException e) {
            // On error add back the count. Since the operation didn't run correctly. Next call to this method might
            // get the chance to update the value properly.
            difference.addAndGet(count);
            log.error("Error while updating message counts for queue " + queueName, e);
        }
    }
}