blob: ed0cc8cdef3309d5941e513b907ee141e6ea83d3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2015 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.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
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.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
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.ITasksCoreConstants;
import org.eclipse.mylyn.internal.tasks.core.ITasksCoreConstants.ObjectSchedulingRule;
import org.eclipse.mylyn.internal.tasks.core.RepositoryQuery;
import org.eclipse.mylyn.internal.tasks.core.TaskList;
import org.eclipse.mylyn.internal.tasks.core.data.TaskDataManager;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.IRepositoryModel;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITask.SynchronizationState;
import org.eclipse.mylyn.tasks.core.TaskRepository;
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.sync.ISynchronizationSession;
import org.eclipse.mylyn.tasks.core.sync.SynchronizationJob;
import org.eclipse.osgi.util.NLS;
/**
* @author Mik Kersten
* @author Rob Elves
* @author Steffen Pingel
*/
public class SynchronizeQueriesJob extends SynchronizationJob {
private class TaskCollector extends TaskDataCollector {
private final Set<ITask> removedQueryResults;
private final RepositoryQuery repositoryQuery;
private int resultCount;
private final SynchronizationSession session;
public TaskCollector(RepositoryQuery repositoryQuery, SynchronizationSession session) {
this.repositoryQuery = repositoryQuery;
this.session = session;
this.removedQueryResults = new HashSet<ITask>(repositoryQuery.getChildren());
}
@Override
public void accept(TaskData taskData) {
ITask task = taskList.getTask(taskData.getRepositoryUrl(), taskData.getTaskId());
if (task == null) {
task = tasksModel.createTask(repository, taskData.getTaskId());
((AbstractTask) task).setSynchronizationState(SynchronizationState.INCOMING_NEW);
if (taskData.isPartial() && connector.canSynchronizeTask(repository, task)) {
session.markStale(task);
}
} else {
removedQueryResults.remove(task);
}
taskList.addTask(task, repositoryQuery);
try {
session.putTaskData(task, taskData);
} catch (CoreException e) {
StatusHandler.log(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN, "Failed to save task", e)); //$NON-NLS-1$
}
resultCount++;
}
public Set<ITask> getRemovedChildren() {
return removedQueryResults;
}
public int getResultCount() {
return resultCount;
}
@Override
public void failed(String taskId, IStatus status) {
ITask task = taskList.getTask(repositoryQuery.getRepositoryUrl(), taskId);
if (task != null && status != null) {
statuses.add(status);
((AbstractTask) task).setStatus(status);
if (!isUser()) {
((AbstractTask) task).setSynchronizing(false);
}
taskList.notifyElementChanged(task);
}
}
}
private final AbstractRepositoryConnector connector;
private final Set<RepositoryQuery> queries;
private final TaskRepository repository;
private final TaskDataManager taskDataManager;
private final TaskList taskList;
private final IRepositoryModel tasksModel;
private final List<IStatus> statuses;
public SynchronizeQueriesJob(TaskList taskList, TaskDataManager taskDataManager, IRepositoryModel tasksModel,
AbstractRepositoryConnector connector, TaskRepository repository, Set<RepositoryQuery> queries) {
super(Messages.SynchronizeQueriesJob_Synchronizing_Queries + " (" + repository.getRepositoryLabel() + ")"); //$NON-NLS-1$//$NON-NLS-2$
this.taskList = taskList;
this.taskDataManager = taskDataManager;
this.tasksModel = tasksModel;
this.connector = connector;
this.repository = repository;
this.queries = queries;
this.statuses = new ArrayList<IStatus>();
}
@Override
public IStatus run(IProgressMonitor jobMonitor) {
try {
monitor.setCanceled(false);
monitor.attach(jobMonitor);
try {
monitor.beginTask(Messages.SynchronizeQueriesJob_Processing, 20 + queries.size() * 20 + 40 + 10);
Set<ITask> allTasks;
if (!isFullSynchronization()) {
allTasks = new HashSet<ITask>();
for (RepositoryQuery query : queries) {
allTasks.addAll(query.getChildren());
}
} else {
allTasks = taskList.getTasks(repository.getRepositoryUrl());
}
ObjectSchedulingRule rule = new ObjectSchedulingRule(repository);
try {
Job.getJobManager().beginRule(rule, monitor);
final Map<String, TaskRelation[]> relationsByTaskId = new HashMap<String, TaskRelation[]>();
SynchronizationSession session = new SynchronizationSession(taskDataManager) {
@Override
public void putTaskData(ITask task, TaskData taskData) throws CoreException {
boolean changed = connector.hasTaskChanged(repository, task, taskData);
taskDataManager.putUpdatedTaskData(task, taskData, isUser(), this, monitor);
if (taskData.isPartial()) {
if (changed && connector.canSynchronizeTask(repository, task)) {
markStale(task);
}
} else {
Collection<TaskRelation> relations = connector.getTaskRelations(taskData);
if (relations != null) {
relationsByTaskId.put(task.getTaskId(), relations.toArray(new TaskRelation[0]));
}
}
}
};
session.setTaskRepository(repository);
session.setFullSynchronization(isFullSynchronization());
session.setTasks(Collections.unmodifiableSet(allTasks));
session.setNeedsPerformQueries(true);
session.setUser(isUser());
updateQueryStatus(null);
try {
boolean success = preSynchronization(session, new SubProgressMonitor(monitor, 20));
if ((success && session.needsPerformQueries()) || isUser()) {
// synchronize queries, tasks changed within query are added to set of tasks to be synchronized
synchronizeQueries(monitor, session);
} else {
monitor.worked(queries.size() * 20);
}
} finally {
for (RepositoryQuery repositoryQuery : queries) {
repositoryQuery.setSynchronizing(false);
}
taskList.notifySynchronizationStateChanged(queries);
}
Set<ITask> tasksToBeSynchronized = new HashSet<ITask>();
for (ITask task : session.getStaleTasks()) {
tasksToBeSynchronized.add(task);
((AbstractTask) task).setSynchronizing(true);
}
// synchronize tasks that were marked by the connector
SynchronizeTasksJob job = new SynchronizeTasksJob(taskList, taskDataManager, tasksModel, connector,
repository, tasksToBeSynchronized);
job.setFetchSubtasks(getFetchSubtasks());
job.setUser(isUser());
job.setSession(session);
if (!tasksToBeSynchronized.isEmpty()) {
Policy.checkCanceled(monitor);
IStatus result = job.run(new SubProgressMonitor(monitor, 30));
if (result == Status.CANCEL_STATUS) {
throw new OperationCanceledException();
}
statuses.addAll(job.getStatuses());
}
monitor.subTask(Messages.SynchronizeQueriesJob_Receiving_related_tasks);
job.synchronizedTaskRelations(monitor, relationsByTaskId);
monitor.worked(10);
session.setChangedTasks(tasksToBeSynchronized);
if (statuses.size() > 0) {
Status status = new MultiStatus(ITasksCoreConstants.ID_PLUGIN, 0,
statuses.toArray(new IStatus[0]), "Query synchronization failed", null); //$NON-NLS-1$
session.setStatus(status);
}
// hook into the connector for synchronization time stamp management
postSynchronization(session, new SubProgressMonitor(monitor, 10));
} finally {
Job.getJobManager().endRule(rule);
}
} catch (OperationCanceledException e) {
return Status.CANCEL_STATUS;
} catch (Exception e) {
StatusHandler
.log(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN, "Synchronization failed", e)); //$NON-NLS-1$
} catch (LinkageError e) {
StatusHandler.log(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN,
NLS.bind("Synchronization for connector ''{0}'' failed", connector.getConnectorKind()), e)); //$NON-NLS-1$
} finally {
monitor.done();
}
} finally {
monitor.detach(jobMonitor);
}
return Status.OK_STATUS;
}
private void synchronizeQueries(IProgressMonitor monitor, SynchronizationSession session) {
for (RepositoryQuery repositoryQuery : queries) {
Policy.checkCanceled(monitor);
monitor.subTask(MessageFormat.format(Messages.SynchronizeQueriesJob_Synchronizing_query_X,
repositoryQuery.getSummary()));
synchronizeQuery(repositoryQuery, session, new SubProgressMonitor(monitor, 20));
}
}
private boolean postSynchronization(SynchronizationSession event, IProgressMonitor monitor) {
try {
Policy.checkCanceled(monitor);
monitor.subTask(Messages.SynchronizeQueriesJob_Updating_repository_state);
if (!isUser()) {
monitor = Policy.backgroundMonitorFor(monitor);
}
connector.postSynchronization(event, monitor);
return true;
} catch (CoreException e) {
updateQueryStatus(e.getStatus());
return false;
}
}
private boolean preSynchronization(ISynchronizationSession event, IProgressMonitor monitor) {
try {
Policy.checkCanceled(monitor);
monitor.subTask(Messages.SynchronizeQueriesJob_Querying_repository);
if (!isUser()) {
monitor = Policy.backgroundMonitorFor(monitor);
}
connector.preSynchronization(event, monitor);
return true;
} catch (CoreException e) {
// synchronization is unlikely to succeed, inform user and exit
updateQueryStatus(e.getStatus());
statuses.add(e.getStatus());
return false;
}
}
private void synchronizeQuery(RepositoryQuery repositoryQuery, SynchronizationSession event,
IProgressMonitor monitor) {
TaskCollector collector = new TaskCollector(repositoryQuery, event);
if (!isUser()) {
monitor = Policy.backgroundMonitorFor(monitor);
}
IStatus result = connector.performQuery(repository, repositoryQuery, collector, event, monitor);
if (result == null || result.isOK()) {
Set<ITask> removedChildren = collector.getRemovedChildren();
if (!removedChildren.isEmpty()) {
taskList.removeFromContainer(repositoryQuery, removedChildren);
}
repositoryQuery.setLastSynchronizedStamp(new SimpleDateFormat("MMM d, H:mm:ss").format(new Date())); //$NON-NLS-1$
} else if (result.getSeverity() == IStatus.CANCEL) {
throw new OperationCanceledException();
} else {
repositoryQuery.setStatus(result);
statuses.add(result);
}
}
private void updateQueryStatus(final IStatus status) {
for (RepositoryQuery repositoryQuery : queries) {
repositoryQuery.setStatus(status);
}
taskList.notifySynchronizationStateChanged(queries);
}
public Collection<IStatus> getStatuses() {
return Collections.unmodifiableCollection(statuses);
}
}