blob: 1c5f453e14bcf5136cbc2b5f950295f7a3f59925 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 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.equinox.internal.provisional.p2.ui;
import java.io.IOException;
import java.util.HashSet;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.*;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.equinox.internal.p2.ui.ProvUIActivator;
import org.eclipse.equinox.internal.p2.ui.ProvUIMessages;
import org.eclipse.equinox.internal.p2.ui.dialogs.ApplyProfileChangesDialog;
import org.eclipse.equinox.internal.provisional.configurator.Configurator;
import org.eclipse.equinox.internal.provisional.p2.ui.operations.ProfileModificationOperation;
import org.eclipse.equinox.internal.provisional.p2.ui.operations.ProvisioningOperation;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressConstants;
import org.eclipse.ui.progress.WorkbenchJob;
import org.eclipse.ui.statushandlers.StatusManager;
/**
* Utility methods for running provisioning operations. Operations can either
* be run synchronously or in a job. When scheduled as a job, the operation
* determines whether the job is run in
* the background or in the UI.
*
* @since 3.4
*/
public class ProvisioningOperationRunner {
private static final String PROPERTY_PREFIX = "org.eclipse.equinox.p2.ui"; //$NON-NLS-1$
private static final QualifiedName OPERATION_KEY = new QualifiedName(PROPERTY_PREFIX, "operationKey"); //$NON-NLS-1$
static HashSet scheduledJobs = new HashSet();
static boolean restartRequested = false;
static boolean restartRequired = false;
static ListenerList jobListeners = new ListenerList();
/**
* Run the provisioning operation synchronously, adding it to the undo history if it
* supports undo. Should only be used for operations that run quickly.
* @param op The operation to execute
* @param shell provided by the caller in order to supply UI information for prompting the
* user if necessary. May be <code>null</code>.
* @param errorStyle the flags passed to the StatusManager for error reporting
*/
public static void run(ProvisioningOperation op, Shell shell, int errorStyle) {
try {
if (op instanceof IUndoableOperation) {
PlatformUI.getWorkbench().getOperationSupport().getOperationHistory().execute((IUndoableOperation) op, null, ProvUI.getUIInfoAdapter(shell));
} else {
op.execute(null, ProvUI.getUIInfoAdapter(shell));
}
} catch (ExecutionException e) {
ProvUI.handleException(e.getCause(), NLS.bind(ProvUIMessages.ProvisioningOperationRunner_ErrorExecutingOperation, op.getLabel()), errorStyle);
}
}
/**
* Schedule a job to execute the supplied ProvisioningOperation, and add it to the
* undo history if it supports undo.
*
* @param op The operation to execute
* @param shell provided by the caller in order to supply UI information for prompting the
* user if necessary. May be <code>null</code>.
* @param errorStyle the flags passed to the StatusManager for error reporting
*/
public static Job schedule(final ProvisioningOperation op, final Shell shell, final int errorStyle) {
Job job;
final boolean noPrompt = (errorStyle & (StatusManager.BLOCK | StatusManager.SHOW)) == 0;
if (op.runInBackground()) {
job = new Job(op.getLabel()) {
protected IStatus run(IProgressMonitor monitor) {
final Job thisJob = this;
try {
IStatus status;
if (op instanceof IUndoableOperation) {
status = PlatformUI.getWorkbench().getOperationSupport().getOperationHistory().execute((IUndoableOperation) op, monitor, ProvUI.getUIInfoAdapter(shell));
} else {
status = op.execute(monitor, ProvUI.getUIInfoAdapter(shell));
}
if (status != Status.OK_STATUS && noPrompt) {
this.setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE);
this.setProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY, Boolean.TRUE);
}
return status;
} catch (final ExecutionException e) {
if (noPrompt) {
thisJob.setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE);
thisJob.setProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY, Boolean.TRUE);
}
String message = e.getCause().getLocalizedMessage();
if (message == null)
message = NLS.bind(ProvUIMessages.ProvisioningOperationRunner_ErrorExecutingOperation, op.getLabel());
return new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, 0, message, e.getCause());
}
}
};
job.setPriority(Job.LONG);
} else {
job = new WorkbenchJob(op.getLabel()) {
public IStatus runInUIThread(IProgressMonitor monitor) {
try {
IStatus status;
if (op instanceof IUndoableOperation) {
status = PlatformUI.getWorkbench().getOperationSupport().getOperationHistory().execute((IUndoableOperation) op, monitor, ProvUI.getUIInfoAdapter(shell));
} else {
status = op.execute(monitor, ProvUI.getUIInfoAdapter(shell));
}
if (status != Status.OK_STATUS && noPrompt) {
this.setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE);
this.setProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY, Boolean.TRUE);
}
return status;
} catch (ExecutionException e) {
if (noPrompt) {
this.setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE);
this.setProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY, Boolean.TRUE);
}
return new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, 0, NLS.bind(ProvUIMessages.ProvisioningOperationRunner_ErrorExecutingOperation, op.getLabel()), e.getCause());
}
}
};
job.setPriority(Job.SHORT);
}
job.setUser(op.isUser());
job.setProperty(OPERATION_KEY, op);
job.setProperty(IProgressConstants.ICON_PROPERTY, ProvUIImages.getImageDescriptor(ProvUIImages.IMG_PROFILE));
manageJob(job);
job.schedule();
return job;
}
public static void requestRestart(boolean force) {
if (hasScheduledOperations()) {
restartRequested = true;
restartRequired = restartRequired || force;
return;
}
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
if (PlatformUI.getWorkbench().isClosing())
return;
int retCode = ApplyProfileChangesDialog.promptForRestart(ProvUI.getDefaultParentShell(), restartRequired);
// Now that we have asked, regardless of answer, we won't need to
// ask again until the next profile changing operation. Don't reset
// the restart required flag so that the next time we ask, if it
// was required before, it will still be required.
restartRequested = false;
if (retCode == ApplyProfileChangesDialog.PROFILE_APPLYCHANGES) {
Configurator configurator = (Configurator) ServiceHelper.getService(ProvUIActivator.getContext(), Configurator.class.getName());
try {
configurator.applyConfiguration();
} catch (IOException e) {
ProvUI.handleException(e, ProvUIMessages.ProvUI_ErrorDuringApplyConfig, StatusManager.LOG | StatusManager.BLOCK);
}
} else if (retCode == ApplyProfileChangesDialog.PROFILE_RESTART) {
PlatformUI.getWorkbench().restart();
}
}
});
}
public static boolean hasScheduledOperations() {
return !scheduledJobs.isEmpty();
}
public static boolean hasScheduledOperationsFor(String profileId) {
Job[] jobs = getScheduledJobs();
for (int i = 0; i < jobs.length; i++) {
Object op = jobs[i].getProperty(OPERATION_KEY);
if (op instanceof ProfileModificationOperation) {
String id = ((ProfileModificationOperation) op).getProfileId();
if (profileId.equals(id))
return true;
}
}
return false;
}
public static void addJobChangeListener(IJobChangeListener listener) {
jobListeners.add(listener);
Job[] jobs = getScheduledJobs();
for (int i = 0; i < jobs.length; i++)
jobs[i].addJobChangeListener(listener);
}
public static void removeJobChangeListener(IJobChangeListener listener) {
jobListeners.remove(listener);
Job[] jobs = getScheduledJobs();
for (int i = 0; i < jobs.length; i++)
jobs[i].removeJobChangeListener(listener);
}
private static Job[] getScheduledJobs() {
return (Job[]) scheduledJobs.toArray(new Job[scheduledJobs.size()]);
}
public static void manageJob(Job job) {
scheduledJobs.add(job);
job.addJobChangeListener(new JobChangeAdapter() {
public void done(IJobChangeEvent event) {
scheduledJobs.remove(event.getJob());
if (restartRequested) {
requestRestart(restartRequired);
}
}
});
Object[] listeners = jobListeners.getListeners();
for (int i = 0; i < listeners.length; i++)
job.addJobChangeListener((IJobChangeListener) listeners[i]);
}
}