org.energy_home.jemma.internal.utils.thread.ExecutorManager.java Source code

Java tutorial

Introduction

Here is the source code for org.energy_home.jemma.internal.utils.thread.ExecutorManager.java

Source

/**
 * This file is part of JEMMA - http://jemma.energy-home.org
 * (C) Copyright 2013 Telecom Italia (http://www.telecomitalia.it)
 *
 * JEMMA is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (LGPL) version 3
 * or later as published by the Free Software Foundation, which accompanies
 * this distribution and is available at http://www.gnu.org/licenses/lgpl.html
 *
 * JEMMA 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 Lesser General Public License (LGPL) for more details.
 *
 */
package org.energy_home.jemma.internal.utils.thread;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

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

public class ExecutorManager {
    private static final Log log = LogFactory.getLog(ExecutorManager.class);

    private static final long NEAR_REAL_TIME_DELAY = 100;
    //  TODO: check for execution interruption (currenlty not used): it seems working only if a thread sleep is added to the task
    //   private static final Long MAX_EXECUTION_DURATION = new Long(5000);      
    private static final long SHUTDOWN_TIMEOUT = 10000;

    private static final ExecutorManager instance = new ExecutorManager();

    public static ExecutorManager getInstance() {
        return instance;
    }

    private class UserTasks implements Runnable {
        private List<Runnable> runnableList;
        private Future<?> lastExecutionFuture;
        private Long maxExecutionTime;
        private List<ScheduledUserTask> scheduledList;

        UserTasks(Long maxExecutionTime) {
            this.runnableList = new LinkedList<Runnable>();
            this.scheduledList = new LinkedList<ScheduledUserTask>();
            this.maxExecutionTime = maxExecutionTime;
        }

        void addTask(Runnable task) {
            synchronized (runnableList) {
                runnableList.add(task);
            }
        }

        void waitForTasksCompletion() {
            try {
                synchronized (runnableList) {
                    try {
                        if (lastExecutionFuture != null)
                            lastExecutionFuture.get();
                    } catch (Exception e) {
                        log.error("cancelAllTasks error while waiting for current task execution", e);
                    }
                    while (runnableList.size() > 0)
                        run();
                }
            } catch (Exception e) {
                log.error("cancelAllTasks error", e);
            }
        }

        void addScheduledTask(ScheduledUserTask task) {
            synchronized (scheduledList) {
                scheduledList.add(task);
            }
        }

        void cancelAllScheduledTasks() {
            try {
                synchronized (scheduledList) {
                    ScheduledUserTask scheduledTask;
                    for (Iterator<ScheduledUserTask> iterator = scheduledList.iterator(); iterator.hasNext();) {
                        scheduledTask = (ScheduledUserTask) iterator.next();
                        scheduledTask.scheduledFuture.cancel(true);
                        iterator.remove();
                    }
                }
            } catch (Exception e) {
                log.error("cancelAllScheduledTasks error", e);
            }
        }

        public void run() {
            synchronized (scheduledList) {
                try {
                    // Remove completed scheduled tasks
                    if (scheduledList.size() > 0) {
                        ScheduledUserTask task;
                        for (Iterator<ScheduledUserTask> iterator = scheduledList.iterator(); iterator.hasNext();) {
                            task = (ScheduledUserTask) iterator.next();
                            if (task.scheduledFuture.isDone())
                                iterator.remove();
                        }
                    }
                } catch (Exception e) {
                    log.error("UserTasks run: error while removing scheduled completed tasks", e);
                }
            }
            synchronized (runnableList) {
                if (runnableList.size() > 0) {
                    try {
                        if (lastExecutionFuture != null)
                            lastExecutionFuture.get();
                    } catch (Exception e) {
                        log.error("UserTasks run: error while waiting previous task execution", e);
                    }
                    final Runnable runnable = runnableList.remove(0);
                    numberOfOrderedTasks--;
                    try {
                        if (maxExecutionTime == null) {
                            runnable.run();
                        } else {
                            final Future<?> future = scheduler.submit(runnable);
                            canceller.schedule(new Runnable() {
                                public void run() {
                                    if (!future.isDone()) {
                                        try {
                                            log.error("UserTasks: timeout exceeded, cancelling execution: "
                                                    + runnable);
                                            future.cancel(true);
                                        } catch (Exception e) {
                                            log.error("UserTasks: error while cancelling for timeout: " + runnable);
                                        }
                                    }
                                }
                            }, maxExecutionTime, TimeUnit.MILLISECONDS);
                            lastExecutionFuture = future;
                        }
                    } catch (Exception e) {
                        log.error("UserTasks run error " + runnable, e);
                    }
                }
            }
        }
    }

    private class ScheduledUserTask implements Runnable {
        private Runnable runnable;
        private ScheduledFuture<?> scheduledFuture;
        private Long maxExecutionTime;

        ScheduledUserTask(Runnable runnable, long delay, Long period, Long maxExecutionTime) {
            this.runnable = runnable;
            this.maxExecutionTime = maxExecutionTime;
            if (period == null)
                this.scheduledFuture = scheduler.schedule(this, delay, TimeUnit.MILLISECONDS);
            else
                this.scheduledFuture = scheduler.scheduleWithFixedDelay(this, delay, period.longValue(),
                        TimeUnit.MILLISECONDS);
        }

        ScheduledFuture<?> getScheduledFuture() {
            return scheduledFuture;
        }

        public void run() {
            try {
                if (maxExecutionTime == null) {
                    runnable.run();
                } else {
                    final Future<?> future = scheduler.submit(runnable);
                    canceller.schedule(new Runnable() {
                        public void run() {
                            if (!future.isDone()) {
                                try {
                                    log.error("ScheduledUserTask: timeout exceeded, cancelling execution: "
                                            + runnable);
                                    future.cancel(true);
                                } catch (Exception e) {
                                    log.error("ScheduledUserTask: error while cancelling for timeout: " + runnable);
                                }
                            }
                        }
                    }, maxExecutionTime, TimeUnit.MILLISECONDS);
                }
            } catch (Exception e) {
                log.error("ScheduledUserTask run error " + runnable, e);
            }
        }
    }

    private ScheduledExecutorService scheduler;
    private ScheduledExecutorService canceller;
    private Map<String, UserTasks> nearRealTimeOrderedTasksMap;
    private int numberOfOrderedTasks = 0;
    private Runnable nearRealTimeOrderedTask;
    private ScheduledFuture<?> nearRealTimeMainTaskFuture;

    private class SchedulerThreadFactory implements ThreadFactory {
        public Thread newThread(Runnable r) {
            return new Thread(r, "Executor Scheduler Thread");
        }
    }

    private class CancellerThreadFactory implements ThreadFactory {
        public Thread newThread(Runnable r) {
            return new Thread(r, "Executor Canceller Thread");
        }
    }

    public ExecutorManager() {
        log.info("Calling constructor...");
        nearRealTimeOrderedTasksMap = new HashMap<String, UserTasks>();
    }

    private void start() {
        log.info("ExecutorManager starting...");
        numberOfOrderedTasks = 0;
        scheduler = Executors.newScheduledThreadPool(1, new SchedulerThreadFactory());
        canceller = Executors.newScheduledThreadPool(1, new CancellerThreadFactory());
        nearRealTimeOrderedTask = new Runnable() {
            public void run() {
                try {
                    UserTasks[] tasksArray = null;
                    synchronized (nearRealTimeOrderedTasksMap) {
                        if (nearRealTimeOrderedTasksMap.size() > 0) {
                            tasksArray = new UserTasks[nearRealTimeOrderedTasksMap.size()];
                            nearRealTimeOrderedTasksMap.values().toArray(tasksArray);
                        }
                    }
                    if (tasksArray != null) {
                        for (int i = 0; i < tasksArray.length; i++) {
                            tasksArray[i].run();
                        }
                    }
                    synchronized (nearRealTimeOrderedTasksMap) {
                        if (numberOfOrderedTasks == 0)
                            nearRealTimeMainTaskFuture.cancel(true);
                    }
                } catch (Exception e) {
                    log.error("nearRealTimeTask run error", e);
                }
            }
        };
    }

    private void stop() {
        log.info("ExecutorManager stopping...");
        scheduler.shutdown();
        try {
            scheduler.awaitTermination(SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error("scheduler await termination error", e);
        }
        canceller.shutdown();
        try {
            canceller.awaitTermination(0, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error("canceller await termination error", e);
        }
        nearRealTimeOrderedTasksMap.clear();
    }

    private UserTasks getUserTasks(String user) {
        synchronized (nearRealTimeOrderedTasksMap) {
            return nearRealTimeOrderedTasksMap.get(user);
        }
    }

    public void addUser(String user) {
        log.info("Adding user " + user);
        synchronized (nearRealTimeOrderedTasksMap) {
            if (nearRealTimeOrderedTasksMap.size() == 0)
                start();
            nearRealTimeOrderedTasksMap.put(user, new UserTasks(null));
        }
    }

    public void removeUser(String user) {
        log.info("Removing user " + user);
        UserTasks tasks;
        synchronized (nearRealTimeOrderedTasksMap) {
            tasks = nearRealTimeOrderedTasksMap.remove(user);
        }
        if (tasks != null) {
            tasks.cancelAllScheduledTasks();
            tasks.waitForTasksCompletion();
        }
        synchronized (nearRealTimeOrderedTasksMap) {
            if (nearRealTimeOrderedTasksMap.size() == 0) {
                stop();
                return;
            }
        }
    }

    public void addNearRealTimeOrderedTask(String user, Runnable runnable) {
        synchronized (nearRealTimeOrderedTasksMap) {
            UserTasks tasks = nearRealTimeOrderedTasksMap.get(user);
            if (tasks == null)
                return;
            tasks.addTask(runnable);
            if (numberOfOrderedTasks == 0)
                nearRealTimeMainTaskFuture = scheduler.scheduleWithFixedDelay(nearRealTimeOrderedTask,
                        NEAR_REAL_TIME_DELAY, NEAR_REAL_TIME_DELAY, TimeUnit.MILLISECONDS);
            numberOfOrderedTasks++;
        }
    }

    public ScheduledFuture<?> scheduleTask(String user, Runnable runnable, long delay) {
        synchronized (nearRealTimeOrderedTasksMap) {
            UserTasks tasks = getUserTasks(user);
            if (tasks == null)
                return null;
            ScheduledUserTask scheduledTask = new ScheduledUserTask(runnable, delay, null, null);
            tasks.addScheduledTask(scheduledTask);
            return scheduledTask.getScheduledFuture();
        }
    }

    public ScheduledFuture<?> scheduleTask(String user, Runnable runnable, long delay, long period) {
        synchronized (nearRealTimeOrderedTasksMap) {
            UserTasks tasks = getUserTasks(user);
            if (tasks == null)
                return null;
            ScheduledUserTask scheduledTask = new ScheduledUserTask(runnable, delay, new Long(period), null);
            tasks.addScheduledTask(scheduledTask);
            return scheduledTask.getScheduledFuture();
        }
    }
}