org.eclipse.equinox.internal.p2.ui.discovery.operations.DiscoveryInstallOperation.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.equinox.internal.p2.ui.discovery.operations.DiscoveryInstallOperation.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2010 Tasktop Technologies and others.
 * 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:
 *     Tasktop Technologies - initial API and implementation
 *     David Dubrow - fix for bug 313412 
 *******************************************************************************/
package org.eclipse.equinox.internal.p2.ui.discovery.operations;

import java.lang.reflect.InvocationTargetException;
import java.net.*;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.discovery.model.CatalogItem;
import org.eclipse.equinox.internal.p2.ui.discovery.DiscoveryUi;
import org.eclipse.equinox.internal.p2.ui.discovery.util.WorkbenchUtil;
import org.eclipse.equinox.internal.p2.ui.discovery.wizards.Messages;
import org.eclipse.equinox.p2.core.ProvisionException;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.operations.*;
import org.eclipse.equinox.p2.query.*;
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.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;

/**
 * A job that configures a p2 install action for installing one or more {@link CatalogItem
 * connectors}. The bulk of the installation work is done by p2; this class just sets up the p2 repository meta-data and
 * selects the appropriate features to install. After running the job the install action
 * must be run to perform the installation.
 * 
 * @author David Green
 * @author Steffen Pingel
 */
public class DiscoveryInstallOperation implements IRunnableWithProgress {

    private final List<CatalogItem> installableConnectors;

    private final ProvisioningUI provisioningUI;

    private Set<URI> repositoryLocations;

    public DiscoveryInstallOperation(List<CatalogItem> installableConnectors) {
        if (installableConnectors == null || installableConnectors.isEmpty()) {
            throw new IllegalArgumentException();
        }
        this.installableConnectors = new ArrayList<CatalogItem>(installableConnectors);
        this.provisioningUI = ProvisioningUI.getDefaultUI();
    }

    public void run(IProgressMonitor progressMonitor) throws InvocationTargetException, InterruptedException {
        try {
            SubMonitor monitor = SubMonitor.convert(progressMonitor, Messages.InstallConnectorsJob_task_configuring,
                    100);
            try {
                final IInstallableUnit[] ius = computeInstallableUnits(monitor.newChild(50));

                checkCancelled(monitor);

                final InstallOperation installOperation = resolve(monitor.newChild(50), ius,
                        repositoryLocations.toArray(new URI[0]));

                checkCancelled(monitor);

                Display.getDefault().asyncExec(new Runnable() {
                    public void run() {
                        provisioningUI.openInstallWizard(Arrays.asList(ius), installOperation, null);
                    }
                });
            } finally {
                monitor.done();
            }
        } catch (OperationCanceledException e) {
            throw new InterruptedException();
        } catch (Exception e) {
            throw new InvocationTargetException(e);
        }
    }

    private void checkCancelled(IProgressMonitor monitor) {
        if (monitor.isCanceled()) {
            throw new OperationCanceledException();
        }
    }

    private InstallOperation resolve(IProgressMonitor monitor, final IInstallableUnit[] ius, URI[] repositories)
            throws CoreException {
        final InstallOperation installOperation = provisioningUI.getInstallOperation(Arrays.asList(ius),
                repositories);
        IStatus operationStatus = installOperation
                .resolveModal(new SubProgressMonitor(monitor, installableConnectors.size()));
        if (operationStatus.getSeverity() > IStatus.WARNING) {
            throw new CoreException(operationStatus);
        }
        return installOperation;
    }

    public IInstallableUnit[] computeInstallableUnits(SubMonitor monitor) throws CoreException {
        try {
            monitor.setWorkRemaining(100);
            // add repository urls and load meta data
            List<IMetadataRepository> repositories = addRepositories(monitor.newChild(50));
            final List<IInstallableUnit> installableUnits = queryInstallableUnits(monitor.newChild(50),
                    repositories);
            removeOldVersions(installableUnits);
            checkForUnavailable(installableUnits);
            return installableUnits.toArray(new IInstallableUnit[installableUnits.size()]);

            //         MultiStatus status = new MultiStatus(DiscoveryUi.ID_PLUGIN, 0, Messages.PrepareInstallProfileJob_ok, null);
            //         ius = installableUnits.toArray(new IInstallableUnit[installableUnits.size()]);
            //         ProfileChangeRequest profileChangeRequest = InstallAction.computeProfileChangeRequest(ius, profileId,
            //               status, new SubProgressMonitor(monitor, installableConnectors.size()));
            //         if (status.getSeverity() > IStatus.WARNING) {
            //            throw new CoreException(status);
            //         }
            //         if (profileChangeRequest == null) {
            //            // failed but no indication as to why
            //            throw new CoreException(new Status(IStatus.ERROR, DiscoveryUi.ID_PLUGIN,
            //                  Messages.PrepareInstallProfileJob_computeProfileChangeRequestFailed, null));
            //         }
            //         PlannerResolutionOperation operation = new PlannerResolutionOperation(
            //               Messages.PrepareInstallProfileJob_calculatingRequirements, profileId, profileChangeRequest, null,
            //               status, true);
            //         IStatus operationStatus = operation.execute(new SubProgressMonitor(monitor, installableConnectors.size()));
            //         if (operationStatus.getSeverity() > IStatus.WARNING) {
            //            throw new CoreException(operationStatus);
            //         }
            //
            //         plannerResolutionOperation = operation;

        } catch (URISyntaxException e) {
            // should never happen, since we already validated URLs.
            throw new CoreException(new Status(IStatus.ERROR, DiscoveryUi.ID_PLUGIN,
                    Messages.InstallConnectorsJob_unexpectedError_url, e));
        } catch (MalformedURLException e) {
            // should never happen, since we already validated URLs.
            throw new CoreException(new Status(IStatus.ERROR, DiscoveryUi.ID_PLUGIN,
                    Messages.InstallConnectorsJob_unexpectedError_url, e));
        } finally {
            monitor.done();
        }
    }

    /**
     * Verifies that we found what we were looking for: it's possible that we have connector descriptors that are no
     * longer available on their respective sites. In that case we must inform the user. Unfortunately this is the
     * earliest point at which we can know.
     */
    private void checkForUnavailable(final List<IInstallableUnit> installableUnits) throws CoreException {
        // at least one selected connector could not be found in a repository
        Set<String> foundIds = new HashSet<String>();
        for (IInstallableUnit unit : installableUnits) {
            foundIds.add(unit.getId());
        }

        String message = ""; //$NON-NLS-1$
        String detailedMessage = ""; //$NON-NLS-1$
        for (CatalogItem descriptor : installableConnectors) {
            StringBuilder unavailableIds = null;
            for (String id : descriptor.getInstallableUnits()) {
                if (!foundIds.contains(id)) {
                    if (unavailableIds == null) {
                        unavailableIds = new StringBuilder();
                    } else {
                        unavailableIds.append(Messages.InstallConnectorsJob_commaSeparator);
                    }
                    unavailableIds.append(id);
                }
            }
            if (unavailableIds != null) {
                if (message.length() > 0) {
                    message += Messages.InstallConnectorsJob_commaSeparator;
                }
                message += descriptor.getName();

                if (detailedMessage.length() > 0) {
                    detailedMessage += Messages.InstallConnectorsJob_commaSeparator;
                }
                detailedMessage += NLS.bind(Messages.PrepareInstallProfileJob_notFoundDescriptorDetail,
                        new Object[] { descriptor.getName(), unavailableIds.toString(), descriptor.getSiteUrl() });
            }
        }

        if (message.length() > 0) {
            // instead of aborting here we ask the user if they wish to proceed anyways
            final boolean[] okayToProceed = new boolean[1];
            final String finalMessage = message;
            Display.getDefault().syncExec(new Runnable() {
                public void run() {
                    okayToProceed[0] = MessageDialog.openQuestion(WorkbenchUtil.getShell(),
                            Messages.InstallConnectorsJob_questionProceed,
                            NLS.bind(Messages.InstallConnectorsJob_questionProceed_long,
                                    new Object[] { finalMessage }));
                }
            });
            if (!okayToProceed[0]) {
                throw new CoreException(new Status(IStatus.ERROR, DiscoveryUi.ID_PLUGIN,
                        NLS.bind(Messages.InstallConnectorsJob_connectorsNotAvailable, detailedMessage), null));
            }
        }
    }

    /**
     * Filters those installable units that have a duplicate in the list with a higher version number. it's possible
     * that some repositories will host multiple versions of a particular feature. we assume that the user wants the
     * highest version.
     */
    private void removeOldVersions(final List<IInstallableUnit> installableUnits) {
        Map<String, Version> symbolicNameToVersion = new HashMap<String, Version>();
        for (IInstallableUnit unit : installableUnits) {
            Version version = symbolicNameToVersion.get(unit.getId());
            if (version == null || version.compareTo(unit.getVersion()) < 0) {
                symbolicNameToVersion.put(unit.getId(), unit.getVersion());
            }
        }
        if (symbolicNameToVersion.size() != installableUnits.size()) {
            for (IInstallableUnit unit : new ArrayList<IInstallableUnit>(installableUnits)) {
                Version version = symbolicNameToVersion.get(unit.getId());
                if (!version.equals(unit.getVersion())) {
                    installableUnits.remove(unit);
                }
            }
        }
    }

    /**
     * Perform a query to get the installable units. This causes p2 to determine what features are available in each
     * repository. We select installable units by matching both the feature id and the repository; it is possible though
     * unlikely that the same feature id is available from more than one of the selected repositories, and we must
     * ensure that the user gets the one that they asked for.
     */
    private List<IInstallableUnit> queryInstallableUnits(SubMonitor monitor, List<IMetadataRepository> repositories)
            throws URISyntaxException {
        final List<IInstallableUnit> installableUnits = new ArrayList<IInstallableUnit>();

        monitor.setWorkRemaining(repositories.size());
        for (final IMetadataRepository repository : repositories) {
            checkCancelled(monitor);
            final Set<String> installableUnitIdsThisRepository = getDescriptorIds(repository);
            IQueryResult<IInstallableUnit> result = repository.query(createInstalledIUsQuery(),
                    monitor.newChild(1));
            for (Iterator<IInstallableUnit> iter = result.iterator(); iter.hasNext();) {
                IInstallableUnit iu = iter.next();
                String id = iu.getId();
                if (installableUnitIdsThisRepository.contains(id))
                    installableUnits.add(iu);
            }
        }
        return installableUnits;
    }

    protected IQuery<IInstallableUnit> createInstalledIUsQuery() {
        return QueryUtil.createIUGroupQuery();
    }

    private List<IMetadataRepository> addRepositories(SubMonitor monitor)
            throws MalformedURLException, URISyntaxException, ProvisionException {
        // tell p2 that it's okay to use these repositories
        ProvisioningSession session = ProvisioningUI.getDefaultUI().getSession();
        RepositoryTracker repositoryTracker = ProvisioningUI.getDefaultUI().getRepositoryTracker();
        repositoryLocations = new HashSet<URI>();
        monitor.setWorkRemaining(installableConnectors.size() * 5);
        for (CatalogItem descriptor : installableConnectors) {
            URI uri = new URL(descriptor.getSiteUrl()).toURI();
            if (repositoryLocations.add(uri)) {
                checkCancelled(monitor);
                repositoryTracker.addRepository(uri, null, session);
                //               ProvisioningUtil.addMetaDataRepository(url.toURI(), true);
                //               ProvisioningUtil.addArtifactRepository(url.toURI(), true);
                //               ProvisioningUtil.setColocatedRepositoryEnablement(url.toURI(), true);
            }
            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);
            IMetadataRepository repository = manager.loadRepository(uri, monitor.newChild(1));
            repositories.add(repository);
        }
        return repositories;
    }

    private Set<String> getDescriptorIds(final IMetadataRepository repository) throws URISyntaxException {
        final Set<String> installableUnitIdsThisRepository = new HashSet<String>();
        // determine all installable units for this repository
        for (CatalogItem descriptor : installableConnectors) {
            try {
                if (repository.getLocation().equals(new URL(descriptor.getSiteUrl()).toURI())) {
                    installableUnitIdsThisRepository.addAll(descriptor.getInstallableUnits());
                }
            } catch (MalformedURLException e) {
                // will never happen, ignore
            }
        }
        return installableUnitIdsThisRepository;
    }

}