package org.kie.eclipse.navigator.view.actions.repository;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.egit.core.op.PullOperation;
import org.eclipse.egit.ui.JobFamilies;
import org.eclipse.egit.ui.internal.pull.PullResultDialog;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jgit.api.PullResult;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.kie.eclipse.IKieConstants;
import org.kie.eclipse.navigator.Activator;
import org.kie.eclipse.navigator.view.content.RepositoryNode;
import org.kie.eclipse.server.IKieRepositoryHandler;
import org.kie.eclipse.server.IKieServerHandler;
import org.kie.eclipse.server.IKieServiceDelegate;
import org.kie.eclipse.utils.PreferencesUtils;
/**
* UI wrapper for {@link PullOperation}
*/
public class PullOperationUI extends JobChangeAdapter implements IKieConstants {
private static final IStatus NOT_TRIED_STATUS = new Status(IStatus.ERROR, Activator.getDefault().PLUGIN_ID, "Not tried");
private final Repository repository;
private final Shell shell;
private final String repoName;
private final RepositoryNode repoNode;
/**
* Holds the number of PullOperationUIs that need to complete before the
* pull results can be shown. The number includes this instance of
* PullOperationUI and all subtasks spawned by it.
*/
private final AtomicInteger tasksToWaitFor = new AtomicInteger(1);
/** pull results per repository */
protected final Map<Repository, Object> results = Collections.synchronizedMap(new LinkedHashMap<Repository, Object>());
private final PullOperation pullOperation;
/**
* @param repositories
*/
public PullOperationUI(RepositoryNode container) {
this.repoNode = container;
final IKieRepositoryHandler handler = (IKieRepositoryHandler) container.getHandler();
final IKieServerHandler serverHandler = (IKieServerHandler) handler.getRoot();
final IKieServiceDelegate delegate = handler.getDelegate();
this.repository = handler.getRepository();
this.shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
this.repoName = handler.getName();
String host = delegate.getServer().getHost();
int port = delegate.getGitPort();
String username = delegate.getUsername();
String password = delegate.getPassword();
URIish uri = PreferencesUtils.getRepoURI(host, port, username, repoName);
// LoginDialog dlg = new LoginDialog(shell, uri);
// dlg.setUsername(username);
// dlg.setPassword(password);
// if (dlg.open() != Dialog.OK)
// return;
// username = dlg.getUsername();
// password = dlg.getPassword();
uri = PreferencesUtils.getRepoURI(host, port, username, repoName);
final CredentialsProvider credentialsProvider = new KieCredentialsProvider(serverHandler, username, password);
int timeout = serverHandler.getPreference(PREF_REMOTE_TIMEOUT, 60);
Set<Repository> repos = new HashSet<Repository>();
repos.add(repository);
pullOperation = new PullOperation(repos, timeout);
pullOperation.setCredentialsProvider(credentialsProvider);
results.put(repository, NOT_TRIED_STATUS);
}
/**
* Starts this operation asynchronously
*/
public void start() {
start(this);
}
private void start(IJobChangeListener jobChangeListener) {
// figure out a job name
String jobName;
String shortBranchName;
try {
shortBranchName = repository.getBranch();
}
catch (IOException e) {
// ignore here
shortBranchName = ""; //$NON-NLS-1$
}
jobName = NLS.bind("Pulling Branch {0} of Repository {1}", shortBranchName, repoName);
Job job = new WorkspaceJob(jobName) {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) {
execute(monitor);
// we always return OK and handle display of errors on our own
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(Object family) {
if (JobFamilies.PULL.equals(family))
return true;
return super.belongsTo(family);
}
};
job.setRule(null);
job.setUser(true);
job.addJobChangeListener(jobChangeListener);
job.schedule();
}
/**
* Starts this operation synchronously.
*
* @param monitor
*/
@SuppressWarnings("restriction")
public void execute(IProgressMonitor monitor) {
try {
pullOperation.execute(monitor);
results.putAll(pullOperation.getResults());
}
catch (CoreException e) {
if (e.getStatus().getSeverity() == IStatus.CANCEL)
results.putAll(pullOperation.getResults());
else
repoNode.handleException((Throwable) e);
}
}
@Override
public void done(IJobChangeEvent event) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
Map<Repository, Object> res = new LinkedHashMap<Repository, Object>(PullOperationUI.this.results);
handlePullResults(res);
}
});
}
/**
* Post-process the pull results, allowing the user to deal with uncommitted
* changes and re-pull if the initial pull failed because of these changes
*
* @param resultsMap a copy of the initial pull results
* @param shell
*/
private void handlePullResults(final Map<Repository, Object> resultsMap) {
if (tasksToWaitFor.decrementAndGet() == 0 && !results.isEmpty())
showResults();
}
private void showResults() {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
showResults(shell);
}
});
}
private void showResults(final Shell shell) {
if (this.results.isEmpty())
// shouldn't really happen, but just in case...
return;
Entry<Repository, Object> entry = this.results.entrySet().iterator().next();
if (entry.getValue() instanceof PullResult)
new PullResultDialog(shell, entry.getKey(), (PullResult) entry.getValue()).open();
else {
IStatus status = (IStatus) entry.getValue();
if (status == NOT_TRIED_STATUS) {
MessageDialog.openInformation(shell, "Pull Canceled", "The pull operation was canceled.");
}
else if (status.getException() instanceof TransportException) {
ErrorDialog.openError(shell, "Pull Failed", "Git connection problem.\n"
+ "\n\nMaybe you are offline or behind a proxy.\n"
+ "Check your network connection and proxy configuration.", status);
}
else
repoNode.handleException(status.getException());
}
}
}