blob: e3b540830ed701daaa370121310efc39f52b18f0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2015 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.pde.internal.ui.build;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.IProvidedCapability;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
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.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitPatchDescription;
import org.eclipse.equinox.p2.operations.*;
import org.eclipse.equinox.p2.query.IQueryResult;
import org.eclipse.equinox.p2.query.QueryUtil;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
import org.eclipse.equinox.p2.ui.ProvisioningUI;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.internal.build.site.QualifierReplacer;
import org.eclipse.pde.internal.core.exports.FeatureExportInfo;
import org.eclipse.pde.internal.core.ifeature.IFeatureModel;
import org.eclipse.pde.internal.ui.PDEPlugin;
import org.eclipse.pde.internal.ui.PDEUIMessages;
/**
* This job attempts to install a set of exported plug-ins or
* features into the current runtime.
*/
public class RuntimeInstallJob extends Job {
private FeatureExportInfo fInfo;
private ProvisioningUI ui;
/**
* Creates a new job that will install exported plug-ins. For a
* successful install, specific option in the feature export info
* object need to be set before the export operation see
* {@link #modifyInfoForInstall(FeatureExportInfo)}
*
* @param jobName the name to use for this job
* @param info the info object describing what is being exported
*/
public RuntimeInstallJob(String jobName, FeatureExportInfo info) {
super(jobName);
fInfo = info;
// This provisioning UI manages the currently running profile.
ui = ProvisioningUI.getDefaultUI();
ui.manageJob(this, ProvisioningJob.RESTART_OR_APPLY);
}
/**
* Sets the export options required to make the export installable.
* This method should be called before the export operation takes
* place.
*
* @param info the feature info object that will be modified
*/
public static void modifyInfoForInstall(FeatureExportInfo info) {
info.exportSource = false;
info.useJarFormat = true;
info.exportMetadata = true;
info.qualifier = QualifierReplacer.getDateQualifier();
}
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
ProvisioningSession session = ui.getSession();
monitor.beginTask(PDEUIMessages.RuntimeInstallJob_Job_name_installing, 12 + (2 * fInfo.items.length));
// p2 needs to know about the generated repos
URI destination = new File(fInfo.destinationDirectory).toURI();
ui.loadArtifactRepository(destination, false, new SubProgressMonitor(monitor, 1));
IMetadataRepository metaRepo = ui.loadMetadataRepository(destination, false, new SubProgressMonitor(monitor, 1));
IProfileRegistry profileRegistry = (IProfileRegistry) session.getProvisioningAgent().getService(IProfileRegistry.SERVICE_NAME);
if (profileRegistry == null) {
return new Status(IStatus.ERROR, PDEPlugin.getPluginId(), PDEUIMessages.RuntimeInstallJob_ErrorCouldntOpenProfile);
}
IProfile profile = profileRegistry.getProfile(IProfileRegistry.SELF);
if (profile == null) {
return new Status(IStatus.ERROR, PDEPlugin.getPluginId(), PDEUIMessages.RuntimeInstallJob_ErrorCouldntOpenProfile);
}
List<IInstallableUnit> toInstall = new ArrayList<>();
for (int i = 0; i < fInfo.items.length; i++) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
monitor.subTask(NLS.bind(PDEUIMessages.RuntimeInstallJob_Creating_installable_unit, fInfo.items[i].toString()));
//Get the installable unit from the repo
String id = null;
String version = null;
if (fInfo.items[i] instanceof IPluginModelBase) {
id = ((IPluginModelBase) fInfo.items[i]).getPluginBase().getId();
version = ((IPluginModelBase) fInfo.items[i]).getPluginBase().getVersion();
} else if (fInfo.items[i] instanceof IFeatureModel) {
id = ((IFeatureModel) fInfo.items[i]).getFeature().getId() + ".feature.group"; //$NON-NLS-1$
version = ((IFeatureModel) fInfo.items[i]).getFeature().getVersion();
}
if (id == null && version == null) {
return new Status(IStatus.ERROR, PDEPlugin.getPluginId(), NLS.bind(PDEUIMessages.RuntimeInstallJob_ErrorCouldNotGetIdOrVersion, fInfo.items[i].toString()));
}
// Use the same qualifier replacement as the export operation used
version = QualifierReplacer.replaceQualifierInVersion(version, id, null, null);
// Check if the right version exists in the new meta repo
Version newVersion = Version.parseVersion(version);
IQueryResult<?> queryMatches = metaRepo.query(QueryUtil.createIUQuery(id, newVersion), monitor);
if (queryMatches.isEmpty()) {
return new Status(IStatus.ERROR, PDEPlugin.getPluginId(), NLS.bind(PDEUIMessages.RuntimeInstallJob_ErrorCouldNotFindUnitInRepo, new String[] {id, version}));
}
IInstallableUnit iuToInstall = (IInstallableUnit) queryMatches.iterator().next();
// Find out if the profile already has that iu installed
queryMatches = profile.query(QueryUtil.createIUQuery(id), new SubProgressMonitor(monitor, 0));
if (queryMatches.isEmpty()) {
// Just install the new iu into the profile
toInstall.add(iuToInstall);
} else {
// There is an existing iu that we need to replace using an installable unit patch
IInstallableUnit existingIU = (IInstallableUnit) queryMatches.iterator().next();
toInstall.add(createInstallableUnitPatch(existingIU, newVersion, profile, monitor));
}
monitor.worked(2);
}
if (toInstall.size() > 0) {
InstallOperation operation = ui.getInstallOperation(toInstall, new URI[] {destination});
operation.resolveModal(new SubProgressMonitor(monitor, 5));
IStatus status = operation.getResolutionResult();
if (status.getSeverity() == IStatus.CANCEL || !(status.isOK() || status.getSeverity() == IStatus.INFO)) {
return status;
}
ProvisioningJob job = operation.getProvisioningJob(null);
status = job.runModal(new SubProgressMonitor(monitor, 5));
return status;
}
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
return Status.OK_STATUS;
} catch (ProvisionException e) {
return e.getStatus();
} finally {
monitor.done();
}
}
/**
* Creates an installable unit patch that will change the version of
* existing requirements with the given version.
*
* @param existingIU an existing plug-in that this patch will replace, used to generate lifecycle
* @param newVersion the new version to require
* @param profile the profile we are installing in
* @param monitor progress monitor
* @return an installable unit patch
*/
private IInstallableUnitPatch createInstallableUnitPatch(IInstallableUnit existingIU, Version newVersion, IProfile profile, IProgressMonitor monitor) {
InstallableUnitPatchDescription iuPatchDescription = new MetadataFactory.InstallableUnitPatchDescription();
String id = existingIU.getId();
iuPatchDescription.setId(id + ".patch"); //$NON-NLS-1$
iuPatchDescription.setProperty(IInstallableUnit.PROP_NAME, NLS.bind(PDEUIMessages.RuntimeInstallJob_installPatchName, id));
iuPatchDescription.setProperty(IInstallableUnit.PROP_DESCRIPTION, PDEUIMessages.RuntimeInstallJob_installPatchDescription);
Version patchVersion = Version.createOSGi(1, 0, 0, QualifierReplacer.getDateQualifier());
iuPatchDescription.setVersion(patchVersion);
iuPatchDescription.setUpdateDescriptor(MetadataFactory.createUpdateDescriptor(iuPatchDescription.getId(), new VersionRange(Version.createOSGi(0, 0, 0), true, patchVersion, false), 0, null));
ArrayList<IProvidedCapability> list = new ArrayList<>(1);
list.add(MetadataFactory.createProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, iuPatchDescription.getId(), iuPatchDescription.getVersion()));
iuPatchDescription.addProvidedCapabilities(list);
IRequirement applyTo = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, id, null, null, false, false);
IRequirement newValue = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, id, new VersionRange(newVersion, true, newVersion, true), null, false, false);
iuPatchDescription.setRequirementChanges(new IRequirementChange[] {MetadataFactory.createRequirementChange(applyTo, newValue)});
iuPatchDescription.setApplicabilityScope(new IRequirement[0][0]);
// Locate IU's that appoint the existing version of the IU that we are patching.
// Add lifecycle requirement on a changed bundle, if it gets updated, then we should uninstall the patch
IQueryResult<?> queryMatches = profile.query(QueryUtil.createMatchQuery("requirements.exists(rc | $0 ~= rc)", new Object[] {existingIU}), monitor); //$NON-NLS-1$
if (!queryMatches.isEmpty()) {
IInstallableUnit lifecycleUnit = (IInstallableUnit) queryMatches.iterator().next();
iuPatchDescription.setLifeCycle(MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, lifecycleUnit.getId(), new VersionRange(lifecycleUnit.getVersion(), true, lifecycleUnit.getVersion(), true), null, false, false, false));
}
iuPatchDescription.setProperty(InstallableUnitDescription.PROP_TYPE_PATCH, Boolean.TRUE.toString());
return MetadataFactory.createInstallableUnitPatch(iuPatchDescription);
}
}