| /******************************************************************************* |
| * Copyright (c) 2009,2011 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 |
| ******************************************************************************/ |
| |
| package org.eclipse.equinox.p2.ui; |
| |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.List; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; |
| import org.eclipse.equinox.internal.p2.ui.*; |
| import org.eclipse.equinox.p2.core.ProvisionException; |
| import org.eclipse.equinox.p2.operations.ProvisioningJob; |
| import org.eclipse.equinox.p2.operations.RepositoryTracker; |
| import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| |
| /** |
| * A job that loads a set of metadata repositories and caches the loaded |
| * repositories. This job can be used when repositories are loaded by a client |
| * who wishes to maintain (and pass along) the in-memory references to the |
| * repositories. For example, repositories can be loaded in the background and |
| * then passed to another component, thus ensuring that the repositories remain |
| * loaded in memory. |
| * |
| * @since 2.0 |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public class LoadMetadataRepositoryJob extends ProvisioningJob { |
| |
| /** |
| * An object representing the family of jobs that load repositories. |
| */ |
| public static final Object LOAD_FAMILY = new Object(); |
| |
| /** |
| * The key that should be used to set a property on a repository load job to |
| * indicate that authentication should be suppressed when loading the |
| * repositories. |
| */ |
| public static final QualifiedName SUPPRESS_AUTHENTICATION_JOB_MARKER = new QualifiedName(ProvUIActivator.PLUGIN_ID, |
| "SUPPRESS_AUTHENTICATION_REQUESTS"); //$NON-NLS-1$ |
| |
| /** |
| * The key that should be used to set a property on a repository load job to |
| * indicate that repository events triggered by this job should be suppressed so |
| * that clients will ignore all events related to the load. |
| */ |
| public static final QualifiedName SUPPRESS_REPOSITORY_EVENTS = new QualifiedName(ProvUIActivator.PLUGIN_ID, |
| "SUPRESS_REPOSITORY_EVENTS"); //$NON-NLS-1$ |
| |
| /** |
| * The key that should be used to set a property on a repository load job to |
| * indicate that a wizard receiving this job needs to schedule it. In some |
| * cases, a load job is finished before invoking a wizard. In other cases, the |
| * job has not yet been scheduled so that listeners can be set up first. |
| */ |
| public static final QualifiedName WIZARD_CLIENT_SHOULD_SCHEDULE = new QualifiedName(ProvUIActivator.PLUGIN_ID, |
| "WIZARD_CLIENT_SHOULD_SCHEDULE"); //$NON-NLS-1$ |
| |
| /** |
| * The key that should be used to set a property on a repository load job to |
| * indicate that load errors should be accumulated into a single status rather |
| * than reported as they occur. |
| */ |
| public static final QualifiedName ACCUMULATE_LOAD_ERRORS = new QualifiedName(ProvUIActivator.PLUGIN_ID, |
| "ACCUMULATE_LOAD_ERRORS"); //$NON-NLS-1$ |
| |
| private List<IMetadataRepository> repoCache = new ArrayList<>(); |
| private RepositoryTracker tracker; |
| private MultiStatus accumulatedStatus; |
| private URI[] locations; |
| private ProvisioningUI ui; |
| |
| /** |
| * Create a job that loads the metadata repositories known by the specified |
| * RepositoryTracker. |
| * |
| * @param ui the ProvisioningUI providing the necessary services |
| */ |
| public LoadMetadataRepositoryJob(ProvisioningUI ui) { |
| super(ProvUIMessages.LoadMetadataRepositoryJob_ContactSitesProgress, ui.getSession()); |
| this.ui = ui; |
| this.tracker = ui.getRepositoryTracker(); |
| this.locations = tracker.getKnownRepositories(ui.getSession()); |
| } |
| |
| @Override |
| public IStatus runModal(IProgressMonitor monitor) { |
| if (locations == null || locations.length == 0) |
| return Status.OK_STATUS; |
| |
| IStatus status; |
| |
| // We batch all the time as a way of distinguishing client-initiated repository |
| // jobs from low level repository manipulation. |
| ui.signalRepositoryOperationStart(); |
| try { |
| status = doLoad(monitor); |
| } finally { |
| ui.signalRepositoryOperationComplete(null, getProperty(SUPPRESS_REPOSITORY_EVENTS) == null); |
| } |
| return status; |
| } |
| |
| private IStatus doLoad(IProgressMonitor monitor) { |
| SubMonitor sub = SubMonitor.convert(monitor, ProvUIMessages.LoadMetadataRepositoryJob_ContactSitesProgress, |
| locations.length * 100); |
| if (sub.isCanceled()) |
| return Status.CANCEL_STATUS; |
| for (URI location : locations) { |
| if (sub.isCanceled()) |
| return Status.CANCEL_STATUS; |
| try { |
| repoCache.add(ProvUI.getMetadataRepositoryManager(ui.getSession()).loadRepository(location, |
| sub.newChild(100))); |
| } catch (ProvisionException e) { |
| handleLoadFailure(e, location); |
| } catch (OperationCanceledException e) { |
| return Status.CANCEL_STATUS; |
| } |
| } |
| return getCurrentStatus(); |
| } |
| |
| private void handleLoadFailure(ProvisionException e, URI location) { |
| if (shouldAccumulateFailures()) { |
| // Some ProvisionExceptions include an empty multi status with a message. |
| // Since empty multi statuses have a severity OK, The platform status handler |
| // doesn't handle |
| // this well. We correct this by recreating a status with error severity |
| // so that the platform status handler does the right thing. |
| IStatus status = e.getStatus(); |
| if (status instanceof MultiStatus && ((MultiStatus) status).getChildren().length == 0) |
| status = new Status(IStatus.ERROR, status.getPlugin(), status.getCode(), status.getMessage(), |
| status.getException()); |
| if (accumulatedStatus == null) { |
| accumulatedStatus = new MultiStatus(ProvUIActivator.PLUGIN_ID, ProvisionException.REPOSITORY_NOT_FOUND, |
| new IStatus[] { status }, ProvUIMessages.LoadMetadataRepositoryJob_SitesMissingError, null); |
| } else { |
| accumulatedStatus.add(status); |
| } |
| ui.getRepositoryTracker().addNotFound(location); |
| // Always log the complete exception so the detailed stack trace is in the log. |
| LogHelper.log(e); |
| } else { |
| tracker.reportLoadFailure(location, e); |
| } |
| } |
| |
| protected boolean shouldAccumulateFailures() { |
| return getProperty(LoadMetadataRepositoryJob.ACCUMULATE_LOAD_ERRORS) != null; |
| } |
| |
| /** |
| * Report the accumulated status for repository load failures. If there has been |
| * no status accumulated, or if the job has been cancelled, do not report |
| * anything. Detailed errors have already been logged. |
| */ |
| public void reportAccumulatedStatus() { |
| IStatus status = getCurrentStatus(); |
| if (status.isOK() || status.getSeverity() == IStatus.CANCEL) |
| return; |
| |
| // If user is unaware of individual sites, nothing to report here. |
| if (!ui.getPolicy().getRepositoriesVisible()) |
| return; |
| StatusManager.getManager().handle(status, StatusManager.SHOW); |
| // Reset the accumulated status so that next time we only report the newly not |
| // found repos. |
| accumulatedStatus = null; |
| } |
| |
| private IStatus getCurrentStatus() { |
| if (accumulatedStatus != null) { |
| // If there is only missing repo to report, use the specific message rather than |
| // the generic. |
| if (accumulatedStatus.getChildren().length == 1) |
| return accumulatedStatus.getChildren()[0]; |
| return accumulatedStatus; |
| } |
| return Status.OK_STATUS; |
| } |
| |
| @Override |
| public boolean belongsTo(Object family) { |
| return family == LOAD_FAMILY; |
| } |
| } |