| /******************************************************************************* |
| * Copyright (c) 2011 Sonatype, Inc. |
| * All rights reserved. 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: |
| * Sonatype, Inc. - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.m2e.internal.discovery.operation; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.equinox.internal.p2.discovery.model.CatalogItem; |
| import org.eclipse.equinox.p2.core.ProvisionException; |
| import org.eclipse.equinox.p2.metadata.IInstallableUnit; |
| import org.eclipse.equinox.p2.metadata.IVersionedId; |
| import org.eclipse.equinox.p2.metadata.VersionedId; |
| import org.eclipse.equinox.p2.operations.ProvisioningJob; |
| import org.eclipse.equinox.p2.operations.ProvisioningSession; |
| import org.eclipse.equinox.p2.operations.RepositoryTracker; |
| 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.repository.metadata.IMetadataRepositoryManager; |
| import org.eclipse.equinox.p2.ui.ProvisioningUI; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.osgi.util.NLS; |
| |
| import org.eclipse.m2e.internal.discovery.DiscoveryActivator; |
| import org.eclipse.m2e.internal.discovery.MavenDiscovery; |
| import org.eclipse.m2e.internal.discovery.Messages; |
| |
| |
| /* |
| * This class allows us to open MavenDiscoveryInstallWizard instead of the default p2 wizard |
| * to support changing the restart policy for the subsequent ProvisioningJob. |
| */ |
| @SuppressWarnings("restriction") |
| public class MavenDiscoveryInstallOperation implements IRunnableWithProgress { |
| private Collection<CatalogItem> installableConnectors; |
| |
| private ProvisioningSession session; |
| |
| private Set<URI> repositoryLocations; |
| |
| private final boolean restart; |
| |
| private List<IStatus> statuses = new ArrayList<IStatus>(); |
| |
| private RestartInstallOperation operation; |
| |
| private final IRunnableWithProgress postInstallHook; |
| |
| private Collection<String> projectsToConfigure; |
| |
| private boolean shouldResolve; |
| |
| public MavenDiscoveryInstallOperation(Collection<CatalogItem> installableConnectors, |
| IRunnableWithProgress postInstallHook, boolean restart) { |
| this(installableConnectors, postInstallHook, restart, true, null); |
| } |
| |
| public MavenDiscoveryInstallOperation(Collection<CatalogItem> installableConnectors, |
| IRunnableWithProgress postInstallHook, boolean restart, boolean shouldResolve, |
| Collection<String> projectsToConfigure) { |
| this.installableConnectors = installableConnectors; |
| this.postInstallHook = postInstallHook; |
| this.restart = restart; |
| this.session = ProvisioningUI.getDefaultUI().getSession(); |
| this.shouldResolve = shouldResolve; |
| this.projectsToConfigure = projectsToConfigure; |
| } |
| |
| public void run(IProgressMonitor progressMonitor) throws InvocationTargetException, InterruptedException { |
| try { |
| SubMonitor monitor = SubMonitor |
| .convert(progressMonitor, Messages.MavenDiscoveryInstallOperation_Configuring, 100); |
| try { |
| final IInstallableUnit[] ius = computeInstallableUnits(monitor.newChild(50)); |
| |
| checkCancelled(monitor); |
| |
| operation = createAndResolve(monitor.newChild(50), ius, new URI[0], |
| restart && MavenDiscovery.requireRestart(installableConnectors)); |
| |
| checkCancelled(monitor); |
| } finally { |
| monitor.done(); |
| } |
| } catch(OperationCanceledException e) { |
| throw new InterruptedException(); |
| } catch(Exception e) { |
| throw new InvocationTargetException(e); |
| } |
| } |
| |
| /* |
| * Should only be called after a successful call to run |
| */ |
| public RestartInstallOperation getOperation() { |
| return operation; |
| } |
| |
| /* |
| * Compute the InstallableUnits & IMetadataRepository |
| */ |
| public IInstallableUnit[] computeInstallableUnits(IProgressMonitor progressMonitor) throws CoreException { |
| SubMonitor monitor = SubMonitor.convert(progressMonitor); |
| try { |
| |
| List<IMetadataRepository> repositories = addRepositories(monitor.newChild(50)); |
| final Collection<IInstallableUnit> installableUnits = queryInstallableUnits(monitor.newChild(50), repositories); |
| |
| if(!statuses.isEmpty()) { |
| throw new CoreException(new MultiStatus(DiscoveryActivator.PLUGIN_ID, 0, statuses.toArray(new IStatus[statuses |
| .size()]), Messages.MavenDiscoveryInstallOperation_ErrorMessage, null)); |
| } |
| return installableUnits.toArray(new IInstallableUnit[installableUnits.size()]); |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| /* |
| * Get IUs to install from the specified repository |
| */ |
| private Collection<IInstallableUnit> queryInstallableUnits(IProgressMonitor progressMonitor, |
| List<IMetadataRepository> repositories) { |
| final Set<IInstallableUnit> installableUnits = new HashSet<IInstallableUnit>(installableConnectors.size()); |
| |
| SubMonitor monitor = SubMonitor.convert(progressMonitor, installableConnectors.size()); |
| try { |
| for(CatalogItem item : installableConnectors) { |
| SubMonitor subMon = monitor.newChild(1); |
| checkCancelled(monitor); |
| URI address = URI.create(item.getSiteUrl()); |
| // get repository |
| IMetadataRepository repository = null; |
| for(IMetadataRepository candidate : repositories) { |
| if(address.equals(candidate.getLocation())) { |
| repository = candidate; |
| break; |
| } |
| } |
| if(repository == null) { |
| statuses.add(new Status(IStatus.ERROR, DiscoveryActivator.PLUGIN_ID, NLS.bind( |
| Messages.MavenDiscoveryInstallOperation_missingRepository, item.getName(), item.getSiteUrl()))); |
| // Continue so we gather all the problems before telling the user |
| continue; |
| } |
| // get IUs |
| checkCancelled(monitor); |
| |
| Set<IVersionedId> ids = getDescriptorIds(repository); |
| for(IVersionedId versionedId : ids) { |
| IQueryResult<IInstallableUnit> result = repository.query(QueryUtil.createIUQuery(versionedId), |
| subMon.newChild(1)); |
| Set<IInstallableUnit> matches = result.toSet(); |
| if(matches.size() == 1) { |
| installableUnits.addAll(matches); |
| } else if(matches.size() == 0) { |
| statuses.add(new Status(IStatus.ERROR, DiscoveryActivator.PLUGIN_ID, NLS.bind( |
| Messages.MavenDiscoveryInstallOperation_missingIU, item.getName(), versionedId.toString()))); |
| } else { |
| // Choose the highest available version |
| IInstallableUnit match = null; |
| for(IInstallableUnit iu : matches) { |
| if(match == null || iu.getVersion().compareTo(match.getVersion()) > 0) { |
| match = iu; |
| } |
| } |
| if(match != null) { |
| installableUnits.add(match); |
| } |
| } |
| } |
| } |
| return installableUnits; |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| /* |
| * Get the IVersionedId expected to be in the repository |
| */ |
| protected Set<IVersionedId> getDescriptorIds(IMetadataRepository repository) { |
| Set<IVersionedId> ids = new HashSet<IVersionedId>(); |
| for(CatalogItem item : installableConnectors) { |
| if(repository.getLocation().equals(URI.create(item.getSiteUrl()))) { |
| for(String id : item.getInstallableUnits()) { |
| ids.add(VersionedId.parse(id)); |
| } |
| } |
| } |
| return ids; |
| } |
| |
| /* |
| * Add the necessary repositories |
| */ |
| protected List<IMetadataRepository> addRepositories(SubMonitor monitor) { |
| // TODO this isn't right |
| // tell p2 that it's okay to use these repositories |
| RepositoryTracker repositoryTracker = ProvisioningUI.getDefaultUI().getRepositoryTracker(); |
| repositoryLocations = new HashSet<URI>(); |
| monitor.setWorkRemaining(installableConnectors.size() * 5); |
| for(CatalogItem descriptor : installableConnectors) { |
| URI uri = URI.create(descriptor.getSiteUrl()); |
| if(repositoryLocations.add(uri)) { |
| checkCancelled(monitor); |
| repositoryTracker.addRepository(uri, null, session); |
| } |
| monitor.worked(1); |
| } |
| |
| // fetch meta-data for these repositories |
| ArrayList<IMetadataRepository> repositories = new ArrayList<IMetadataRepository>(); |
| monitor.setWorkRemaining(repositories.size()); |
| IMetadataRepositoryManager manager = (IMetadataRepositoryManager) session.getProvisioningAgent().getService( |
| IMetadataRepositoryManager.SERVICE_NAME); |
| for(URI uri : repositoryLocations) { |
| checkCancelled(monitor); |
| try { |
| IMetadataRepository repository = manager.loadRepository(uri, monitor.newChild(1)); |
| repositories.add(repository); |
| } catch(ProvisionException e) { |
| statuses.add(e.getStatus()); |
| } |
| } |
| return repositories; |
| } |
| |
| /* |
| * Create a RestartInstallOperation and resolve |
| */ |
| private RestartInstallOperation createAndResolve(IProgressMonitor monitor, final IInstallableUnit[] ius, |
| URI[] repositories, boolean requireRestart) throws CoreException { |
| SubMonitor mon = SubMonitor.convert(monitor, ius.length); |
| try { |
| RestartInstallOperation op = new RestartInstallOperation(session, Arrays.asList(ius), postInstallHook, |
| projectsToConfigure, requireRestart ? ProvisioningJob.RESTART_ONLY : ProvisioningJob.RESTART_NONE); |
| if(shouldResolve) { |
| IStatus operationStatus = op.resolveModal(mon); |
| if(operationStatus.getSeverity() > IStatus.WARNING) { |
| throw new CoreException(operationStatus); |
| } |
| } |
| return op; |
| } finally { |
| mon.done(); |
| } |
| } |
| |
| private void checkCancelled(IProgressMonitor monitor) { |
| if(monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| } |
| } |