blob: 0d7916a6820aaaace189a196c6b17b872b260015 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2018 Red Hat, Inc. 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:
* Red Hat, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.p2.operations;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest;
import org.eclipse.equinox.internal.p2.operations.*;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.planner.IPlanner;
import org.eclipse.equinox.p2.planner.IProfileChangeRequest;
/**
* <p>
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as
* part of a work in progress. There is no guarantee that this API will
* work or that it will remain the same. Please do not use this API without
* consulting with the p2 team.
* </p>
* @noreference
* @since 2.3
*/
public class RemediationOperation extends ProfileChangeOperation {
private static int ZERO_WEIGHT = 0;
private static int LOW_WEIGHT = 1;
private static int MEDIUM_WEIGHT = 2;
private static int HIGH_WEIGHT = 3;
private List<Remedy> remedies;
private Remedy bestSolutionChangingTheRequest;
private Remedy bestSolutionChangingWhatIsInstalled;
private Remedy currentRemedy;
private RemedyConfig[] remedyConfigs;
public RemedyConfig[] getRemedyConfigs() {
return remedyConfigs;
}
public Remedy getCurrentRemedy() {
return currentRemedy;
}
public void setCurrentRemedy(Remedy currentRemedy) {
this.currentRemedy = currentRemedy;
request = currentRemedy == null ? null : currentRemedy.getRequest();
}
private IProfileChangeRequest originalRequest;
public RemediationOperation(ProvisioningSession session, IProfileChangeRequest iProfileChangeRequest) {
this(session, iProfileChangeRequest, RemedyConfig.getAllRemedyConfigs());
}
public RemediationOperation(ProvisioningSession session, IProfileChangeRequest originalRequest, RemedyConfig[] remedyConfigs) {
super(session);
this.originalRequest = originalRequest;
remedies = new ArrayList<>();
this.remedyConfigs = remedyConfigs;
}
public Remedy bestSolutionChangingTheRequest() {
return bestSolutionChangingTheRequest;
}
public Remedy bestSolutionChangingWhatIsInstalled() {
return bestSolutionChangingWhatIsInstalled;
}
public List<Remedy> getRemedies() {
return remedies;
}
@Override
protected void computeProfileChangeRequest(MultiStatus status, IProgressMonitor monitor) {
SubMonitor sub = SubMonitor.convert(monitor, 1);
if (currentRemedy != null) {
request = currentRemedy.getRequest();
sub.worked(1);
return;
}
try {
status.add(computeAllRemediations(sub.newChild(1)));
} catch (OperationCanceledException e) {
status.add(Status.CANCEL_STATUS);
}
determineBestSolutions();
}
private IStatus computeAllRemediations(IProgressMonitor monitor) {
SubMonitor sub = SubMonitor.convert(monitor, remedyConfigs.length);
sub.setTaskName(Messages.RemediationOperation_ProfileChangeRequestProgress);
List<Remedy> tmpRemedies = new ArrayList<>(remedyConfigs.length);
try {
for (int i = 0; i < remedyConfigs.length; i++) {
sub.subTask((i + 1) + " / " + remedyConfigs.length); //$NON-NLS-1$
if (sub.isCanceled())
return Status.CANCEL_STATUS;
Remedy remedy = computeRemedy(remedyConfigs[i], sub.newChild(1, SubMonitor.SUPPRESS_ALL_LABELS));
if (remedy != null) {
tmpRemedies.add(remedy);
}
}
} finally {
sub.done();
}
remedies = tmpRemedies;
return getResolutionResult();
}
private void determineBestSolutions() {
int beingInstalledWeight = 0;
int installationWeight = 0;
for (Remedy remedy : remedies) {
if (remedy.getRequest() != null) {
if (remedy.getBeingInstalledRelaxedWeight() > beingInstalledWeight && remedy.getInstallationRelaxedWeight() == 0) {
bestSolutionChangingTheRequest = remedy;
beingInstalledWeight = remedy.getBeingInstalledRelaxedWeight();
continue;
}
if (remedy.getInstallationRelaxedWeight() > installationWeight && remedy.getBeingInstalledRelaxedWeight() == 0) {
bestSolutionChangingWhatIsInstalled = remedy;
installationWeight = remedy.getInstallationRelaxedWeight();
continue;
}
}
}
}
private Remedy computeRemedy(RemedyConfig configuration, IProgressMonitor monitor) {
Remedy remedy = new Remedy(originalRequest);
remedy.setConfig(configuration);
IPlanner planner = session.getPlanner();
RequestFlexer av = new RequestFlexer(planner);
av.setAllowDifferentVersion(configuration.allowDifferentVersion);
av.setAllowInstalledElementChange(configuration.allowInstalledUpdate);
av.setAllowInstalledElementRemoval(configuration.allowInstalledRemoval);
av.setAllowPartialInstall(configuration.allowPartialInstall);
av.setProvisioningContext(getProvisioningContext());
remedy.setRequest((ProfileChangeRequest) av.getChangeRequest(originalRequest, ((ProfileChangeRequest) originalRequest).getProfile(), monitor));
if (remedy.getRequest() == null)
return null;
if (configuration.allowInstalledUpdate && !configuration.allowInstalledRemoval) {
remedy.setInstallationRelaxedWeight(HIGH_WEIGHT);
} else if (!configuration.allowInstalledUpdate && configuration.allowInstalledRemoval) {
remedy.setInstallationRelaxedWeight(MEDIUM_WEIGHT);
} else if (configuration.allowInstalledUpdate && configuration.allowInstalledRemoval) {
remedy.setInstallationRelaxedWeight(LOW_WEIGHT);
} else
remedy.setInstallationRelaxedWeight(ZERO_WEIGHT);
if (configuration.allowDifferentVersion && !configuration.allowPartialInstall) {
remedy.setBeingInstalledRelaxedWeight(HIGH_WEIGHT);
} else if (!configuration.allowDifferentVersion && configuration.allowPartialInstall) {
remedy.setBeingInstalledRelaxedWeight(MEDIUM_WEIGHT);
} else if (configuration.allowDifferentVersion && configuration.allowPartialInstall) {
remedy.setBeingInstalledRelaxedWeight(LOW_WEIGHT);
} else {
remedy.setBeingInstalledRelaxedWeight(ZERO_WEIGHT);
}
computeRemedyDetails(remedy);
return remedy;
}
@Override
protected String getResolveJobName() {
return Messages.RemediationOperation_ResolveJobName;
}
@Override
protected String getProvisioningJobName() {
return Messages.RemediationOperation_RemediationJobName;
}
@Override
public ProvisioningJob getProvisioningJob(IProgressMonitor monitor) {
IStatus status = getResolutionResult();
if (status.getSeverity() != IStatus.CANCEL && status.getSeverity() != IStatus.ERROR) {
if (job.getProvisioningPlan() != null) {
ProfileModificationJob pJob = new ProfileModificationJob(getProvisioningJobName(), session, profileId, job.getProvisioningPlan(), job.getActualProvisioningContext());
pJob.setAdditionalProgressMonitor(monitor);
return pJob;
}
}
return null;
}
public ProfileChangeRequest getOriginalRequest() {
return (ProfileChangeRequest) originalRequest;
}
@Override
void makeResolveJob(final IProgressMonitor monitor) {
// throw away any previous requests
request = null;
noChangeRequest = PlanAnalyzer.getProfileChangeAlteredStatus();
// the requestHolder is a hack to work around the fact that there is no public API
// for the resolution job to get the request from the operation after it has been
// computed.
final ProfileChangeRequest[] requestHolder = new ProfileChangeRequest[1];
job = new RemediationResolutionJob(getResolveJobName(), session, profileId, request, getFirstPassProvisioningContext(), getSecondPassEvaluator(), noChangeRequest, mon -> {
SubMonitor sub = SubMonitor.convert(mon, 2);
// We only check for other jobs running if this job is *not* scheduled
if (job.getState() == Job.NONE && session.hasScheduledOperationsFor(profileId)) {
noChangeRequest.add(PlanAnalyzer.getStatus(IStatusCodes.OPERATION_ALREADY_IN_PROGRESS, null));
} else {
sub.worked(1);
computeProfileChangeRequest(noChangeRequest, sub.newChild(1));
requestHolder[0] = RemediationOperation.this.request;
}
}, requestHolder, this);
}
@Override
public IStatus getResolutionResult() {
if (currentRemedy != null)
return super.getResolutionResult();
return remedies.size() > 0 ? Status.OK_STATUS : new Status(IStatus.ERROR, Constants.BUNDLE_ID, Messages.RemediationOperation_NoRemedyFound);
}
private void computeRemedyDetails(Remedy remedy) {
ArrayList<String> updateIds = new ArrayList<>();
for (IInstallableUnit addedIU : remedy.getRequest().getAdditions()) {
for (IInstallableUnit removedIU : remedy.getRequest().getRemovals()) {
if (removedIU.getId().equals(addedIU.getId())) {
createModificationRemedyDetail(addedIU, removedIU, remedy);
updateIds.add(addedIU.getId());
break;
}
}
if (!updateIds.contains(addedIU.getId())) {
createAdditionRemedyDetail(addedIU, remedy);
}
}
for (IInstallableUnit removedIU : remedy.getRequest().getRemovals()) {
if (!updateIds.contains(removedIU.getId())) {
createRemovalRemedyDetail(removedIU, remedy);
}
}
for (IInstallableUnit addedIUinOriginalRequest : originalRequest.getAdditions()) {
boolean found = false;
for (IInstallableUnit addedIU : remedy.getRequest().getAdditions()) {
if (addedIU.getId().equals(addedIUinOriginalRequest.getId())) {
found = true;
break;
}
}
if (!found) {
createNotAddedRemedyDetail(addedIUinOriginalRequest, remedy);
found = false;
}
}
}
private void createNotAddedRemedyDetail(IInstallableUnit iu, Remedy remedy) {
RemedyIUDetail iuDetail = new RemedyIUDetail(iu);
iuDetail.setStatus(RemedyIUDetail.STATUS_NOT_ADDED);
iuDetail.setRequestedVersion(iu.getVersion());
remedy.addRemedyIUDetail(iuDetail);
}
private void createRemovalRemedyDetail(IInstallableUnit iu, Remedy remedy) {
RemedyIUDetail iuDetail = new RemedyIUDetail(iu);
iuDetail.setStatus(RemedyIUDetail.STATUS_REMOVED);
iuDetail.setInstalledVersion(iu.getVersion());
remedy.addRemedyIUDetail(iuDetail);
}
private void createAdditionRemedyDetail(IInstallableUnit iu, Remedy remedy) {
RemedyIUDetail iuDetail = new RemedyIUDetail(iu);
iuDetail.setStatus(RemedyIUDetail.STATUS_ADDED);
iuDetail.setBeingInstalledVersion(iu.getVersion());
iuDetail.setRequestedVersion(searchInOriginalRequest(iu.getId()));
remedy.addRemedyIUDetail(iuDetail);
}
private void createModificationRemedyDetail(IInstallableUnit beingInstalledIU, IInstallableUnit installedIU, Remedy remedy) {
RemedyIUDetail iuDetail = new RemedyIUDetail(beingInstalledIU);
iuDetail.setStatus(RemedyIUDetail.STATUS_CHANGED);
iuDetail.setBeingInstalledVersion(beingInstalledIU.getVersion());
iuDetail.setInstalledVersion(installedIU.getVersion());
iuDetail.setRequestedVersion(searchInOriginalRequest(beingInstalledIU.getId()));
remedy.addRemedyIUDetail(iuDetail);
}
private Version searchInOriginalRequest(String id) {
for (IInstallableUnit iu : originalRequest.getAdditions()) {
if (iu.getId() == id)
return iu.getVersion();
}
return null;
}
}