/******************************************************************************* * 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 v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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(); } } }