view src/main/java/org/icedrobot/ika/output/StatusKeeper.java @ 29:3b0d6002605a

Add patch to output commands names. Contributed by Giulio Franco.
author Mario Torre <neugens.limasoftware@gmail.com>
date Fri, 15 Apr 2011 17:13:03 +0200
parents
children
line wrap: on
line source

/*
 * IKA - IcedRobot Kiosk Application
 * Copyright (C) 2011  IcedRobot team
 *
 * 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 org.icedrobot.ika.output;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Central repository which can gather status information from multiple
 * working threads.
 * Working threads can register to the StatusKeeper using
 * {@link #registerThread}, then they can periodically send status information
 * updates with the {@link #updateStatus} method.
 * Data consumers can access progress information through getter methods or
 * by registering themselves as listeners.
 */
public class StatusKeeper {

    private static StatusKeeper instance;          //Singleton instance


    private ConcurrentMap<String, WorkerStatus> workerData;
    private final List<String> insertionOrder;
    private CopyOnWriteArrayList<StatusListener> listeners;


    /**
     * StatusKeeper is a singleton, because it is a centralized repository.
     */
    private StatusKeeper() {
        workerData = new ConcurrentHashMap<String, WorkerStatus>();
        insertionOrder = new ArrayList<String>();
        listeners = new CopyOnWriteArrayList<StatusListener>();
    }


    /**
     * Obtains the instance of StatusKeeper
     */
    public static synchronized StatusKeeper getInstance() {
        if (instance == null) {
            instance = new StatusKeeper();
        }

        return instance;
    }


    /**
     * Registers a new worker as active.
     * Each worker is registered with a unique name
     * @param workerName    Unique name identifying the worker.
     * @param status        Current status of the process.
     */
    public void registerWorker (WorkerStatus status) {

        if (workerData.putIfAbsent(status.getName(), status) != null) {
            throw new IllegalArgumentException("Worker " + status.getName()
                                                + " is already registered.");
        }
        
        synchronized (insertionOrder) {
            insertionOrder.add(status.getName());
        }
    }

    /**
     * Registers a new worker as active, with a starting empty status.
     * Each worker is registered with a unique name
     * @param workerName    Unique name identifying the worker.
     * @param status        Current status of the process.
     */
    public void registerWorker (String workerName) {

        registerWorker(new DefaultWorkerStatus(workerName));
    }

    /**
     * Deregister a previously registered worker, notifying its termination.
     * @param workerName    Name the worker was registered with.
     */
    public void deregisterWorker (String workerName) {
        synchronized (insertionOrder) {
            if (insertionOrder.remove(workerName) == false) {
                throw new IllegalArgumentException(
                                "Worker " + workerName + " is not registered.");
            }
        }

        workerData.remove(workerName);
    }


    /**
     * Updates the status associated to a previously registered worker.
     * @param workerName    Worker whose status is to be updated
     * @param data          New status of the worker.
     */
    public void reportStatus (WorkerStatus data) {
        if (workerData.replace(data.getName(), data) == null) {
            throw new IllegalArgumentException("Worker " + data.getName()
                                                + " is not registered.");
        }
        fireStatusUpdate(data.getName(), data);
    }


    /**
     * Returns an iterable collection over the workers currently registered and
     * their statuses.
     * @return Iterable collection of map entries
     */
    public Iterable<WorkerStatus> statusList() {
        String[] workerNames;
        ArrayList<WorkerStatus> iterable;
        WorkerStatus status;
        
        synchronized (insertionOrder) {
            workerNames = new String[insertionOrder.size()];
            workerNames = insertionOrder.toArray(workerNames);
        }

        iterable = new ArrayList<WorkerStatus>(workerNames.length);
        for (String w : workerNames) {
            status = workerData.get(w);
            if (status != null) {
                iterable.add(status);
            }
        }

        return iterable;
    }

    /**
     * Returns the current status of a worker
     * @param workerName    Name of the worker
     * @return              Current status of the worker,
     *                      or null if workerName is not a registered worker.
     */
    public WorkerStatus getWorkerStatus(String workerName) {
        return workerData.get(workerName);
    }

    /**
     * Returns the number of active workers,
     * i.e. the number of registered workers that are not completed.
     * @return  Count of active workers
     */
    public int getActiveTasksCount() {
        int aCount = 0;

        for (WorkerStatus stat : workerData.values()) {
            if (!stat.completed()) {
                aCount += 1;
            }
        }

        return aCount;
    }


    /**
     * Adds a listener to the list of those that will be notified about
     * changes in registered tasks' statuses.
     * @param l     Listener to register
     */
    public void addStatusListener (StatusListener l) {
        listeners.add(l);
    }

    /**
     * Removes a status listener from the StatusKeeper
     * @param l     Listener to remove
     */
    public void removeStatusListener (StatusListener l) {
        listeners.remove(l);
    }

    public int getStatusListenersCount () {
        return listeners.size();
    }

    protected void fireStatusUpdate (String workerName, WorkerStatus status) {
        for (StatusListener l : listeners) {
            l.statusUpdate(workerName, status);
        }
    }
}