| /******************************************************************************* |
| * Copyright (c) 2009, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Sonatype, Inc. - ongoing development |
| * Red Hat Inc. - Bug 460967 |
| ******************************************************************************/ |
| |
| package org.eclipse.equinox.p2.ui; |
| |
| import java.net.URI; |
| import java.util.Collection; |
| import org.eclipse.core.runtime.*; |
| 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 ServiceHelper.getService(ProvUIActivator.getContext(), LicenseManager.class); |
| } |
| |
| /** |
| * 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 session.getProvisioningAgent().getService(RepositoryTracker.class); |
| } |
| |
| /** |
| * 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) { |
| return openInstallWizard(initialSelections, operation, null, job); |
| } |
| |
| /** |
| * Open an install wizard for installing the specified IInstallableUnits and |
| * remediationOperation. |
| * |
| * @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 remediationOperation the alternate operations if the proposed update |
| * failed. May 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 |
| * @see RemediationOperation |
| * @since 2.3 |
| */ |
| public int openInstallWizard(Collection<IInstallableUnit> initialSelections, InstallOperation operation, |
| RemediationOperation remediationOperation, 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); |
| wizard.setRemediationOperation(remediationOperation); |
| 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) { |
| return openUpdateWizard(skipSelectionsPage, operation, null, job); |
| |
| } |
| |
| /** |
| * Open an update wizard for the specified update operation and |
| * remediationOperation. |
| * |
| * @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 remediationOperation the alternate operations if the proposed update |
| * failed. May 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 |
| * @since 2.3 |
| */ |
| public int openUpdateWizard(boolean skipSelectionsPage, UpdateOperation operation, |
| RemediationOperation remediationOperation, 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.setRemediationOperation(remediationOperation); |
| 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 && remediationOperation != null |
| && remediationOperation.getResolutionResult() != Status.OK_STATUS) { |
| 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; |
| |
| @Override |
| 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(); |
| } |
| |
| @Override |
| protected boolean isResizable() { |
| return true; |
| } |
| |
| @Override |
| protected void okPressed() { |
| if (page.performOk()) |
| super.okPressed(); |
| } |
| |
| @Override |
| 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.isEmpty()) { |
| name = repo.getName(); |
| if (name != null && !name.isEmpty()) |
| 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; |
| } |
| } |