| /******************************************************************************* |
| * Copyright (c) 2007, 2017 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 |
| * Code 9 - ongoing development |
| * Sonatype, Inc. - ongoing development |
| * Red Hat Inc. - Bug 460967 |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.installer; |
| |
| import java.net.URI; |
| import java.util.*; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper; |
| import org.eclipse.equinox.internal.provisional.p2.director.IDirector; |
| import org.eclipse.equinox.internal.provisional.p2.installer.IInstallOperation; |
| import org.eclipse.equinox.internal.provisional.p2.installer.InstallDescription; |
| import org.eclipse.equinox.p2.core.IProvisioningAgent; |
| import org.eclipse.equinox.p2.core.ProvisionException; |
| import org.eclipse.equinox.p2.engine.IProfile; |
| import org.eclipse.equinox.p2.engine.IProfileRegistry; |
| import org.eclipse.equinox.p2.metadata.*; |
| import org.eclipse.equinox.p2.planner.IPlanner; |
| import org.eclipse.equinox.p2.planner.IProfileChangeRequest; |
| import org.eclipse.equinox.p2.query.*; |
| import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; |
| import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; |
| import org.eclipse.osgi.service.environment.EnvironmentInfo; |
| import org.eclipse.osgi.util.NLS; |
| |
| /** |
| * This operation performs installation or update of an Eclipse-based product. |
| */ |
| public class InstallUpdateProductOperation implements IInstallOperation { |
| |
| private IArtifactRepositoryManager artifactRepoMan; |
| private IProvisioningAgent agent; |
| private IDirector director; |
| private final InstallDescription installDescription; |
| private boolean isInstall = true; |
| private IMetadataRepositoryManager metadataRepoMan; |
| private IProfileRegistry profileRegistry; |
| private IStatus result; |
| |
| public InstallUpdateProductOperation(IProvisioningAgent agent, InstallDescription description) { |
| this.agent = agent; |
| this.installDescription = description; |
| } |
| |
| /** |
| * Determine what top level installable units should be installed by the |
| * director |
| */ |
| private Collection<IInstallableUnit> computeUnitsToInstall() throws CoreException { |
| ArrayList<IInstallableUnit> units = new ArrayList<>(); |
| IVersionedId roots[] = installDescription.getRoots(); |
| for (IVersionedId root : roots) { |
| IInstallableUnit iu = findUnit(root); |
| if (iu != null) |
| units.add(iu); |
| } |
| return units; |
| } |
| |
| /** |
| * This profile is being updated; return the units to uninstall from the |
| * profile. |
| */ |
| private IQueryResult<IInstallableUnit> computeUnitsToUninstall(IProfile p) { |
| return p.query(QueryUtil.createIUAnyQuery(), null); |
| } |
| |
| /** |
| * Create and return the profile into which units will be installed. |
| */ |
| private IProfile createProfile() throws ProvisionException { |
| IProfile profile = getProfile(); |
| if (profile == null) { |
| Map<String, String> properties = new HashMap<>(); |
| properties.put(IProfile.PROP_INSTALL_FOLDER, installDescription.getInstallLocation().toString()); |
| EnvironmentInfo info = ServiceHelper.getService(InstallerActivator.getDefault().getContext(), |
| EnvironmentInfo.class); |
| String env = "osgi.os=" + info.getOS() + ",osgi.ws=" + info.getWS() + ",osgi.arch=" + info.getOSArch(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| properties.put(IProfile.PROP_ENVIRONMENTS, env); |
| properties.put(IProfile.PROP_NAME, installDescription.getProductName()); |
| properties.putAll(installDescription.getProfileProperties()); |
| IPath location = installDescription.getBundleLocation(); |
| if (location != null) |
| properties.put(IProfile.PROP_CACHE, location.toOSString()); |
| profile = profileRegistry.addProfile(getProfileId(), properties); |
| } |
| return profile; |
| } |
| |
| IPlanner getPlanner() { |
| return agent.getService(IPlanner.class); |
| } |
| |
| /** |
| * Performs the actual product install or update. |
| */ |
| private void doInstall(SubMonitor monitor) throws CoreException { |
| prepareMetadataRepositories(); |
| prepareArtifactRepositories(); |
| IProfile p = createProfile(); |
| Collection<IInstallableUnit> toInstall = computeUnitsToInstall(); |
| monitor.worked(5); |
| |
| IStatus s; |
| IProfileChangeRequest request = getPlanner().createChangeRequest(p); |
| if (isInstall) { |
| monitor.setTaskName(NLS.bind(Messages.Op_Installing, installDescription.getProductName())); |
| request.addAll(toInstall); |
| s = director.provision(request, null, monitor.newChild(90)); |
| } else { |
| monitor.setTaskName(NLS.bind(Messages.Op_Updating, installDescription.getProductName())); |
| IQueryResult<IInstallableUnit> toUninstall = computeUnitsToUninstall(p); |
| request.removeAll(toUninstall.toUnmodifiableSet()); |
| request.addAll(toInstall); |
| s = director.provision(request, null, monitor.newChild(90)); |
| } |
| if (!s.isOK()) |
| throw new CoreException(s); |
| } |
| |
| /** |
| * Returns an exception of severity error with the given error message. |
| */ |
| private CoreException fail(String message) { |
| return fail(message, null); |
| } |
| |
| /** |
| * Returns an exception of severity error with the given error message. |
| */ |
| private CoreException fail(String message, Throwable throwable) { |
| return new CoreException(new Status(IStatus.ERROR, InstallerActivator.PI_INSTALLER, message, throwable)); |
| } |
| |
| /** |
| * Finds and returns the installable unit with the given id, and optionally the |
| * given version. |
| */ |
| private IInstallableUnit findUnit(IVersionedId spec) throws CoreException { |
| String id = spec.getId(); |
| if (id == null) |
| throw fail(Messages.Op_NoId); |
| Version version = spec.getVersion(); |
| VersionRange range = VersionRange.emptyRange; |
| if (version != null && !version.equals(Version.emptyVersion)) |
| range = new VersionRange(version, true, version, true); |
| IQuery<IInstallableUnit> query = QueryUtil.createIUQuery(id, range); |
| Iterator<IInstallableUnit> matches = metadataRepoMan.query(query, null).iterator(); |
| // pick the newest match |
| IInstallableUnit newest = null; |
| while (matches.hasNext()) { |
| IInstallableUnit candidate = matches.next(); |
| if (newest == null || (newest.getVersion().compareTo(candidate.getVersion()) < 0)) |
| newest = candidate; |
| } |
| if (newest == null) |
| throw fail(Messages.Op_IUNotFound + id); |
| return newest; |
| } |
| |
| /** |
| * Returns the profile being installed into. |
| */ |
| private IProfile getProfile() { |
| return profileRegistry.getProfile(getProfileId()); |
| } |
| |
| /** |
| * Returns the id of the profile to use for install/update based on this |
| * operation's install description. |
| */ |
| private String getProfileId() { |
| IPath location = installDescription.getInstallLocation(); |
| if (location != null) |
| return location.toString(); |
| return installDescription.getProductName(); |
| } |
| |
| /** |
| * Returns the result of the install operation, or <code>null</code> if no |
| * install operation has been run. |
| */ |
| public IStatus getResult() { |
| return result; |
| } |
| |
| private <T> T getService(Class<T> name) throws CoreException { |
| T service = agent.getService(name); |
| if (service == null) |
| throw fail(Messages.Op_NoServiceImpl + name); |
| return service; |
| } |
| |
| @Override |
| public IStatus install(IProgressMonitor pm) { |
| SubMonitor monitor = SubMonitor.convert(pm, Messages.Op_Preparing, 100); |
| try { |
| try { |
| preInstall(); |
| isInstall = getProfile() == null; |
| doInstall(monitor); |
| result = new Status(IStatus.OK, InstallerActivator.PI_INSTALLER, |
| isInstall ? Messages.Op_InstallComplete : Messages.Op_UpdateComplete, null); |
| monitor.setTaskName(Messages.Op_Cleanup); |
| } finally { |
| postInstall(); |
| } |
| } catch (CoreException e) { |
| result = e.getStatus(); |
| } finally { |
| monitor.done(); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns whether this operation represents the product being installed for the |
| * first time, in a new profile. |
| */ |
| public boolean isFirstInstall() { |
| return isInstall; |
| } |
| |
| private void postInstall() { |
| // nothing to do |
| } |
| |
| private void preInstall() throws CoreException { |
| // obtain required services |
| director = getService(IDirector.class); |
| metadataRepoMan = getService(IMetadataRepositoryManager.class); |
| artifactRepoMan = getService(IArtifactRepositoryManager.class); |
| profileRegistry = getService(IProfileRegistry.class); |
| } |
| |
| private void prepareArtifactRepositories() throws ProvisionException { |
| URI[] repos = installDescription.getArtifactRepositories(); |
| if (repos == null) |
| return; |
| |
| // Repositories must be registered before they are loaded |
| // This is to avoid them being possibly overridden with the configuration as a |
| // referenced repository |
| for (URI repo : repos) { |
| artifactRepoMan.addRepository(repo); |
| artifactRepoMan.loadRepository(repo, null); |
| } |
| } |
| |
| private void prepareMetadataRepositories() throws ProvisionException { |
| URI[] repos = installDescription.getMetadataRepositories(); |
| if (repos == null) |
| return; |
| |
| // Repositories must be registered before they are loaded |
| // This is to avoid them being possibly overridden with the configuration as a |
| // referenced repository |
| for (URI repo : repos) { |
| metadataRepoMan.addRepository(repo); |
| metadataRepoMan.loadRepository(repo, null); |
| } |
| } |
| } |