nl.strohalm.cyclos.utils.WorkerThreads.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.utils.WorkerThreads.java

Source

/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
    
Cyclos 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 2 of the License, or
(at your option) any later version.
    
Cyclos 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 Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 */
package nl.strohalm.cyclos.utils;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;

import nl.strohalm.cyclos.utils.access.LoggedUser;

import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * An abstract implementation for a thread group of consumer threads
 * @author luis
 */
public abstract class WorkerThreads<T> {

    private final class WorkerThread extends Thread {
        private long lastUsedAt;
        private boolean inProcess;

        @Override
        public void run() {
            while (true) {
                // Block until something is available or this thread has been stopped
                final T object;
                try {
                    object = queue.take();
                } catch (final InterruptedException e) {
                    // This thread has been interrupted. Leave the loop
                    break;
                }

                inProcess = true;

                // Update the last used counter
                lastUsedAt = System.currentTimeMillis();

                // Process the object
                LoggedUser.runAsSystem(new Callable<Void>() {
                    @Override
                    public Void call() throws Exception {
                        try {
                            process(object);
                        } catch (final Exception e) {
                            LOG.error("Error processing work by " + name, e);
                        }
                        return null;
                    }
                });

                inProcess = false;
            }
        }
    }

    private static final long CHECK_INTERVAL = DateUtils.MILLIS_PER_MINUTE;
    private static final Log LOG = LogFactory.getLog(WorkerThreads.class);

    private String name;
    private List<WorkerThread> threads;
    private int maxThreads;
    private BlockingQueue<T> queue = new LinkedBlockingQueue<T>();
    private long threadIndex;
    private Timer cleanUpTimer;

    protected WorkerThreads(final String name, final int maxThreads) {
        this(name, maxThreads, true);
    }

    protected WorkerThreads(final String name, final int maxThreads, final boolean purgeOld) {
        this.name = name;
        this.maxThreads = maxThreads;
        threads = Collections.synchronizedList(new LinkedList<WorkerThread>());
        if (purgeOld) {
            cleanUpTimer = new Timer("Clean up timer for " + name);
            final TimerTask task = new TimerTask() {
                @Override
                public void run() {
                    interruptOldThreads();
                }
            };
            cleanUpTimer.scheduleAtFixedRate(task, CHECK_INTERVAL, CHECK_INTERVAL);
        }
    }

    /**
     * Enqueues the given object for processing
     */
    public synchronized void enqueue(final T object) {
        if (maxThreads <= 0) {
            return;
        }
        queue.offer(object);
        final int queueSize = queue.size();
        final int threadsSize = threads.size();
        if (threadsSize < maxThreads && threadsSize < queueSize) {
            // Start another thread
            final WorkerThread thread = new WorkerThread();
            thread.setName("#" + (threadIndex++) + " " + name);
            threads.add(thread);
            thread.start();
        }
    }

    /**
     * Enqueues all the given objects for processing
     */
    public void enqueueAll(final Collection<T> objects) {
        for (final T object : objects) {
            enqueue(object);
        }
    }

    /**
     * Interrupts all threads
     */
    public synchronized void interrupt() {
        if (cleanUpTimer != null) {
            cleanUpTimer.cancel();
        }
        for (final WorkerThread thread : threads) {
            thread.interrupt();
        }
        threads.clear();
    }

    /**
     * Should be implemented in order to do the actual work with the given object
     */
    protected abstract void process(T object);

    private synchronized void interruptOldThreads() {
        final long tolerance = System.currentTimeMillis() - CHECK_INTERVAL;
        for (final Iterator<WorkerThread> iterator = threads.iterator(); iterator.hasNext();) {
            final WorkerThread thread = iterator.next();
            if (thread.lastUsedAt < tolerance && !thread.inProcess) {
                thread.interrupt();
                iterator.remove();
            }
        }
    }

}