com.miraclelinux.historygluon.HistoryStreamer.java Source code

Java tutorial

Introduction

Here is the source code for com.miraclelinux.historygluon.HistoryStreamer.java

Source

/* History Gluon
   Copyright (C) 2012 MIRACLE LINUX CORPORATION
     
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
    
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
    
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.miraclelinux.historygluon;

import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;

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

public class HistoryStreamer extends Thread {

    /* -----------------------------------------------------------------------
     * Private constants
     * -------------------------------------------------------------------- */
    private static final int THREAD_QUEUE_CAPACITY = 10;
    private static final int NUM_MAX_DATA_AT_ONCE = 100;

    /* -----------------------------------------------------------------------
     * Private static members
     * -------------------------------------------------------------------- */
    private static ArrayBlockingQueue<HistoryStreamer> m_threadQueue = null;

    /* -----------------------------------------------------------------------
     * Private members
     * -------------------------------------------------------------------- */
    private Log m_log = null;
    private BasicStorageDriver m_basicDriver = null;
    private Semaphore m_semaphore = null;
    private Semaphore m_working_sem = null;
    private AtomicBoolean m_stopRequested = new AtomicBoolean(false);
    private BlockingQueue<HistoryStreamElement> m_queue = null;
    private String m_key0 = null;
    private String m_key1 = null;

    static {
        // We make a list of HistoryStreamer first, because we want to avoid to
        // add try block for BLockingQueue#put() here.
        List<HistoryStreamer> streamers = new ArrayList<HistoryStreamer>(THREAD_QUEUE_CAPACITY);
        for (int i = 0; i < THREAD_QUEUE_CAPACITY; i++) {
            HistoryStreamer thread = new HistoryStreamer();
            thread.start();
            streamers.add(thread);
        }
        m_threadQueue = new ArrayBlockingQueue<HistoryStreamer>(THREAD_QUEUE_CAPACITY, true, streamers);
    }

    /* -----------------------------------------------------------------------
     * Public Methods
     * -------------------------------------------------------------------- */
    public static HistoryStreamer getInstance(BasicStorageDriver basicDriver) {
        HistoryStreamer thread = null;
        try {
            thread = m_threadQueue.take();
            thread.m_basicDriver = basicDriver;
        } catch (InterruptedException e) {
            /* nothing to do */
        }
        return thread;
    }

    public void close() {
        if (!m_working_sem.tryAcquire()) {
            // This condition happens when the thread is now runinng
            // in generateStream(). We have to wait for stopping it.
            // [TODO] However, It's better to avoid bloking here.
            // This function should return soon.
            m_stopRequested.set(true);
            try {
                m_working_sem.acquire();
            } catch (InterruptedException e) {
                // TODO: What should we do here ?
            }
            m_stopRequested.set(false);
        }
        m_working_sem.release();

        // clear the stream queue
        m_queue.clear();

        // return this instance to the thread pool.
        m_threadQueue.add(this);
    }

    public BlockingQueue<HistoryStreamElement> open(String key0, String key1) {
        if (!m_working_sem.tryAcquire()) {
            String msg;
            msg = "HistoryStreamer#open(): Stream has already been opened.";
            throw new InternalCheckException(msg);
        }
        m_working_sem.release();

        if (!m_queue.isEmpty()) {
            String msg;
            msg = "HistoryStreamer#open(): queue is not empty.";
            throw new InternalCheckException(msg);
        }

        m_key0 = key0;
        m_key1 = key1;
        m_semaphore.release();
        return m_queue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                m_semaphore.acquire();
                m_working_sem.acquire();
                generateStream();
            } catch (Exception e) {
                e.printStackTrace();
                m_log.error(e);
                putEndOfStream(ErrorCode.UNKNOWN_ERROR);
            } finally {
                m_working_sem.release();
            }
        }
    }

    /* -----------------------------------------------------------------------
     * Private Methods
     * -------------------------------------------------------------------- */
    private HistoryStreamer() {
        m_log = LogFactory.getLog(HistoryStreamer.class);
        m_queue = new LinkedBlockingQueue<HistoryStreamElement>();
        m_semaphore = new Semaphore(1);
        m_semaphore.acquireUninterruptibly(); // This is never blocked.
        m_working_sem = new Semaphore(1);
    }

    private void generateStream() throws InterruptedException {
        while (!m_stopRequested.get()) {
            HistoryDataSet dataSet;
            try {
                dataSet = m_basicDriver.getDataSet(m_key0, m_key1, NUM_MAX_DATA_AT_ONCE);
            } catch (HistoryDataSet.TooManyException e) {
                // This won't be occured, because NUM_MAX_DATA_AT_ONCE is
                // not so large.
                break;
            }

            int dataSize = dataSet.size();
            if (dataSize == 0)
                break;

            // push obtained data to the queue
            Iterator<HistoryData> it = dataSet.iterator();
            HistoryData history = null;
            while (it.hasNext()) {
                history = it.next();
                m_queue.put(new HistoryStreamElement(history));
            }

            // break if this is the last chunk.
            if (dataSize < NUM_MAX_DATA_AT_ONCE)
                break;

            if (history.sec == 0xffffffff && history.ns == 0xffffffff)
                m_key0 = m_basicDriver.makeKey(history.id + 1, 0, 0);
            else
                m_key0 = m_basicDriver.makeKey(history.id, history.sec, history.ns + 1);
        }
        putEndOfStream(ErrorCode.SUCCESS);
    }

    private void putEndOfStream(int errorCode) {
        HistoryStreamElement eos = HistoryStreamElement.createEndOfStream(errorCode);
        try {
            m_queue.put(eos);
        } catch (InterruptedException e) {
            // TODO: implement exception
            e.printStackTrace();
            m_log.error(e);
        }
    }
}