/** * File ./src/main/java/de/lemo/dms/processing/AnalysisTaskManager.java * Lemo-Data-Management-Server for learning analytics. * Copyright (C) 2013 * Leonard Kappe, Andreas Pursian, Sebastian Schwarzrock, Boris Wenzlaff * * 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 * 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/>. **/ /** * File BideTask.java * Date 22.04.2013 * Project Lemo Learning Analytics */ package de.lemo.dms.processing; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.log4j.Logger; import de.lemo.dms.core.config.ServerConfiguration; import de.lemo.dms.service.ServiceTaskManager; /** * Thread pool to ensure that a user can start any long running analysis only once. Instead of a result, the client gets * an URL that may be polled until the processing has finished. * * @author Boris Wenzlaff * @author Leonard Kappe */ public class AnalysisTaskManager { private final Logger logger = Logger.getLogger(getClass()); // TODO arbitrary value, make configurable private static final int MAX_THREADS = 10; // TODO use enum singleton pattern private static AnalysisTaskManager INSTANCE = null; private TaskTimeoutThread resultTimeoutThread; // TODO use own thread factory to set low priority! private ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS); private Map<String, AnalysisTask> tasks = Collections.synchronizedMap(new HashMap<String, AnalysisTask>()); private AnalysisTaskManager() { long computationTimeout = ServerConfiguration.getInstance().getPathAnalysisTimeout(); long resultTimeout = computationTimeout; startResultTimeoutThread(computationTimeout, resultTimeout); } /** * implements singleton pattern * * @return Instance of the singleton */ public static synchronized AnalysisTaskManager getInstance() { if (INSTANCE == null) { INSTANCE = new AnalysisTaskManager(); } return INSTANCE; } /** * Starts a new asynchronous task. The result may be polled from the {@link ServiceTaskManager} with the task's ID. * If any task with the same ID is queued, running or done, it will be canceled and any results will be removed. * * @param taskId * id to identify the task * @param task * the task to add */ public synchronized void addTask(AnalysisTask task) { String taskId = task.getTaskId(); // check if any task by this user is already running and delete any pending results AnalysisTask pendingTask = tasks.remove(taskId); if (pendingTask != null) { logger.debug("cancelled pending task: " + taskId); pendingTask.cancel(); } // create a new task and let it run at some point later synchronized (executor) { Future<?> future = executor.submit(task); task.setFuture(future); tasks.put(taskId, task); logger.debug("submitted task " + taskId); } } public AnalysisTask getTask(String key) { return tasks.get(key); } private void startResultTimeoutThread(long computationTimeout, long resultTimeout) { resultTimeoutThread = new TaskTimeoutThread(tasks, computationTimeout, resultTimeout); resultTimeoutThread.setPriority(Thread.NORM_PRIORITY); resultTimeoutThread.start(); } }