| /******************************************************************************* |
| * Copyright (c) 2007, 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 |
| * Ericsson AB (Hamdan Msheik) - Bug 396420 - Control Install dialog through preference customization |
| * Red Hat Inc. - Bug 460967 |
| * Mickael Istria (Red Hat Inc.) - 483644 Improve "No updates found" dialog |
| *******************************************************************************/ |
| |
| package org.eclipse.equinox.internal.p2.ui; |
| |
| import java.util.*; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| import org.eclipse.core.commands.*; |
| import org.eclipse.core.commands.common.NotDefinedException; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest; |
| import org.eclipse.equinox.internal.p2.metadata.ProvidedCapability; |
| import org.eclipse.equinox.internal.p2.ui.dialogs.ILayoutConstants; |
| import org.eclipse.equinox.internal.p2.ui.query.IUViewQueryContext; |
| import org.eclipse.equinox.internal.p2.ui.viewers.IUColumnConfig; |
| import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus; |
| import org.eclipse.equinox.p2.engine.*; |
| import org.eclipse.equinox.p2.metadata.*; |
| import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription; |
| import org.eclipse.equinox.p2.operations.*; |
| import org.eclipse.equinox.p2.planner.IPlanner; |
| import org.eclipse.equinox.p2.planner.IProfileChangeRequest; |
| import org.eclipse.equinox.p2.query.QueryUtil; |
| import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; |
| import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; |
| import org.eclipse.equinox.p2.ui.Policy; |
| import org.eclipse.equinox.p2.ui.ProvisioningUI; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.osgi.util.ManifestElement; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.commands.ICommandService; |
| import org.eclipse.ui.handlers.IHandlerService; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.Constants; |
| |
| /** |
| * Generic provisioning UI utility and policy methods. |
| * |
| * @since 3.4 |
| */ |
| public class ProvUI { |
| |
| // Public constants for common command and tooltip names |
| public static final String INSTALL_COMMAND_LABEL = ProvUIMessages.InstallIUCommandLabel; |
| public static final String INSTALL_COMMAND_TOOLTIP = ProvUIMessages.InstallIUCommandTooltip; |
| public static final String UNINSTALL_COMMAND_LABEL = ProvUIMessages.UninstallIUCommandLabel; |
| public static final String UNINSTALL_COMMAND_TOOLTIP = ProvUIMessages.UninstallIUCommandTooltip; |
| public static final String UPDATE_COMMAND_LABEL = ProvUIMessages.UpdateIUCommandLabel; |
| public static final String UPDATE_COMMAND_TOOLTIP = ProvUIMessages.UpdateIUCommandTooltip; |
| public static final String REVERT_COMMAND_LABEL = ProvUIMessages.RevertIUCommandLabel; |
| public static final String REVERT_COMMAND_TOOLTIP = ProvUIMessages.RevertIUCommandTooltip; |
| |
| /** |
| * A constant indicating that there was nothing to size (there was no valid plan |
| * that could be used to compute size). |
| */ |
| public static final long SIZE_NOTAPPLICABLE = -3L; |
| /** |
| * Indicates that the size is unavailable (an attempt was made to compute size |
| * but it failed) |
| */ |
| public static final long SIZE_UNAVAILABLE = -2L; |
| /** |
| * Indicates that the size is currently unknown |
| */ |
| public static final long SIZE_UNKNOWN = -1L; |
| |
| private static IUColumnConfig[] columnConfig; |
| |
| // These values rely on the command markup in org.eclipse.ui.ide that defines |
| // the update commands |
| private static final String UPDATE_MANAGER_FIND_AND_INSTALL = "org.eclipse.ui.update.findAndInstallUpdates"; //$NON-NLS-1$ |
| // This value relies on the command markup in org.eclipse.ui |
| private static final String INSTALLATION_DIALOG = "org.eclipse.ui.help.installationDialog"; //$NON-NLS-1$ |
| |
| public static IStatus handleException(Throwable t, String message, int style) { |
| if (message == null && t != null) { |
| message = t.getMessage(); |
| } |
| IStatus status = new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, 0, message, t); |
| StatusManager.getManager().handle(status, style); |
| return status; |
| } |
| |
| public static void reportStatus(IStatus status, int style) { |
| // workaround for |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=211933 |
| // Note we'd rather have a proper looking dialog than get the |
| // blocking right. |
| if ((style & StatusManager.BLOCK) == StatusManager.BLOCK |
| || (style & StatusManager.SHOW) == StatusManager.SHOW) { |
| if (status.getSeverity() == IStatus.INFO) { |
| final MessageDialogWithLink dialog = new MessageDialogWithLink(ProvUI.getDefaultParentShell(), |
| ProvUIMessages.ProvUI_InformationTitle, null, status.getMessage(), MessageDialog.INFORMATION, 0, |
| IDialogConstants.OK_LABEL); |
| if (status.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) { |
| dialog.addSelectionListener(SelectionListener.widgetSelectedAdapter( |
| e -> ProvisioningUI.getDefaultUI().manipulateRepositories(dialog.getShell()))); |
| } |
| dialog.open(); |
| // unset the dialog bits |
| style = style & ~StatusManager.BLOCK; |
| style = style & ~StatusManager.SHOW; |
| // unset logging for statuses that should never be logged. |
| // Ideally the caller would do this but this bug keeps coming back. |
| // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274074 |
| if (status.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) |
| style = 0; |
| } else if (status.getSeverity() == IStatus.WARNING) { |
| MessageDialog.openWarning(ProvUI.getDefaultParentShell(), ProvUIMessages.ProvUI_WarningTitle, |
| status.getMessage()); |
| // unset the dialog bits |
| style = style & ~StatusManager.BLOCK; |
| style = style & ~StatusManager.SHOW; |
| } |
| } |
| if (style != 0) |
| StatusManager.getManager().handle(status, style); |
| } |
| |
| public static IUColumnConfig[] getIUColumnConfig() { |
| if (columnConfig == null) |
| columnConfig = new IUColumnConfig[] { |
| new IUColumnConfig(ProvUIMessages.ProvUI_NameColumnTitle, IUColumnConfig.COLUMN_NAME, |
| ILayoutConstants.DEFAULT_PRIMARY_COLUMN_WIDTH), |
| new IUColumnConfig(ProvUIMessages.ProvUI_VersionColumnTitle, IUColumnConfig.COLUMN_VERSION, |
| ILayoutConstants.DEFAULT_COLUMN_WIDTH) }; |
| return columnConfig; |
| |
| } |
| |
| // Factory method returning a new instance of a IUViewQueryContext |
| public static IUViewQueryContext getQueryContext(Policy policy) { |
| IUViewQueryContext queryContext = new IUViewQueryContext( |
| policy.getGroupByCategory() ? IUViewQueryContext.AVAILABLE_VIEW_BY_CATEGORY |
| : IUViewQueryContext.AVAILABLE_VIEW_FLAT); |
| queryContext.setShowInstallChildren(policy.getShowDrilldownRequirements()); |
| queryContext.setShowProvisioningPlanChildren(policy.getShowDrilldownRequirements()); |
| |
| // among other things the 4 calls below are used to control the available |
| // software dialog (AvailableIUPage) |
| queryContext.setShowLatestVersionsOnly(policy.getShowLatestVersionsOnly()); |
| queryContext.setHideAlreadyInstalled(policy.getHideAlreadyInstalled()); |
| queryContext.setUseCategories(policy.getGroupByCategory()); |
| queryContext.setFilterOnEnv(policy.getFilterOnEnv()); |
| return queryContext; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static <T> T getAdapter(Object object, Class<T> adapterType) { |
| if (object == null) |
| return null; |
| if (adapterType.isInstance(object)) |
| // Ideally, we would use Class.cast here but it was introduced in Java 1.5 |
| return (T) object; |
| if (object instanceof IAdaptable) |
| // Ideally, we would use Class.cast here but it was introduced in Java 1.5 |
| return ((IAdaptable) object).getAdapter(adapterType); |
| return null; |
| } |
| |
| /** |
| * Returns a shell that is appropriate to use as the parent for a modal dialog. |
| */ |
| public static Shell getDefaultParentShell() { |
| return PlatformUI.getWorkbench().getModalDialogShellProvider().getShell(); |
| } |
| |
| public static void openUpdateManagerInstaller(Event event) { |
| runCommand(UPDATE_MANAGER_FIND_AND_INSTALL, |
| ProvUIMessages.UpdateManagerCompatibility_UnableToOpenFindAndInstall, event); |
| } |
| |
| public static void openInstallationDialog(Event event) { |
| runCommand(INSTALLATION_DIALOG, ProvUIMessages.ProvUI_InstallDialogError, event); |
| } |
| |
| public static boolean isUpdateManagerInstallerPresent() { |
| ICommandService commandService = PlatformUI.getWorkbench().getService(ICommandService.class); |
| Command command = commandService.getCommand(UPDATE_MANAGER_FIND_AND_INSTALL); |
| return command.isDefined(); |
| } |
| |
| private static void runCommand(String commandId, String errorMessage, Event event) { |
| ICommandService commandService = PlatformUI.getWorkbench().getService(ICommandService.class); |
| Command command = commandService.getCommand(commandId); |
| if (!command.isDefined()) { |
| return; |
| } |
| IHandlerService handlerService = PlatformUI.getWorkbench().getService(IHandlerService.class); |
| try { |
| handlerService.executeCommand(commandId, event); |
| } catch (ExecutionException e) { |
| reportFail(errorMessage, e); |
| } catch (NotDefinedException e) { |
| reportFail(errorMessage, e); |
| } catch (NotEnabledException e) { |
| reportFail(errorMessage, e); |
| } catch (NotHandledException e) { |
| reportFail(errorMessage, e); |
| } |
| } |
| |
| public static boolean isCategory(IInstallableUnit iu) { |
| return QueryUtil.isCategory(iu); |
| } |
| |
| private static void reportFail(String message, Throwable t) { |
| Status failStatus = new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, message, t); |
| reportStatus(failStatus, StatusManager.BLOCK | StatusManager.LOG); |
| } |
| |
| /** |
| * Get sizing information about the specified plan. |
| * |
| * @param engine the engine |
| * @param plan the provisioning plan |
| * @param context the provisioning context to be used for the sizing |
| * @param monitor the progress monitor |
| * |
| * @return a long integer describing the disk size required for the provisioning |
| * plan. |
| * |
| * @see #SIZE_UNKNOWN |
| * @see #SIZE_UNAVAILABLE |
| * @see #SIZE_NOTAPPLICABLE |
| */ |
| public static long getSize(IEngine engine, IProvisioningPlan plan, ProvisioningContext context, |
| IProgressMonitor monitor) { |
| // If there is nothing to size, return 0 |
| if (plan == null) |
| return SIZE_NOTAPPLICABLE; |
| if (countPlanElements(plan) == 0) |
| return 0; |
| long installPlanSize = 0; |
| SubMonitor mon = SubMonitor.convert(monitor, 300); |
| if (plan.getInstallerPlan() != null) { |
| ISizingPhaseSet sizingPhaseSet = PhaseSetFactory.createSizingPhaseSet(); |
| IStatus status = engine.perform(plan.getInstallerPlan(), sizingPhaseSet, mon.newChild(100)); |
| if (status.isOK()) |
| installPlanSize = sizingPhaseSet.getDiskSize(); |
| } else { |
| mon.worked(100); |
| } |
| ISizingPhaseSet sizingPhaseSet = PhaseSetFactory.createSizingPhaseSet(); |
| IStatus status = engine.perform(plan, sizingPhaseSet, mon.newChild(200)); |
| if (status.isOK()) |
| return installPlanSize + sizingPhaseSet.getDiskSize(); |
| return SIZE_UNAVAILABLE; |
| } |
| |
| private static int countPlanElements(IProvisioningPlan plan) { |
| return QueryUtil.compoundQueryable(plan.getAdditions(), plan.getRemovals()) |
| .query(QueryUtil.createIUAnyQuery(), null).toUnmodifiableSet().size(); |
| } |
| |
| /** |
| * Return the artifact repository manager for the given session |
| * |
| * @return the repository manager |
| */ |
| public static IArtifactRepositoryManager getArtifactRepositoryManager(ProvisioningSession session) { |
| return session.getProvisioningAgent().getService(IArtifactRepositoryManager.class); |
| } |
| |
| /** |
| * Return the metadata repository manager for the given session |
| * |
| * @return the repository manager |
| */ |
| public static IMetadataRepositoryManager getMetadataRepositoryManager(ProvisioningSession session) { |
| return session.getProvisioningAgent().getService(IMetadataRepositoryManager.class); |
| } |
| |
| /** |
| * Return the profile registry for the given session |
| * |
| * @return the profile registry |
| */ |
| public static IProfileRegistry getProfileRegistry(ProvisioningSession session) { |
| return session.getProvisioningAgent().getService(IProfileRegistry.class); |
| } |
| |
| /** |
| * Return the provisioning engine for the given session |
| * |
| * @return the provisioning engine |
| */ |
| public static IEngine getEngine(ProvisioningSession session) { |
| return session.getProvisioningAgent().getService(IEngine.class); |
| } |
| |
| /** |
| * Return the provisioning event bus used for dispatching events. |
| * |
| * @return the event bus |
| */ |
| public static IProvisioningEventBus getProvisioningEventBus(ProvisioningSession session) { |
| return session.getProvisioningAgent().getService(IProvisioningEventBus.class); |
| } |
| |
| public static IProvisioningPlan toCompabilityWithCurrentJREProvisioningPlan( |
| ProfileChangeOperation referenceOperation, IProgressMonitor monitor) { |
| IInstallableUnit currentJREUnit = createCurrentJavaSEUnit(); |
| IProfileChangeRequest compatibilityWithCurrentRequest = toCurrentJREOperation(referenceOperation, |
| currentJREUnit); |
| IPlanner planner = referenceOperation.getProvisioningPlan().getProfile().getProvisioningAgent() |
| .getService(IPlanner.class); |
| IProvisioningPlan res = planner.getProvisioningPlan(compatibilityWithCurrentRequest, |
| referenceOperation.getProvisioningContext(), monitor); |
| return res; |
| } |
| |
| private static IProfileChangeRequest toCurrentJREOperation(ProfileChangeOperation operation, |
| IInstallableUnit currnetJREUnit) { |
| IProfileChangeRequest initialRequest = operation.getProfileChangeRequest(); |
| if (initialRequest == null) { |
| throw new IllegalStateException("operation plan must be resolved"); //$NON-NLS-1$ |
| } |
| IProfileChangeRequest res = ((ProfileChangeRequest) initialRequest).clone(); |
| res.addExtraRequirements(Collections.singleton(MetadataFactory |
| .createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "a.jre.javase", null, null, 0, 0, false))); //$NON-NLS-1$ |
| operation.getProvisioningPlan().getProfile().query(QueryUtil.createIUQuery("a.jre.javase"), null) //$NON-NLS-1$ |
| .forEach(res::remove); |
| res.add(currnetJREUnit); |
| return res; |
| } |
| |
| private static IInstallableUnit createCurrentJavaSEUnit() { |
| InstallableUnitDescription desc = new InstallableUnitDescription(); |
| desc.setId("currently-running-execution-environement-do-not-actually-install"); //$NON-NLS-1$ |
| Version eeVersion = getCurrentJavaSEVersion(); |
| desc.setVersion(eeVersion); |
| desc.addProvidedCapabilities(Collections |
| .singletonList(new ProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, desc.getId(), eeVersion))); |
| desc.addProvidedCapabilities(parseSystemCapabilities(Constants.FRAMEWORK_SYSTEMCAPABILITIES)); |
| desc.addProvidedCapabilities(parseSystemCapabilities(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA)); |
| desc.addProvidedCapabilities(toJavaPackageCapabilities(Constants.FRAMEWORK_SYSTEMPACKAGES)); |
| desc.addProvidedCapabilities(toJavaPackageCapabilities(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA)); |
| return MetadataFactory.createInstallableUnit(desc); |
| } |
| |
| private static List<IProvidedCapability> toJavaPackageCapabilities(String systemPropertyName) { |
| String packages = System.getProperty(systemPropertyName); |
| if (packages != null && !packages.trim().isEmpty()) { |
| try { |
| return Arrays.stream(ManifestElement.parseHeader(systemPropertyName, packages)) // |
| .map(jrePackage -> { |
| String packageName = jrePackage.getValue(); |
| Version packageVersion = Version.create(jrePackage.getAttribute("version")); //$NON-NLS-1$ |
| return MetadataFactory.createProvidedCapability("java.package", packageName, //$NON-NLS-1$ |
| packageVersion); |
| }).collect(Collectors.toList()); |
| } catch (BundleException e) { |
| ProvUIActivator.getDefault().getLog() |
| .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); |
| } |
| } |
| return Collections.emptyList(); |
| } |
| |
| private static Version getCurrentJavaSEVersion() { |
| String[] segments = System.getProperty("java.version").split("\\."); //$NON-NLS-1$ //$NON-NLS-2$ |
| if ("1".equals(segments[0])) { //$NON-NLS-1$ |
| return Version.create(segments[0] + '.' + segments[1] + ".0"); //$NON-NLS-1$ |
| } |
| return Version.create(segments[0].split("-")[0] + ".0.0"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| static Collection<IProvidedCapability> parseSystemCapabilities(String systemProperty) { |
| String systemCapabilities = System.getProperty(systemProperty); |
| if (systemCapabilities == null || systemCapabilities.trim().isEmpty()) { |
| return Collections.emptyList(); |
| } |
| try { |
| return Arrays.stream(ManifestElement.parseHeader(systemProperty, systemCapabilities)) // |
| .flatMap(eeCapability -> { |
| String eeName = eeCapability.getAttribute("osgi.ee"); //$NON-NLS-1$ |
| if (eeName == null) { |
| return Stream.empty(); |
| } |
| return parseEECapabilityVersion(eeCapability) // |
| .map(version -> MetadataFactory.createProvidedCapability("osgi.ee", eeName, version)); //$NON-NLS-1$ |
| }).collect(Collectors.toList()); |
| } catch (BundleException e) { |
| ProvUIActivator.getDefault().getLog() |
| .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); |
| return Collections.emptyList(); |
| } |
| } |
| |
| private static Stream<Version> parseEECapabilityVersion(ManifestElement eeCapability) { |
| String singleVersion = eeCapability.getAttribute("version:Version"); //$NON-NLS-1$ |
| String[] multipleVersions = ManifestElement |
| .getArrayFromList(eeCapability.getAttribute("version:List<Version>")); //$NON-NLS-1$ |
| |
| if (singleVersion == null && multipleVersions == null) { |
| return Stream.empty(); |
| } else if (singleVersion == null) { |
| return Arrays.stream(multipleVersions).map(Version::parseVersion); |
| } else if (multipleVersions == null) { |
| return Stream.of(singleVersion).map(Version::parseVersion); |
| } |
| return Stream.empty(); |
| } |
| |
| } |