org.eclipse.papyrus.infra.discovery.ui.internal.common.PrepareInstallProfileJob.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.papyrus.infra.discovery.ui.internal.common.PrepareInstallProfileJob.java

Source

/*******************************************************************************
 * Copyright (c) 2009 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
 *     Obeo - adaptation for Amalgamation, EMF based and no Mylyn dependency
 *     CEA LIST - adaptation to Papyrus
 *******************************************************************************/
package org.eclipse.papyrus.infra.discovery.ui.internal.common;

import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
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.InstallOperation;
import org.eclipse.equinox.p2.operations.ProvisioningSession;
import org.eclipse.equinox.p2.operations.RepositoryTracker;
import org.eclipse.equinox.p2.query.IQuery;
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.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.osgi.util.NLS;
import org.eclipse.papyrus.infra.discovery.InstallableComponent;
import org.eclipse.papyrus.infra.discovery.ui.Activator;
import org.eclipse.papyrus.infra.discovery.ui.internal.wizards.Messages;
import org.eclipse.swt.widgets.Display;

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

    private static final String P2_FEATURE_GROUP_SUFFIX = ".feature.group"; //$NON-NLS-1$

    private final List<InstallableComponent> installableConnectors;

    private final ProvisioningUI provisioningUI;

    private Set<URI> repositoryLocations;

    public PrepareInstallProfileJob(Collection<InstallableComponent> installableConnectors) {
        if (installableConnectors == null || installableConnectors.isEmpty()) {
            throw new IllegalArgumentException();
        }
        this.installableConnectors = new ArrayList<InstallableComponent>(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 Collection<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(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 Collection<IInstallableUnit> ius,
            URI[] repositories) throws CoreException {
        final InstallOperation installOperation = provisioningUI.getInstallOperation(ius, repositories);
        IStatus operationStatus = installOperation
                .resolveModal(new SubProgressMonitor(monitor, installableConnectors.size()));
        if (operationStatus.getSeverity() > IStatus.WARNING) {
            throw new CoreException(operationStatus);
        }
        return installOperation;
    }

    public Collection<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;
        } catch (URISyntaxException e) {
            // should never happen, since we already validated URLs.
            throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
                    Messages.InstallConnectorsJob_unexpectedError_url, e));
        } catch (MalformedURLException e) {
            // should never happen, since we already validated URLs.
            throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
                    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) {
            String id = unit.getId();
            if (id.endsWith(P2_FEATURE_GROUP_SUFFIX)) {
                id = id.substring(0, id.indexOf(P2_FEATURE_GROUP_SUFFIX));
            }
            foundIds.add(id);
        }

        String message = ""; //$NON-NLS-1$
        String detailedMessage = ""; //$NON-NLS-1$
        for (InstallableComponent descriptor : installableConnectors) {
            StringBuilder unavailableIds = null;
            for (String id : descriptor.getId()) {
                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.getSitesURLS() });
            }
        }

        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(DiscoveryUiUtil.getShell(),
                            Messages.InstallConnectorsJob_questionProceed,
                            NLS.bind(Messages.InstallConnectorsJob_questionProceed_long,
                                    new Object[] { finalMessage }));
                }
            });
            if (!okayToProceed[0]) {
                throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
                        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()) == -1) {
                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);
            IQuery<IInstallableUnit> query = QueryUtil.createMatchQuery( //
                    "id ~= /*.feature.group/ && " + //$NON-NLS-1$
                            "properties['org.eclipse.equinox.p2.type.group'] == true ");//$NON-NLS-1$
            IQueryResult<IInstallableUnit> result = repository.query(query, monitor.newChild(1));
            for (Iterator<IInstallableUnit> iter = result.iterator(); iter.hasNext();) {
                IInstallableUnit iu = iter.next();
                String id = iu.getId();
                if (installableUnitIdsThisRepository
                        .contains(id.substring(0, id.indexOf(P2_FEATURE_GROUP_SUFFIX)))) {
                    installableUnits.add(iu);
                }
            }
        }
        return installableUnits;
    }

    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 (InstallableComponent descriptor : installableConnectors) {
            for (String url : descriptor.getSitesURLS()) {
                addASiteURL(monitor, session, repositoryTracker, url);
            }
        }

        // 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 void addASiteURL(SubMonitor monitor, ProvisioningSession session, RepositoryTracker repositoryTracker,
            String url) throws URISyntaxException, MalformedURLException {
        URI uri = new URL(url).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);
    }

    private Set<String> getDescriptorIds(final IMetadataRepository repository) throws URISyntaxException {
        final Set<String> installableUnitIdsThisRepository = new HashSet<String>();
        // determine all installable units for this repository
        for (InstallableComponent descriptor : installableConnectors) {
            try {
                if (hasThisUpdateSite(repository.getLocation(), descriptor)) {
                    installableUnitIdsThisRepository.addAll(descriptor.getId());
                }

            } catch (MalformedURLException e) {
                // will never happen, ignore
            }
        }
        return installableUnitIdsThisRepository;
    }

    private boolean hasThisUpdateSite(URI location, InstallableComponent descriptor) throws MalformedURLException {
        boolean found = false;
        Iterator<String> it = descriptor.getSitesURLS().iterator();
        while (it.hasNext() && !found) {
            String url = it.next();
            if (location.toString().equals(url))
                found = true;
        }
        return found;
    }

}