Skip to content

Commit

Permalink
Improve message if artifact can not be downloaded from any repository
Browse files Browse the repository at this point in the history
There are rare cases where one gets a message that an artifact can not
be downloaded from any repository, this has two pitfalls:

1) You don't get an idea what repositories are actually contacted
2) Its hard to find out who actually requires this artifact

This now enhances an IArtifactRequest by a method to set a "context iu"
why this artifact is requested. If the information is given, it searches
through the available IUs and gives additional information why it is
required and what repositories are queried.
  • Loading branch information
laeubi committed Apr 23, 2024
1 parent e82bbdf commit 229d269
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 25 deletions.
2 changes: 1 addition & 1 deletion bundles/org.eclipse.equinox.p2.engine/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.equinox.p2.engine;singleton:=true
Bundle-Version: 2.10.100.qualifier
Bundle-Version: 2.10.200.qualifier
Bundle-Activator: org.eclipse.equinox.internal.p2.engine.EngineActivator
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,30 @@
*******************************************************************************/
package org.eclipse.equinox.internal.p2.engine;

import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.engine.phases.Collect;
import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.engine.ProvisioningContext;
import org.eclipse.equinox.p2.metadata.*;
import org.eclipse.equinox.p2.metadata.expression.ExpressionUtil;
import org.eclipse.equinox.p2.query.*;
import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
import org.eclipse.equinox.p2.repository.artifact.IArtifactRequest;
import org.eclipse.osgi.util.NLS;

public class DownloadManager {
private ProvisioningContext provContext = null;
ArrayList<IArtifactRequest> requestsToProcess = new ArrayList<>();
private IProvisioningAgent agent = null;

/**
* This Comparator sorts the repositories such that local repositories are first.
* TODO: This is copied from the ProvisioningContext class. Can we combine them?
* See https://bugs.eclipse.org/335153.
* This Comparator sorts the repositories such that local repositories are
* first. TODO: This is copied from the ProvisioningContext class. Can we
* combine them? See https://bugs.eclipse.org/335153.
*/
private static final Comparator<IArtifactRepository> LOCAL_FIRST_COMPARATOR = new Comparator<>() {
private static final String FILE_PROTOCOL = "file"; //$NON-NLS-1$
Expand All @@ -49,15 +53,21 @@ public int compare(IArtifactRepository arg0, IArtifactRepository arg1) {
return 0;
}
};
private Set<IInstallableUnit> ius;

public DownloadManager(ProvisioningContext context, IProvisioningAgent agent) {
this(context, Set.of(), agent);
}

public DownloadManager(ProvisioningContext context, Set<IInstallableUnit> ius, IProvisioningAgent agent) {
provContext = context;
this.ius = ius;
this.agent = agent;
}

/*
* Add the given artifact to the download queue. When it
* is downloaded, put it in the specified location.
* Add the given artifact to the download queue. When it is downloaded, put it
* in the specified location.
*/
public void add(IArtifactRequest toAdd) {
Assert.isNotNull(toAdd);
Expand All @@ -81,7 +91,8 @@ private void filterUnfetched() {
}

/*
* Start the downloads. Return a status message indicating success or failure of the overall operation
* Start the downloads. Return a status message indicating success or failure of
* the overall operation
*/
public IStatus start(IProgressMonitor monitor) {
SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.download_artifact, 1000);
Expand All @@ -94,9 +105,10 @@ public IStatus start(IProgressMonitor monitor) {

IArtifactRepository[] repositories = getArtifactRepositories(subMonitor);
if (repositories.length == 0)
return new Status(IStatus.ERROR, EngineActivator.ID, Messages.download_no_repository, new Exception(Collect.NO_ARTIFACT_REPOSITORIES_AVAILABLE));
return new Status(IStatus.ERROR, EngineActivator.ID, Messages.download_no_repository,
new Exception(Collect.NO_ARTIFACT_REPOSITORIES_AVAILABLE));
fetch(repositories, subMonitor.newChild(500));
return overallStatus(monitor);
return overallStatus(monitor, repositories);
} finally {
subMonitor.done();
}
Expand All @@ -106,13 +118,17 @@ public IStatus start(IProgressMonitor monitor) {
* @return artifact repositories sorted according to LOCAL_FIRST_COMPARATOR
*/
private IArtifactRepository[] getArtifactRepositories(SubMonitor subMonitor) {
IQuery<IArtifactRepository> queryArtifactRepositories = new ExpressionMatchQuery<>(IArtifactRepository.class, ExpressionUtil.TRUE_EXPRESSION);
IQueryable<IArtifactRepository> artifactRepositories = provContext.getArtifactRepositories(subMonitor.newChild(250));
IQueryResult<IArtifactRepository> queryResult = artifactRepositories.query(queryArtifactRepositories, subMonitor.newChild(250));
IQuery<IArtifactRepository> queryArtifactRepositories = new ExpressionMatchQuery<>(IArtifactRepository.class,
ExpressionUtil.TRUE_EXPRESSION);
IQueryable<IArtifactRepository> artifactRepositories = provContext
.getArtifactRepositories(subMonitor.newChild(250));
IQueryResult<IArtifactRepository> queryResult = artifactRepositories.query(queryArtifactRepositories,
subMonitor.newChild(250));
IArtifactRepository[] repositories = queryResult.toArray(IArtifactRepository.class);

// Although we get a sorted list back from the ProvisioningContext above, it
// gets unsorted when we convert the queryable into an array so we must re-sort it.
// gets unsorted when we convert the queryable into an array so we must re-sort
// it.
// See https://bugs.eclipse.org/335153.
Arrays.sort(repositories, LOCAL_FIRST_COMPARATOR);

Expand All @@ -123,9 +139,11 @@ private void fetch(IArtifactRepository[] repositories, IProgressMonitor mon) {
SubMonitor monitor = SubMonitor.convert(mon, requestsToProcess.size());
for (int i = 0; i < repositories.length && !requestsToProcess.isEmpty() && !monitor.isCanceled(); i++) {
IArtifactRequest[] requests = getRequestsForRepository(repositories[i]);
publishDownloadEvent(new CollectEvent(CollectEvent.TYPE_REPOSITORY_START, repositories[i], provContext, requests));
publishDownloadEvent(
new CollectEvent(CollectEvent.TYPE_REPOSITORY_START, repositories[i], provContext, requests));
IStatus dlStatus = repositories[i].getArtifacts(requests, monitor.newChild(requests.length));
publishDownloadEvent(new CollectEvent(CollectEvent.TYPE_REPOSITORY_END, repositories[i], provContext, requests));
publishDownloadEvent(
new CollectEvent(CollectEvent.TYPE_REPOSITORY_END, repositories[i], provContext, requests));
if (dlStatus.getSeverity() == IStatus.CANCEL)
return;
filterUnfetched();
Expand All @@ -148,12 +166,7 @@ private IArtifactRequest[] getRequestsForRepository(IArtifactRepository reposito
return applicable.toArray(new IArtifactRequest[applicable.size()]);
}

// private void notifyFetched() {
// ProvisioningEventBus bus = (ProvisioningEventBus) ServiceHelper.getService(DownloadActivator.context, ProvisioningEventBus.class);
// bus.publishEvent();
// }

private IStatus overallStatus(IProgressMonitor monitor) {
private IStatus overallStatus(IProgressMonitor monitor, IArtifactRepository[] repositories) {
if (monitor != null && monitor.isCanceled())
return Status.CANCEL_STATUS;

Expand All @@ -163,9 +176,51 @@ private IStatus overallStatus(IProgressMonitor monitor) {
MultiStatus result = new MultiStatus(EngineActivator.ID, IStatus.OK, null, null);
for (IArtifactRequest request : requestsToProcess) {
IStatus failed = request.getResult();
if (failed != null && !failed.isOK())
if (failed != null && !failed.isOK()) {
IArtifactKey key = request.getArtifactKey();
IInstallableUnit unit = getUnit(key);
if (unit != null) {
String dependencyPath = computeDependency(unit);
result.add(Status.error(NLS.bind(Messages.DownloadManager_cant_find_artifact,
new Object[] { request.getArtifactKey().toString(), dependencyPath,
Arrays.stream(repositories).map(repo -> repo.getLocation()).filter(Objects::nonNull)
.map(URI::toString).collect(Collectors.joining(System.lineSeparator(),
System.lineSeparator(), "")) }))); //$NON-NLS-1$
continue;
}
result.add(failed);
}
}
return result;
}

private IInstallableUnit getUnit(IArtifactKey artifactKey) {
if (ius != null) {
for (IInstallableUnit unit : ius) {
if (unit.getArtifacts().contains(artifactKey)) {
return unit;
}
}
}
return null;
}

private String computeDependency(IInstallableUnit unit) {
List<String> requiredBy = new ArrayList<>();
requiredBy.add(toIdAndVersion(unit));
if (ius != null) {
for (IInstallableUnit other : ius) {
List<IRequirement> requirement = other.getRequirements().stream().filter(req -> unit.satisfies(req))
.toList();
if (!requirement.isEmpty()) {
requiredBy.add(toIdAndVersion(other));
}
}
}
return requiredBy.stream().collect(Collectors.joining(", ")); //$NON-NLS-1$
}

private static String toIdAndVersion(IInstallableUnit unit) {
return String.format("%s[%s]", unit.getId(), unit.getVersion()); //$NON-NLS-1$
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public class Messages extends NLS {
public static String committing;
public static String download_artifact;
public static String download_no_repository;

public static String DownloadManager_cant_find_artifact;
public static String Engine_Operation_Canceled_By_User;

public static String EngineActivator_0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ CertificateChecker_SignedContentError=Error with signed content.
CertificateChecker_SignedContentIOError=Error reading signed content.
CertificateChecker_UnsignedNotAllowed=Installing unsigned artifacts is not permitted: {0}
CertificateChecker_UnsignedRejected=Unsigned content is rejected. Installation cannot proceed.
DownloadManager_cant_find_artifact=Can't download artifact {0} required by {1} from any of the following repositories: {2}

Phase_Collect_Error=An error occurred while collecting items to be installed
Phase_Configure_Error=An error occurred while configuring the installed items
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ protected IStatus completePhase(IProgressMonitor monitor, IProfile profile, Map<
agent = (IProvisioningAgent) parameters.get(PARM_AGENT);
}

@SuppressWarnings("unchecked")
Set<IInstallableUnit> ius = (Set<IInstallableUnit>) parameters.get(PARM_IUS);
if (Boolean.parseBoolean(context.getProperty(ProvisioningContext.CHECK_AUTHORITIES))) {
@SuppressWarnings("unchecked")
Set<IInstallableUnit> ius = (Set<IInstallableUnit>) parameters.get(PARM_IUS);
IStatus authorityStatus = new AuthorityChecker(agent, context, ius, artifactRequests.stream()
.flatMap(Arrays::stream).map(IArtifactRequest::getArtifactKey).collect(Collectors.toList()),
profile).start(monitor);
Expand All @@ -111,7 +111,7 @@ protected IStatus completePhase(IProgressMonitor monitor, IProfile profile, Map<
}

List<IArtifactRequest> totalArtifactRequests = new ArrayList<>(artifactRequests.size());
DownloadManager dm = new DownloadManager(context, agent);
DownloadManager dm = new DownloadManager(context, ius, agent);
for (IArtifactRequest[] requests : artifactRequests) {
for (IArtifactRequest request : requests) {
dm.add(request);
Expand Down

0 comments on commit 229d269

Please sign in to comment.