blob: ae55513709a08ca90b6f0f7fca04f0150aab7e69 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2008 Tasktop Technologies 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:
* Tasktop Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.internal.tasks.core.sync;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.net.Policy;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.ITasksCoreConstants;
import org.eclipse.mylyn.internal.tasks.core.TaskList;
import org.eclipse.mylyn.internal.tasks.core.ITasksCoreConstants.MutexSchedulingRule;
import org.eclipse.mylyn.internal.tasks.core.data.TaskDataManager;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.IRepositoryManager;
import org.eclipse.mylyn.tasks.core.IRepositoryModel;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskContainer;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.ITask.SynchronizationState;
import org.eclipse.mylyn.tasks.core.data.AbstractTaskDataHandler;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.eclipse.mylyn.tasks.core.data.TaskDataCollector;
import org.eclipse.mylyn.tasks.core.data.TaskRelation;
import org.eclipse.mylyn.tasks.core.data.TaskRelation.Direction;
import org.eclipse.mylyn.tasks.core.data.TaskRelation.Kind;
import org.eclipse.mylyn.tasks.core.sync.SynchronizationJob;
/**
* @author Mik Kersten
* @author Rob Elves
* @author Steffen Pingel
*/
public class SynchronizeTasksJob extends SynchronizationJob {
private final AbstractRepositoryConnector connector;
private final TaskDataManager taskDataManager;
private final TaskList taskList;
private final Set<ITask> allTasks;
private final IRepositoryManager repositoryManager;
private TaskRepository taskRepository;
private Map<String, TaskRelation[]> relationsByTaskId;
private boolean updateRelations;
private final IRepositoryModel tasksModel;
private SynchronizationSession session;
private final List<IStatus> statuses;
public SynchronizeTasksJob(TaskList taskList, TaskDataManager synchronizationManager, IRepositoryModel tasksModel,
AbstractRepositoryConnector connector, TaskRepository taskRepository, Set<ITask> tasks) {
this(taskList, synchronizationManager, tasksModel, connector, (IRepositoryManager) null, tasks);
this.taskRepository = taskRepository;
}
public SynchronizeTasksJob(TaskList taskList, TaskDataManager synchronizationManager, IRepositoryModel tasksModel,
AbstractRepositoryConnector connector, IRepositoryManager repositoryManager, Set<ITask> tasks) {
super("Synchronizing Tasks (" + tasks.size() + " tasks)"); //$NON-NLS-1$ //$NON-NLS-2$
this.taskList = taskList;
this.taskDataManager = synchronizationManager;
this.tasksModel = tasksModel;
this.connector = connector;
this.repositoryManager = repositoryManager;
this.allTasks = tasks;
this.statuses = new ArrayList<IStatus>();
setRule(new MutexSchedulingRule());
}
@Override
public IStatus run(IProgressMonitor monitor) {
try {
if (taskRepository == null) {
try {
monitor.beginTask(Messages.SynchronizeTasksJob_Processing, allTasks.size() * 100);
// group tasks by repository
Map<TaskRepository, Set<ITask>> tasksByRepository = new HashMap<TaskRepository, Set<ITask>>();
for (ITask task : allTasks) {
TaskRepository repository = repositoryManager.getRepository(task.getConnectorKind(),
task.getRepositoryUrl());
Set<ITask> tasks = tasksByRepository.get(repository);
if (tasks == null) {
tasks = new HashSet<ITask>();
tasksByRepository.put(repository, tasks);
}
tasks.add(task);
}
// synchronize tasks for each repositories
for (TaskRepository taskRepository : tasksByRepository.keySet()) {
setName(MessageFormat.format(Messages.SynchronizeTasksJob_Synchronizing_Tasks__X_,
taskRepository.getRepositoryLabel()));
this.taskRepository = taskRepository;
Set<ITask> repositoryTasks = tasksByRepository.get(taskRepository);
run(repositoryTasks, new SubProgressMonitor(monitor, repositoryTasks.size() * 100));
}
} finally {
monitor.done();
}
} else {
run(allTasks, monitor);
}
} catch (OperationCanceledException e) {
for (ITask task : allTasks) {
((AbstractTask) task).setSynchronizing(false);
taskList.notifyElementChanged(task);
}
return Status.CANCEL_STATUS;
}
return Status.OK_STATUS;
}
private void run(Set<ITask> tasks, IProgressMonitor monitor) {
relationsByTaskId = new HashMap<String, TaskRelation[]>();
updateRelations = true;
runInternal(tasks, monitor);
synchronizedTaskRelations(monitor, relationsByTaskId);
}
public void synchronizedTaskRelations(IProgressMonitor monitor, Map<String, TaskRelation[]> relationsByTaskId) {
updateRelations = false;
for (String taskId : relationsByTaskId.keySet()) {
ITask parentTask = taskList.getTask(taskRepository.getRepositoryUrl(), taskId);
if (parentTask instanceof ITaskContainer) {
Set<ITask> removedChildTasks = new HashSet<ITask>(((ITaskContainer) parentTask).getChildren());
TaskRelation[] relations = relationsByTaskId.get(taskId);
for (TaskRelation relation : relations) {
if (relation.getDirection() == Direction.OUTWARD && relation.getKind() == Kind.CONTAINMENT) {
ITask task = taskList.getTask(taskRepository.getRepositoryUrl(), relation.getTaskId());
if (task == null) {
try {
task = synchronizeTask(monitor, relation.getTaskId());
} catch (CoreException e) {
StatusHandler.log(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN,
"Synchronization failed", e)); //$NON-NLS-1$
}
} else {
removedChildTasks.remove(task);
}
if (task != null) {
taskList.addTask(task, (AbstractTaskContainer) parentTask);
}
}
}
for (ITask task : removedChildTasks) {
taskList.removeFromContainer((AbstractTaskContainer) parentTask, task);
}
}
}
}
private void runInternal(Set<ITask> tasks, IProgressMonitor monitor) {
try {
monitor.beginTask(Messages.SynchronizeTasksJob_Processing, tasks.size() * 100);
if (canGetMultiTaskData(taskRepository)) {
try {
for (ITask task : tasks) {
resetStatus(task);
}
synchronizeTasks(new SubProgressMonitor(monitor, tasks.size() * 100), taskRepository, tasks);
} catch (CoreException e) {
for (ITask task : tasks) {
updateStatus(taskRepository, task, e.getStatus());
}
}
} else {
for (ITask task : tasks) {
Policy.checkCanceled(monitor);
resetStatus(task);
try {
synchronizeTask(new SubProgressMonitor(monitor, 100), task);
} catch (CoreException e) {
updateStatus(taskRepository, task, e.getStatus());
}
}
}
} catch (OperationCanceledException e) {
throw e;
} catch (Exception e) {
StatusHandler.log(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN, "Synchronization failed", e)); //$NON-NLS-1$
} finally {
monitor.done();
}
}
private boolean canGetMultiTaskData(TaskRepository taskRepository) {
AbstractTaskDataHandler taskDataHandler = connector.getTaskDataHandler();
return taskDataHandler != null && taskDataHandler.canGetMultiTaskData(taskRepository);
}
private void synchronizeTask(IProgressMonitor monitor, ITask task) throws CoreException {
monitor.subTask(MessageFormat.format(Messages.SynchronizeTasksJob_Receiving_task_X, task.getSummary()));
resetStatus(task);
if (!isUser()) {
monitor = Policy.backgroundMonitorFor(monitor);
}
String taskId = task.getTaskId();
TaskData taskData = connector.getTaskData(taskRepository, taskId, monitor);
if (taskData != null) {
updateFromTaskData(taskRepository, task, taskData);
return;
}
throw new CoreException(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN,
"Connector failed to return task data for task \"" + task + "\"")); //$NON-NLS-1$ //$NON-NLS-2$
}
private ITask synchronizeTask(IProgressMonitor monitor, String taskId) throws CoreException {
monitor.subTask(MessageFormat.format(Messages.SynchronizeTasksJob_Receiving_task_X, taskId));
if (!isUser()) {
monitor = Policy.backgroundMonitorFor(monitor);
}
TaskData taskData = connector.getTaskData(taskRepository, taskId, monitor);
if (taskData != null) {
return createFromTaskData(taskRepository, taskId, taskData);
}
throw new CoreException(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN,
"Connector failed to return task data for task \"" + taskId + "\"")); //$NON-NLS-1$ //$NON-NLS-2$
}
private void resetStatus(ITask task) {
((AbstractTask) task).setStatus(null);
taskList.notifySynchronizationStateChanged(task);
}
private void synchronizeTasks(IProgressMonitor monitor, final TaskRepository repository, Set<ITask> tasks)
throws CoreException {
monitor.subTask(MessageFormat.format(Messages.SynchronizeTasksJob_Receiving_X_tasks_from_X, tasks.size(),
repository.getRepositoryLabel()));
final Map<String, ITask> idToTask = new HashMap<String, ITask>();
for (ITask task : tasks) {
idToTask.put(task.getTaskId(), task);
}
TaskDataCollector collector = new TaskDataCollector() {
@Override
public void accept(TaskData taskData) {
ITask task = idToTask.remove(taskData.getTaskId());
if (task != null) {
updateFromTaskData(repository, task, taskData);
}
}
};
if (!isUser()) {
monitor = Policy.backgroundMonitorFor(monitor);
}
Set<String> taskIds = Collections.unmodifiableSet(new HashSet<String>(idToTask.keySet()));
connector.getTaskDataHandler().getMultiTaskData(repository, taskIds, collector, monitor);
}
private void updateFromTaskData(TaskRepository taskRepository, ITask task, TaskData taskData) {
try {
taskDataManager.putUpdatedTaskData(task, taskData, isUser(), getSession());
if (updateRelations) {
Collection<TaskRelation> relations = connector.getTaskRelations(taskData);
if (relations != null) {
relationsByTaskId.put(task.getTaskId(), relations.toArray(new TaskRelation[0]));
}
}
} catch (CoreException e) {
updateStatus(taskRepository, task, e.getStatus());
}
}
private ITask createFromTaskData(TaskRepository taskRepository, String taskId, TaskData taskData)
throws CoreException {
ITask task = tasksModel.createTask(taskRepository, taskData.getTaskId());
((AbstractTask) task).setSynchronizationState(SynchronizationState.INCOMING_NEW);
taskDataManager.putUpdatedTaskData(task, taskData, isUser(), getSession());
return task;
}
private void updateStatus(TaskRepository repository, ITask task, IStatus status) {
statuses.add(status);
((AbstractTask) task).setStatus(status);
if (!isUser()) {
((AbstractTask) task).setSynchronizing(false);
}
taskList.notifyElementChanged(task);
}
public SynchronizationSession getSession() {
return session;
}
public void setSession(SynchronizationSession session) {
this.session = session;
}
public Collection<IStatus> getStatuses() {
return Collections.unmodifiableCollection(statuses);
}
}