/******************************************************************************* * Copyright (c) 2009, 2011 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 * Sonatype, Inc. - ongoing development ******************************************************************************/ package org.eclipse.equinox.p2.ui; import java.net.URI; import java.util.Collection; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper; import org.eclipse.equinox.internal.p2.core.helpers.Tracing; import org.eclipse.equinox.internal.p2.ui.*; import org.eclipse.equinox.internal.p2.ui.dialogs.*; import org.eclipse.equinox.internal.provisional.p2.repository.RepositoryEvent; import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.engine.IProfileRegistry; import org.eclipse.equinox.p2.engine.ProvisioningContext; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.operations.*; import org.eclipse.equinox.p2.repository.IRepository; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.jface.preference.PreferenceDialog; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.*; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.PreferencesUtil; /** * ProvisioningUI defines the provisioning session, UI policy, and related services for a * provisioning UI. * * @since 2.0 * */ public class ProvisioningUI { /** * Return the default ProvisioningUI. * * @return the default Provisioning UI. */ public static ProvisioningUI getDefaultUI() { return ProvUIActivator.getDefault().getProvisioningUI(); } private Policy policy; private ProvisioningSession session; private String profileId; private ProvisioningOperationRunner runner; /** * Creates a new instance of the provisioning user interface. * * @param session The current provisioning session * @param profileId The profile that this user interface is operating on * @param policy The user interface policy settings to use */ public ProvisioningUI(ProvisioningSession session, String profileId, Policy policy) { this.policy = policy; this.profileId = profileId; if (profileId == null) this.profileId = IProfileRegistry.SELF; this.session = session; this.runner = new ProvisioningOperationRunner(this); // register this UI with the agent so other UI related agent services can find it session.getProvisioningAgent().registerService(ProvisioningUI.class.getName(), this); } /** * Return the UI policy used for this instance of the UI. * * @return the UI policy, must not be <code>null</code> */ public Policy getPolicy() { return policy; } /** * Return the provisioning session that should be used to obtain * provisioning services. * * @return the provisioning session, must not be <code>null</code> */ public ProvisioningSession getSession() { return session; } /** * Return the license manager that should be used to remember * accepted user licenses. * @return the license manager. May be <code>null</code> if licenses are not * to be remembered. */ public LicenseManager getLicenseManager() { return (LicenseManager) ServiceHelper.getService(ProvUIActivator.getContext(), LicenseManager.class.getName()); } /** * Return the repository tracker that should be used to add, remove, and track the * statuses of known repositories. * * @return the repository tracker, must not be <code>null</code> */ public RepositoryTracker getRepositoryTracker() { return (RepositoryTracker) session.getProvisioningAgent().getService(RepositoryTracker.class.getName()); } /** * Return the profile id that should be assumed for this ProvisioningUI if no other * id is otherwise specified. Some UI classes are assigned a profile id, while others * are not. For those classes that are not assigned a current profile id, this id can * be used to obtain one. * * @return a profile id */ public String getProfileId() { return profileId; } /** * Return an install operation that describes installing the specified IInstallableUnits from the * provided list of repositories. * * @param iusToInstall the IInstallableUnits to be installed * @param repositories the repositories to use for the operation * @return the install operation */ public InstallOperation getInstallOperation(Collection<IInstallableUnit> iusToInstall, URI[] repositories) { InstallOperation op = new InstallOperation(getSession(), iusToInstall); op.setProfileId(getProfileId()); op.setProvisioningContext(makeProvisioningContext(repositories)); return op; } /** * Return an update operation that describes updating the specified IInstallableUnits from the * provided list of repositories. * * @param iusToUpdate the IInstallableUnits to be updated * @param repositories the repositories to use for the operation * @return the update operation */ public UpdateOperation getUpdateOperation(Collection<IInstallableUnit> iusToUpdate, URI[] repositories) { UpdateOperation op = new UpdateOperation(getSession(), iusToUpdate); op.setProfileId(getProfileId()); op.setProvisioningContext(makeProvisioningContext(repositories)); return op; } /** * Return an uninstall operation that describes uninstalling the specified IInstallableUnits, using * the supplied repositories to replace any metadata that must be retrieved for the uninstall. * * @param iusToUninstall the IInstallableUnits to be installed * @param repositories the repositories to use for the operation * @return the uninstall operation */ public UninstallOperation getUninstallOperation(Collection<IInstallableUnit> iusToUninstall, URI[] repositories) { UninstallOperation op = new UninstallOperation(getSession(), iusToUninstall); op.setProfileId(getProfileId()); op.setProvisioningContext(makeProvisioningContext(repositories)); return op; } private ProvisioningContext makeProvisioningContext(URI[] repos) { if (repos != null) { ProvisioningContext context = new ProvisioningContext(getSession().getProvisioningAgent()); context.setMetadataRepositories(repos); context.setArtifactRepositories(repos); return context; } // look everywhere return new ProvisioningContext(getSession().getProvisioningAgent()); } /** * Open an install wizard for installing the specified IInstallableUnits * * @param initialSelections the IInstallableUnits that should be selected when the wizard opens. May be <code>null</code>. * @param operation the operation describing the proposed install. If this operation is not <code>null</code>, then a wizard showing * only the IInstallableUnits described in the operation will be shown. If the operation is <code>null</code>, then a * wizard allowing the user to browse the repositories will be opened. * @param job a repository load job that is loading or has already loaded the repositories. Can be used to pass along * an in-memory repository reference to the wizard. * * @return the wizard return code */ public int openInstallWizard(Collection<IInstallableUnit> initialSelections, InstallOperation operation, LoadMetadataRepositoryJob job) { if (operation == null) { InstallWizard wizard = new InstallWizard(this, operation, initialSelections, job); WizardDialog dialog = new ProvisioningWizardDialog(ProvUI.getDefaultParentShell(), wizard); dialog.create(); PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(), IProvHelpContextIds.INSTALL_WIZARD); return dialog.open(); } PreselectedIUInstallWizard wizard = new PreselectedIUInstallWizard(this, operation, initialSelections, job); WizardDialog dialog = new ProvisioningWizardDialog(ProvUI.getDefaultParentShell(), wizard); dialog.create(); PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(), IProvHelpContextIds.INSTALL_WIZARD); return dialog.open(); } /** * Open an update wizard for the specified update operation. * * @param skipSelectionsPage <code>true</code> if the selection page should be skipped so that the user is * viewing the resolution results. <code>false</code> if the update selection page should be shown first. * @param operation the operation describing the proposed update. Must not be <code>null</code>. * @param job a repository load job that is loading or has already loaded the repositories. Can be used to pass along * an in-memory repository reference to the wizard. * * @return the wizard return code */ public int openUpdateWizard(boolean skipSelectionsPage, UpdateOperation operation, LoadMetadataRepositoryJob job) { if (getPolicy().getUpdateWizardStyle() == Policy.UPDATE_STYLE_SINGLE_IUS && UpdateSingleIUWizard.validFor(operation)) { UpdateSingleIUWizard wizard = new UpdateSingleIUWizard(this, operation); WizardDialog dialog = new WizardDialog(ProvUI.getDefaultParentShell(), wizard); dialog.create(); // TODO do we need a hook for providing custom help? Or would this just be shown in the update browser? return dialog.open(); } UpdateWizard wizard = new UpdateWizard(this, operation, operation.getSelectedUpdates(), job); wizard.setSkipSelectionsPage(skipSelectionsPage); WizardDialog dialog = new ProvisioningWizardDialog(ProvUI.getDefaultParentShell(), wizard); dialog.create(); PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(), IProvHelpContextIds.UPDATE_WIZARD); if (wizard.getCurrentStatus().getSeverity() == IStatus.ERROR) { wizard.deselectLockedIUs(); } return dialog.open(); } /** * Open an uninstall wizard for the specified uninstall operation. * * @param initialSelections the IInstallableUnits that should be selected when the wizard opens. May be <code>null</code>. * @param operation the operation describing the proposed uninstall. Must not be <code>null</code>. * @param job a repository load job that is loading or has already loaded the repositories. Can be used to pass along * an in-memory repository reference to the wizard. * * @return the wizard return code */ public int openUninstallWizard(Collection<IInstallableUnit> initialSelections, UninstallOperation operation, LoadMetadataRepositoryJob job) { UninstallWizard wizard = new UninstallWizard(this, operation, initialSelections, job); WizardDialog dialog = new ProvisioningWizardDialog(ProvUI.getDefaultParentShell(), wizard); dialog.create(); PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(), IProvHelpContextIds.UNINSTALL_WIZARD); return dialog.open(); } /** * Open a UI that allows the user to manipulate the repositories. * @param shell the shell that should parent the UI */ public void manipulateRepositories(Shell shell) { if (policy.getRepositoryPreferencePageId() != null) { PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(shell, policy.getRepositoryPreferencePageId(), null, null); dialog.open(); } else { TitleAreaDialog dialog = new TitleAreaDialog(shell) { RepositoryManipulationPage page; protected Control createDialogArea(Composite parent) { page = new RepositoryManipulationPage(); page.setProvisioningUI(ProvisioningUI.this); page.init(PlatformUI.getWorkbench()); page.createControl(parent); this.setTitle(ProvUIMessages.RepositoryManipulationPage_Title); this.setMessage(ProvUIMessages.RepositoryManipulationPage_Description); Control control = page.getControl(); control.setLayoutData(new GridData(GridData.FILL_BOTH)); return page.getControl(); } protected boolean isResizable() { return true; } protected void okPressed() { if (page.performOk()) super.okPressed(); } protected void cancelPressed() { if (page.performCancel()) super.cancelPressed(); } }; dialog.open(); } } /** * Schedule a job to execute the supplied ProvisioningOperation. * * @param job The operation to execute * @param errorStyle the flags passed to the StatusManager for error reporting */ public void schedule(final ProvisioningJob job, final int errorStyle) { job.setUser(true); runner.schedule(job, errorStyle); } /** * Manage the supplied job as a provisioning operation. This will allow * the ProvisioningUI to be aware that a provisioning job is running, as well * as manage the restart behavior for the job. * * @param job the job to be managed * @param jobRestartPolicy an integer constant specifying whether the * supplied job should cause a restart of the system. The UI Policy's * restart policy is used in conjunction with this constant to determine * what actually occurs when a job completes. * * @see ProvisioningJob#RESTART_NONE * @see ProvisioningJob#RESTART_ONLY * @see ProvisioningJob#RESTART_OR_APPLY */ public void manageJob(Job job, final int jobRestartPolicy) { runner.manageJob(job, jobRestartPolicy); } /** * Return a boolean indicating whether the receiver has scheduled any operations * for the profile under management. * * @return <code>true</code> if other provisioning operations have been scheduled, * <code>false</code> if there are no operations scheduled. */ public boolean hasScheduledOperations() { return getSession().hasScheduledOperationsFor(profileId); } /** * This method is for automated testing only. * @return the provisioning operation that can suppress restart for automated testing. * @noreference This method is not intended to be referenced by clients. */ public ProvisioningOperationRunner getOperationRunner() { return runner; } /** * Signal that a repository operation is about to begin. This allows clients to ignore intermediate * events until the operation is completed. Callers are responsible for ensuring that * a corresponding operation ending event is signaled. */ public void signalRepositoryOperationStart() { runner.eventBatchCount++; if (Tracing.DEBUG_EVENTS_CLIENT) Tracing.debug("Batch Count Incremented to: " + Integer.toString(runner.eventBatchCount)); //$NON-NLS-1$ ProvUI.getProvisioningEventBus(getSession()).publishEvent(new RepositoryOperationBeginningEvent(this)); } /** * Signal that a repository operation has completed. * * @param event a {@link RepositoryEvent} that describes the overall operation. May be <code>null</code>, which * indicates that there was no single event that can describe the operation. * @param update <code>true</code> if the event should be reflected in the UI, false if it should be ignored. */ public void signalRepositoryOperationComplete(RepositoryEvent event, boolean update) { runner.eventBatchCount--; if (Tracing.DEBUG_EVENTS_CLIENT) Tracing.debug("Batch Count Decremented to: " + Integer.toString(runner.eventBatchCount)); //$NON-NLS-1$ ProvUI.getProvisioningEventBus(getSession()).publishEvent(new RepositoryOperationEndingEvent(this, update, event)); } /** * Load the specified metadata repository, signaling a repository operation start event * before loading, and a repository operation complete event after loading. * * @param location the location of the repository * @param notify <code>true</code> if the UI should be updated as a result of the load, <code>false</code> if it should not * @param monitor the progress monitor to be used * @return the repository * @throws ProvisionException if the repository could not be loaded */ public IMetadataRepository loadMetadataRepository(URI location, boolean notify, IProgressMonitor monitor) throws ProvisionException { IMetadataRepository repo = null; try { signalRepositoryOperationStart(); IMetadataRepositoryManager manager = ProvUI.getMetadataRepositoryManager(getSession()); repo = manager.loadRepository(location, monitor); // If there is no user nickname assigned to this repo but there is a provider name, then set the nickname. // This will keep the name in the manager even when the repo is not loaded String name = manager.getRepositoryProperty(location, IRepository.PROP_NICKNAME); if (name == null || name.length() == 0) { name = repo.getName(); if (name != null && name.length() > 0) manager.setRepositoryProperty(location, IRepository.PROP_NICKNAME, name); } } catch (ProvisionException e) { getRepositoryTracker().reportLoadFailure(location, e); } finally { // We have no idea how many repos may have been touched as a result of loading this one. signalRepositoryOperationComplete(null, notify); } return repo; } /** * Load the specified artifact repository, signaling a repository operation start event * before loading, and a repository operation complete event after loading. * * @param location the location of the repository * @param update <code>true</code> if the UI should be updated as a result of the load, <code>false</code> if it should not * @param monitor the progress monitor to be used * @return the repository * @throws ProvisionException if the repository could not be loaded */ public IArtifactRepository loadArtifactRepository(URI location, boolean update, IProgressMonitor monitor) throws ProvisionException { IArtifactRepository repo; signalRepositoryOperationStart(); try { IArtifactRepositoryManager manager = ProvUI.getArtifactRepositoryManager(getSession()); repo = manager.loadRepository(location, monitor); // If there is no user nickname assigned to this repo but there is a provider name, then set the nickname. // This will keep the name in the manager even when the repo is not loaded String name = manager.getRepositoryProperty(location, IRepository.PROP_NICKNAME); if (name == null) { name = manager.getRepositoryProperty(location, IRepository.PROP_NAME); if (name != null) manager.setRepositoryProperty(location, IRepository.PROP_NICKNAME, name); } } finally { // We have no idea how many repos may have been touched as a result of loading this one, // so we do not use a specific repository event to represent it. signalRepositoryOperationComplete(null, update); } return repo; } }