/*******************************************************************************
* Copyright (c) 2011, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.orion.internal.server.core.tasks;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.core.runtime.IPath;
import org.eclipse.orion.server.core.LogHelper;
import org.eclipse.orion.server.core.resources.UniversalUniqueIdentifier;
import org.eclipse.orion.server.core.tasks.CorruptedTaskException;
import org.eclipse.orion.server.core.tasks.ITaskCanceller;
import org.eclipse.orion.server.core.tasks.ITaskService;
import org.eclipse.orion.server.core.tasks.TaskDoesNotExistException;
import org.eclipse.orion.server.core.tasks.TaskInfo;
import org.eclipse.orion.server.core.tasks.TaskInfo.TaskStatus;
import org.eclipse.orion.server.core.tasks.TaskJob;
import org.eclipse.orion.server.core.tasks.TaskOperationException;
/**
* A concrete implementation of the {@link ITaskService}.
*/
public class TaskService implements ITaskService {
private TaskStore store;
private Timer timer;
private static long TEMP_TASK_LIFE = 15 * 60 * 1000; // 15 minutes in milliseconds
private Map<TaskDescription, ITaskCanceller> taskCancellers = new HashMap<TaskDescription, ITaskCanceller>();
private TaskCleanupJob taskCleanupJob;
private class RemoveTask extends TimerTask {
private TaskDescription taskDescription;
private ITaskService taskService;
public RemoveTask(TaskDescription taskDescription, ITaskService taskService) {
super();
this.taskDescription = taskDescription;
this.taskService = taskService;
}
@Override
public void run() {
try {
taskService.removeTask(taskDescription.getUserId(), taskDescription.getTaskId(), taskDescription.isKeep());
} catch (TaskDoesNotExistException e) {
// ignore, task was already removed
} catch (TaskOperationException e) {
LogHelper.log(e);
}
}
}
public TaskService(IPath baseLocation) {
store = new TaskStore(baseLocation.toFile());
timer = new Timer();
initTaskCleanupJob(store);
}
private TaskInfo internalRemoveTask(String userId, String id, boolean keep, Date dateRemoved) throws TaskOperationException {
TaskInfo task = getTask(userId, id, keep);
if (task == null)
throw new TaskDoesNotExistException(id);
if (task.isRunning())
throw new TaskOperationException("Cannot remove a task that is running. Try to cancel first");
if (!store.removeTask(new TaskDescription(userId, id, keep)))
throw new TaskOperationException("Task could not be removed");
return task;
}
public void removeTask(String userId, String id, boolean keep) throws TaskOperationException {
Date date = new Date();
internalRemoveTask(userId, id, keep, date);
}
public void removeCompletedTasks(String userId) {
Date date = new Date();
for (TaskInfo task : getTasks(userId)) {
if (!task.isRunning()) {
try {
internalRemoveTask(userId, task.getId(), task.isKeep(), date);
} catch (TaskOperationException e) {
LogHelper.log(e);
}
}
}
}
public TaskInfo createTask(String userId, boolean keep) {
TaskInfo task = new TaskInfo(userId, new UniversalUniqueIdentifier().toBase64String(), keep);
store.writeTask(new TaskDescription(userId, task.getId(), keep), task.toJSON().toString());
return task;
}
public int getActiveCount() {
return TaskJob.GetActiveCount();
}
public TaskInfo getTask(String userId, String id, boolean keep) {
TaskDescription taskDescr = new TaskDescription(userId, id, keep);
String taskString = store.readTask(taskDescr);
if (taskString == null)
return null;
TaskInfo info;
try {
info = TaskInfo.fromJSON(taskDescr, taskString);
if (taskCancellers.containsKey(taskDescr)) {
info.setCancelable(true);
}
return info;
} catch (CorruptedTaskException e) {
LogHelper.log(e);
store.removeTask(new TaskDescription(userId, id, keep));
}
return null;
}
public void updateTask(TaskInfo task) {
TaskDescription taskDescription = new TaskDescription(task.getUserId(), task.getId(), task.isKeep());
store.writeTask(taskDescription, task.toJSON().toString());
if (!task.isRunning()) {
if (task.isKeep()) {
if (task.getExpires() != null) {
timer.schedule(new RemoveTask(taskDescription, this), new Date(task.getExpires()));
}
} else {
timer.schedule(new RemoveTask(taskDescription, this), TEMP_TASK_LIFE);
}
taskCancellers.remove(taskDescription);
}
}
public List<TaskInfo> getTasks(String userId) {
List<TaskInfo> tasks = new ArrayList<TaskInfo>();
for (TaskDescription taskDescr : store.readAllTasks(userId)) {
TaskInfo info;
try {
String taskString = store.readTask(taskDescr);
if (taskString == null) {
continue; // Task removed in between
}
info = TaskInfo.fromJSON(taskDescr, taskString);
if (taskCancellers.containsKey(taskDescr)) {
info.setCancelable(true);
}
tasks.add(info);
} catch (CorruptedTaskException e) {
LogHelper.log(e);
store.removeTask(taskDescr);
}
}
return tasks;
}
public synchronized TaskInfo createTask(String userId, boolean keep, ITaskCanceller taskCanceller) {
TaskInfo info = createTask(userId, keep);
taskCancellers.put(new TaskDescription(info.getUserId(), info.getId(), info.isKeep()), taskCanceller);
info.setCancelable(true);
return info;
}
public synchronized void cancelTask(String userId, String id, boolean keep) throws TaskOperationException {
TaskDescription taskDescription = new TaskDescription(userId, id, keep);
ITaskCanceller taskCanceller = taskCancellers.get(taskDescription);
if (taskCanceller == null) {
TaskInfo task = getTask(userId, id, keep);
if (task == null || task.isRunning() == false) {
return;
}
throw new TaskOperationException("Task does not support cancelling");
}
if (!taskCanceller.cancelTask()) {
throw new TaskOperationException("Cancelling task failed");
}
TaskInfo task = getTask(userId, id, keep);
task.setStatus(TaskStatus.ABORT);
updateTask(task);
taskCancellers.remove(taskDescription);
}
private void initTaskCleanupJob(TaskStore store) {
taskCleanupJob = new TaskCleanupJob(store);
/*
* Schedule the job to start after a random delay of up to 24 hours,
* in an attempt to stagger runs of the job on concurrently-deployed
* servers.
*/
taskCleanupJob.schedule((int)(Math.random() * 86400000));
}
}