org.paxle.core.threading.impl.Master.java Source code

Java tutorial

Introduction

Here is the source code for org.paxle.core.threading.impl.Master.java

Source

/**
 * This file is part of the Paxle project.
 * Visit http://www.paxle.net for more information.
 * Copyright 2007-2010 the original author or authors.
 *
 * Licensed under the terms of the Common Public License 1.0 ("CPL 1.0").
 * Any use, reproduction or distribution of this program constitutes the recipient's acceptance of this agreement.
 * The full license text is available under http://www.opensource.org/licenses/cpl1.0.txt
 * or in the file LICENSE.txt in the root directory of the Paxle distribution.
 *
 * Unless required by applicable law or agreed to in writing, this software is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */

package org.paxle.core.threading.impl;

import java.util.concurrent.Semaphore;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.paxle.core.doc.ICommand;
import org.paxle.core.queue.IInputQueue;
import org.paxle.core.queue.IOutputQueue;
import org.paxle.core.threading.IMaster;
import org.paxle.core.threading.IPool;
import org.paxle.core.threading.IWorker;
import org.paxle.core.threading.PPM;

public class Master<Data> extends Thread implements IMaster<Data> {
    /**
     * For logging
     */
    private Log logger = LogFactory.getLog(this.getClass());

    /**
     * A pool of {@link IWorker worker-threads}
     */
    protected IPool<Data> pool = null;

    /**
     * An input-queue containing jobs to process 
     */
    protected IInputQueue<Data> inQueue = null;

    /**
     * indicates if this thread was terminated
     * @see #terminate()
     */
    protected boolean stopped = false;

    /**
     * indicates if this thread was paused
     * @see #pauseMaster()
     * @see #resumeMaster()
     * @see #isPaused()
     */
    protected boolean paused = false;

    /**
     * total number of processed jobs since startup. 
     */
    protected int processedCount = 0;

    /**
     * timestamp when the master-thread was started
     */
    protected long startTime = System.currentTimeMillis();

    /**
     * Record statistics for PPM calculation 
     * @see #getPPM()
     */
    private PPM ppm = new PPM();

    /**
     * set this to <code>>0</code> to delay
     * the master thread between busy loops.
     */
    private int delay = -1;

    /**
     * Specifies whether the workers will be triggerd to fetch
     * the next command on their own or get the already dequeued
     * command by the master.
     */
    private boolean triggerMode = true;

    /**
     * @param threadPool the thread pool containing {@link IWorker worker-threads}
     * @param commandQueue the queue containing {@link ICommand commands} to process
     */
    public Master(IPool<Data> threadPool, IInputQueue<Data> commandQueue) {
        this(threadPool, commandQueue, true);
    }

    public Master(IPool<Data> threadPool, IInputQueue<Data> commandQueue, boolean useTrigger) {
        this.triggerMode = useTrigger;
        this.pool = threadPool;
        this.inQueue = commandQueue;
        this.setName("Master");
        this.start();
    }

    @Override
    public void run() {
        while (!this.stopped && !Thread.interrupted()) {
            Data command = null;
            try {
                // check if the master was paused
                synchronized (this) {
                    if (this.paused)
                        this.wait();
                }

                if (!this.triggerMode) {
                    // getting a new command from the queue
                    command = this.inQueue.dequeue();
                } else {
                    // just wait for the next command
                    this.inQueue.waitForNext();
                }

                // getting a free worker from pool
                IWorker<Data> worker = this.pool.getWorker();

                if (!this.triggerMode) {
                    // assign the command to the worker
                    worker.assign(command);
                    command = null;
                } else {
                    // force the worker to fetch the next command
                    worker.trigger();
                }
                this.processedCount++;

                //add the job to the total job-count and the PPM
                this.ppm.trick();

                // delay
                if (this.delay > 0) {
                    Thread.sleep(this.delay);
                }
            } catch (InterruptedException e) {
                this.logger.debug("Master thread interrupted from outside.");
                Thread.interrupted();
                this.stopped = true;
            } catch (Throwable e) {
                this.logger.error(String.format("Unexpected '%s' while processing command '%s'.",
                        e.getClass().getName(), command), e);
            }
        }

        // consuming the "is interrupted"-flag
        this.isInterrupted();

        // closing the pool
        try {
            this.logger.debug("Terminating worker-threads ...");
            this.pool.close();
            this.logger.debug("Worker-threads terminated.");
        } catch (Throwable e) {
            this.logger.error(
                    String.format("Unexpected '%s' while terminating worker-threads.", e.getClass().getName()), e);
        }
    }

    public void process(final Data cmd) throws Exception {
        // creating and assigning a dummy output queues
        final Semaphore s = new Semaphore(0);
        final IInputQueue<Data> inputQueue = new IInputQueue<Data>() {
            private int counter = 0;

            public Data dequeue() throws InterruptedException {
                if (counter > 0)
                    throw new IllegalStateException("Method executed multiple times");
                this.counter++;
                return cmd;
            }

            public void waitForNext() throws InterruptedException {
                throw new IllegalStateException("You are not allowed to call this method");
            }
        };
        final IOutputQueue<Data> outputQueue = new IOutputQueue<Data>() {
            public void enqueue(Data command) throws InterruptedException {
                s.release();
            }
        };

        // process the command
        this.process(inputQueue, outputQueue, false);

        // waiting for the worker to finish execution
        s.acquire();
    }

    public void process(final IInputQueue<Data> inputQueue, IOutputQueue<Data> outputQueue, boolean workerFromPool)
            throws Exception {
        // creating a new worker
        IWorker<Data> worker = this.pool.getWorker(workerFromPool);
        worker.setInQueue(inputQueue);
        worker.setOutQueue(outputQueue);

        // signal the worker that a new command is available
        worker.trigger();
    }

    /**
     * @see IMaster#terminate()
     */
    public void terminate() {
        this.stopped = true;
        this.interrupt();
        try {
            this.join(15000);
        } catch (InterruptedException e) {
            /* ignore this */
        }
    }

    /**
     * @see IMaster#pauseMaster()
     */
    public synchronized void pauseMaster() {
        this.paused = true;
    }

    /**
     * @see IMaster#resumeMaster()
     */
    public synchronized void resumeMaster() {
        this.paused = false;
        this.notifyAll();
    }

    /**
     * @see IMaster#processNext()
     */
    public synchronized void processNext() {
        if (this.paused)
            this.notifyAll();
    }

    /**
     * @see IMaster#isPaused()
     */
    public boolean isPaused() {
        return this.paused;
    }

    /**
     * @see IMaster#getPPM()
     */
    public int getPPM() {
        return this.ppm.getPPM();
    }

    /**
     * @see IMaster#setDelay(int)
     */
    public void setDelay(int delay) {
        this.delay = delay;
    }

    public int getDelay() {
        return this.delay;
    }

    /**
     * @see IMaster#processedCount()
     */
    public int processedCount() {
        return this.processedCount;
    }
}