/******************************************************************************* * Copyright (c) 2014 Mentor Graphics 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: * Mentor Graphics - initial API and implementation *******************************************************************************/ package com.codesourcery.internal.installer; import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.NullProgressMonitor; 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.internal.p2.core.helpers.ServiceHelper; import org.eclipse.equinox.internal.p2.touchpoint.natives.Util; import org.eclipse.equinox.internal.p2.ui.query.RequiredIUsQuery; import org.eclipse.equinox.internal.provisional.p2.director.IDirector; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.core.IProvisioningAgentProvider; import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.core.UIServices; import org.eclipse.equinox.p2.engine.IEngine; import org.eclipse.equinox.p2.engine.IProfile; import org.eclipse.equinox.p2.engine.IProfileRegistry; import org.eclipse.equinox.p2.engine.IProvisioningPlan; import org.eclipse.equinox.p2.engine.ISizingPhaseSet; import org.eclipse.equinox.p2.engine.PhaseSetFactory; import org.eclipse.equinox.p2.engine.ProvisioningContext; import org.eclipse.equinox.p2.internal.repository.tools.RepositoryDescriptor; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.IVersionedId; import org.eclipse.equinox.p2.metadata.MetadataFactory; import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.metadata.VersionedId; import org.eclipse.equinox.p2.planner.IPlanner; import org.eclipse.equinox.p2.planner.IProfileChangeRequest; 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.IRepositoryManager; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; import org.eclipse.equinox.p2.repository.artifact.IFileArtifactRepository; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; import org.eclipse.osgi.service.environment.EnvironmentInfo; import org.eclipse.osgi.util.NLS; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import com.codesourcery.installer.IInstallComponent; import com.codesourcery.installer.IInstallConstraint; import com.codesourcery.installer.IInstallConstraint.Constraint; import com.codesourcery.installer.IInstallDescription; import com.codesourcery.installer.IInstallMode; import com.codesourcery.installer.IRepositoryLocation; import com.codesourcery.installer.Installer; /** * This class manages repositories used in the installation. * The manager can be accessed using the {@link #getDefault()} method. */ @SuppressWarnings("restriction") public final class RepositoryManager { /** Install component property indicating it is available from the cache */ private static final String PROPERTY_CACHE = "com.codesourcery.installer.cache"; /** Default instance */ private static RepositoryManager instance = new RepositoryManager(); /** Install components */ private ArrayList<IInstallComponent> components = new ArrayList<IInstallComponent>(); /** Provisioning agent */ private IProvisioningAgent agent; /** Meta-data repository manager */ private IMetadataRepositoryManager metadataRepoMan; /** Artifact repository manager */ private IArtifactRepositoryManager artifactRepoMan; /** Listeners to repository changes */ private ListenerList repositoryListeners = new ListenerList(); /** Artifact repositories */ private ArrayList<IArtifactRepository> artifactRepositories = new ArrayList<IArtifactRepository>(); /** Meta-data repositories */ private ArrayList<IMetadataRepository> metadataRepositories = new ArrayList<IMetadataRepository>(); /** Cache to store computed installation plans */ protected Map<String, IInstallPlan> planCache; /** Installer size thread */ Thread uninstallerSizeThread; /** Size of uninstaller files */ protected long uninstallerSize = 0; /** Install location */ private IPath installLocation; /** Installation profile identifier */ private String profileId; /** Cache location */ private IPath cacheLocation; /** Cache artifact repository */ private IArtifactRepository cacheArtifactRepository; /** Cache meta-data repository */ private IMetadataRepository cacheMetadataRepository; /** <code>true</code> to only install from the cache repository */ private boolean cacheOnly = false; /** <code>true</code> to update the cache with installed IU's */ private boolean cacheUpdate = false; /** Temporary repository to hold product installation IU */ private ProductRepository productRepository; /** * Constructor */ private RepositoryManager() { // Synchronize size cache planCache = Collections.synchronizedMap(new HashMap<String, IInstallPlan>()); } /** * @return The product repository or <code>null</code> if no product repository will be used. */ private ProductRepository getProductRepository() { return productRepository; } /** * Sets the location for the install cache. The cache will store a mirrored P2 repository containing any * installed IU's. * * @param cacheLocation Cache location. * @see #getCacheLocation() */ public void setCacheLocation(IPath cacheLocation) { this.cacheLocation = cacheLocation; } /** * @return Returns the cache location. * @see #setCacheLocation(IPath) */ public IPath getCacheLocation() { return cacheLocation; } /** * Returns if a component is available in the cache. * * @param component Component * @return <code>true</code> if component is in cache. */ public boolean isCachedComponent(IInstallComponent component) { String cached = (String)component.getProperty(PROPERTY_CACHE); return ((cached != null) && Boolean.TRUE.toString().equals(cached)); } /** * Sets whether IU's will only be installed from the cache. If only the cache is used, any IU's not found in the * cache will not be available for installation. * * @param cacheOnly <code>true</code> to only install from cache. * @see #setCacheLocation(IPath) */ public void setCacheOnly(boolean cacheOnly) { this.cacheOnly = cacheOnly; } /** * @return <code>true</code> if only the cache will be used for installation. */ public boolean getCacheOnly() { return cacheOnly; } /** * Sets whether the cache will be updated with installed IU's. * * @param cacheUpdate <code>true</code> to update cache. */ public void setUpdateCache(boolean cacheUpdate) { this.cacheUpdate = cacheUpdate; } /** * @return <code>true</code> to update cache. */ public boolean getUpdateCache() { return cacheUpdate; } /** * Creates the P2 provisioning agent. * * @param installLocation Install location or <code>null</code> to use installer agent * @param monitor Progress monitor or <code>null</code> * @return Provisioning agent or <code>null</code> */ public IProvisioningAgent createAgent(IPath installLocation, IProgressMonitor monitor) throws CoreException { try { boolean locationChanged = false; if ((installLocation != null) && !installLocation.equals(getInstallLocation())) { locationChanged = true; } // Agent not created or install location has changed if ((agent == null) || locationChanged) { this.installLocation = installLocation; if (monitor == null) monitor = new NullProgressMonitor(); // Clear plan cache planCache.clear(); // Start P2 agent agent = startAgent(getAgentLocation()); // Setup certificate handling agent.registerService(UIServices.SERVICE_NAME, AuthenticationService.getDefault()); // Meta-data repository manager metadataRepoMan = (IMetadataRepositoryManager)agent.getService(IMetadataRepositoryManager.SERVICE_NAME); // Artifact repository manager artifactRepoMan = (IArtifactRepositoryManager)agent.getService(IArtifactRepositoryManager.SERVICE_NAME); IInstallDescription installDescription = Installer.getDefault().getInstallManager().getInstallDescription(); if (installDescription != null) { // Initialize the profile identifier profileId = Installer.getDefault().getInstallManager().getInstallDescription().getProfileName(); // If no profile specified, use the first installed profile if (profileId == null) { IProfileRegistry profileRegistry = (IProfileRegistry)getAgent().getService(IProfileRegistry.SERVICE_NAME); IProfile[] profiles = profileRegistry.getProfiles(); if (profiles.length > 0) { profileId = profiles[0].getProfileId(); } } // Initialize product repository if required if (productRepository != null) { productRepository.dispose(); } if (installDescription.getProductRoot()) { productRepository = new ProductRepository(agent); productRepository.createRepository(); } } } } catch (Exception e) { Installer.fail(e.getLocalizedMessage()); } return agent; } /** * Loads the cache repository if it is available. If the cache repository has already been loaded then it will be * refreshed. * * @return <code>true</code> if cache repository was loaded. * @throws ProvisionException on provisioning failure. * @throws OperationCanceledException if operation was cancelled. */ private synchronized boolean loadCacheRepository() throws ProvisionException, OperationCanceledException { boolean loaded = false; IPath cacheLocation = getCacheLocation(); if (cacheLocation != null) { // If cache repository is already loaded then refresh it if ((cacheMetadataRepository != null) && (cacheArtifactRepository != null)) { getMetadataRepositoryManager().refreshRepository(cacheMetadataRepository.getLocation(), null); getArtifactRepositoryManager().refreshRepository(cacheArtifactRepository.getLocation(), null); loaded = true; } // Load the cache repository else { File cacheFile = cacheLocation.toFile(); if (cacheFile.exists()) { // Cache location (co-located) URI repositoryLocation = cacheFile.toURI(); fireRepositoryStatus(IInstallRepositoryListener.RepositoryStatus.loadingStarted); try { getMetadataRepositoryManager().addRepository(repositoryLocation); cacheMetadataRepository = getMetadataRepositoryManager().loadRepository(repositoryLocation, null); if (cacheMetadataRepository != null) { getArtifactRepositoryManager().addRepository(repositoryLocation); cacheArtifactRepository = getArtifactRepositoryManager().loadRepository(repositoryLocation, null); loaded = true; } } catch (Exception e) { unloadCacheRepository(); fireRepositoryError(repositoryLocation, e.getLocalizedMessage()); throw e; } } } // Load components if (loaded) { // Load components available in cache repository IInstallComponent[] components = loadComponents(cacheMetadataRepository); if (components.length > 0) { // Mark the components as coming from the cache for (IInstallComponent component : components) { component.setProperty(PROPERTY_CACHE, Boolean.TRUE.toString()); } // Fire notification fireComponentsChanged(); } fireRepositoryStatus(IInstallRepositoryListener.RepositoryStatus.loadingCompleted); } } return loaded; } /** * Unloads the cache repository. */ private void unloadCacheRepository() { if (cacheMetadataRepository != null) { getMetadataRepositoryManager().removeRepository(cacheMetadataRepository.getLocation()); cacheMetadataRepository = null; } if (cacheArtifactRepository != null) { getArtifactRepositoryManager().removeRepository(cacheArtifactRepository.getLocation()); cacheArtifactRepository = null; } } /** * @return <code>true</code> if a cache repository is available. */ private boolean cacheAvailable() { IPath cacheLocation = getCacheLocation(); return ((cacheLocation != null) && cacheLocation.toFile().exists()); } /** * Loads the P2 repositories for installation. * * @param monitor Progress monitor * @throws CoreException if no repositories could be loaded */ public void loadInstallRepositories(IProgressMonitor monitor) throws CoreException { IInstallDescription installDescription = Installer.getDefault().getInstallManager().getInstallDescription(); if (installDescription != null) { // Get the repository locations List<IRepositoryLocation> locations = installDescription.getRepositoryLocations(); // Get the command line that can specify a specific mirror to use String commandLineGroup = Installer.getDefault().getCommandLineOption(IInstallConstants.COMMAND_LINE_MIRROR); // Clear install components components.clear(); boolean loaded = false; // Load cache repository if (cacheAvailable()) { try { loaded = loadCacheRepository(); } catch (Exception e) { Installer.log(e); } } if (!getCacheOnly()) { for (IRepositoryLocation location : locations) { // If a specific mirror has been specified, skip the rest if ((commandLineGroup != null) && !location.getId().equals(commandLineGroup)) continue; // Attempt to load the repositories from the location if (loadMetadataRepositories(location.getMetadataLocations(), monitor)) { if (loadArtifactRepositories(location.getArtifactLocations(), monitor)) { loaded = true; break; } } } } // No repositories could be loaded from any locations if (!loaded) { Installer.fail(InstallMessages.Error_FailedToLoadRepositories); } } } /** * Unloads the install repositories. */ private void unloadInstallRepositories() { try { // Remove repositories for (IMetadataRepository repository : getMetadataRepositories()) { getMetadataRepositoryManager().removeRepository(repository.getLocation()); } for (IArtifactRepository repository : getArtifactRepositories()) { getArtifactRepositoryManager().removeRepository(repository.getLocation()); } metadataRepositories.clear(); artifactRepositories.clear(); } catch (Exception e) { Installer.log(e); } } /** * Stops the P2 provisioning agent. */ public void stopAgent() { if (agent != null) { unloadInstallRepositories(); agent.stop(); agent = null; } } /** * @return Returns the installation profile identifier */ public String getProfileId() { return profileId; } /** * Returns the install location. * * @return Install location */ public IPath getInstallLocation() { return installLocation; } /** * Returns the agent location. * * @return Agent location or <code>null</code> */ public IPath getAgentLocation() { if (getInstallLocation() == null) { return null; } else { return getInstallLocation().append(IInstallConstants.P2_DIRECTORY); } } /** * Creates a profile. * * @param profileId Profile identifier * @return Profile * @throws ProvisionException on failure */ public IProfile createProfile(String profileId) throws ProvisionException { IProfileRegistry profileRegistry = (IProfileRegistry)getAgent().getService(IProfileRegistry.SERVICE_NAME); IProfile profile = profileRegistry.getProfile(profileId); // Note: On uninstall, the profile will always be available if (profile == null) { Map<String, String> properties = new HashMap<String, String>(); // Install location - this is where the p2 directory will be located properties.put(IProfile.PROP_INSTALL_FOLDER, getInstallLocation().toString()); // Environment EnvironmentInfo info = (EnvironmentInfo) ServiceHelper.getService(Installer.getDefault().getContext(), EnvironmentInfo.class.getName()); String env = "osgi.os=" + info.getOS() + ",osgi.ws=" + info.getWS() + ",osgi.arch=" + info.getOSArch(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ properties.put(IProfile.PROP_ENVIRONMENTS, env); // Profile identifier properties.put(IProfile.PROP_NAME, profileId); // Cache location - this is where features and plugins will be deployed properties.put(IProfile.PROP_CACHE, getInstallLocation().toOSString()); // Set roaming. This will put a path relative to the OSGi configuration area in the config.ini // so that the installation can be moved. Without roaming, absolute paths will be written // to the config.ini and Software Update will not work correctly for a moved installation. properties.put(IProfile.PROP_ROAMING, Boolean.TRUE.toString()); // Profile properties specified in install description if (Installer.getDefault().getInstallManager().getInstallDescription().getProfileProperties() != null) properties.putAll(Installer.getDefault().getInstallManager().getInstallDescription().getProfileProperties()); profile = profileRegistry.addProfile(profileId, properties); } return profile; } public IProfile getExistingInstallProfile() { String profileId = getProfileId(); IProfile profile = getProfile(profileId); return profile; } /** * Returns the profile for this installation. The profile is created if * necessary. * * @return Profile Profile or <code>null</code> if no install location was specified * @throws ProvisionException on failure to create the profile */ public IProfile getInstallProfile() throws ProvisionException { IProfile profile = null; if (getInstallLocation() != null) { String profileId = getProfileId(); profile = getProfile(profileId); if (profile == null) { profile = createProfile(profileId); } } return profile; } /** * Returns a profile. * * @param profileId Profile identifier * @return Profile or <code>null</code> if the profile does not exist. */ public IProfile getProfile(String profileId) { if (profileId == null) return null; IProfileRegistry profileRegistry = (IProfileRegistry)getAgent().getService(IProfileRegistry.SERVICE_NAME); return profileRegistry.getProfile(profileId); } /** * Removes a profile. * * @param profileId Profile identifier * @throws ProvisionException on failure */ public void deleteProfile(String profileId) throws ProvisionException { IProfileRegistry profileRegistry = (IProfileRegistry)agent.getService(IProfileRegistry.SERVICE_NAME); profileRegistry.removeProfile(profileId); } /** * Starts the p2 bundles needed to continue with the install. * * @param agentLocation Agent location or <code>null</code> to use default agent. * @throws ProvisionException on failure to start the agent */ private IProvisioningAgent startAgent(IPath agentLocation) throws ProvisionException { IProvisioningAgentProvider provider = (IProvisioningAgentProvider) getService(Installer.getDefault().getContext(), IProvisioningAgentProvider.SERVICE_NAME); return provider.createAgent(agentLocation == null ? null : agentLocation.toFile().toURI()); } /** * Copied from ServiceHelper because we need to obtain services * before p2 has been started. */ public static Object getService(BundleContext context, String name) { if (context == null) return null; ServiceReference<?> reference = context.getServiceReference(name); if (reference == null) return null; Object result = context.getService(reference); context.ungetService(reference); return result; } /** * Shuts down the repository manager. */ public synchronized void shutdown() { // Stop the P2 agent stopAgent(); } /** * Returns the provisioning agent. * * @return Provisioning agent */ public IProvisioningAgent getAgent() { return agent; } /** * @return Returns the meta-data repository manager. */ private IMetadataRepositoryManager getMetadataRepositoryManager() { return metadataRepoMan; } /** * @return Returns the artifact repository manager. */ private IArtifactRepositoryManager getArtifactRepositoryManager() { return artifactRepoMan; } /** * Returns the default instance. * * @return Instance */ public static RepositoryManager getDefault() { return instance; } /** * Loads meta-data repositories. * * @param repositoryLocations Locations of repositories to load * @param monitor Progress monitor * @param <code>true</code> if all repositories were loaded */ public boolean loadMetadataRepositories(URI[] repositoryLocations, IProgressMonitor monitor) { return internalLoadMetadataRepositories(repositoryLocations, monitor); } /** * Loads artifact repositories. * * @param repositoryLocations Locations of repositories to load. * @param monitor Progress monitor * @param <code>true</code> if all repositories were loaded */ public boolean loadArtifactRepositories(URI[] repositoryLocations, IProgressMonitor monitor) { return internalLoadArtifactRepositories(repositoryLocations, monitor); } /** * Returns if an IU is a category. * * @param unit Install unit * @return <code>true</code> if category */ private boolean isCategoryIu(IInstallableUnit unit) { String category = unit.getProperty(QueryUtil.PROP_TYPE_CATEGORY); return ((category != null) && Boolean.TRUE.toString().equals(category)); } /** * Adds a new install component for an installable unit. If the installable * unit is a category, it's member units will also be added. * * @param unit Installable unit * @param parentGroup Parent component or <code>null</code> * @return Added components or <code>null</code> if the component has already been added * @throws ProvisionException on failure */ private List<IInstallComponent> addInstallComponent(IInstallableUnit unit, InstallComponent parentGroup) throws ProvisionException { IInstallComponent existingComponent = getInstallComponent(unit.getId()); ArrayList<IInstallComponent> addedComponents = new ArrayList<IInstallComponent>(); InstallComponent component = null; // Category IU boolean isCategory = isCategoryIu(unit); // If there is an existing component for the unit if (existingComponent != null) { // If category, add to existing component if (isCategory) component = (InstallComponent)existingComponent; // Else do not allow duplicates units else return null; } // Create component if (component == null) { component = new InstallComponent(unit); } // If category unit, add members if (isCategory) { IQuery<IInstallableUnit> categoryQuery = QueryUtil.createIUCategoryMemberQuery(unit); IQueryResult<IInstallableUnit> query = getMetadataRepositoryManager().query(categoryQuery, null); Iterator<IInstallableUnit> iter = query.iterator(); while (iter.hasNext()) { IInstallableUnit categoryMemberUnit = iter.next(); List<IInstallComponent> members = addInstallComponent(categoryMemberUnit, component); if (members != null) { addedComponents.addAll(members); } } } // Add component if (existingComponent == null) { if (parentGroup != null) { parentGroup.addComponent(component); } // Set component parent component.setParent(parentGroup); // Add component components.add(component); addedComponents.add(component); } return addedComponents; } /** * Loads install components from a meta-data repository. * * @param repository Meta-data repository * @return Install components * @throws ProvisionException on failure */ private IInstallComponent[] loadComponents(IMetadataRepository repository) throws ProvisionException { ArrayList<IInstallComponent> loadedComponents = new ArrayList<IInstallComponent>(); ArrayList<IVersionedId> units = new ArrayList<IVersionedId>(); // Required roots IVersionedId[] requiredRoots = Installer.getDefault().getInstallManager().getInstallDescription().getRequiredRoots(); if (requiredRoots != null) { for (IVersionedId unit : requiredRoots) { units.add(unit); } } // Optional roots IVersionedId[] optionalRoots = Installer.getDefault().getInstallManager().getInstallDescription().getOptionalRoots(); if (optionalRoots != null) { for (IVersionedId unit : optionalRoots) { units.add(unit); } } // Create components RepositoryAdapter adapter = new RepositoryAdapter(repository); for (IVersionedId unit : units) { IInstallableUnit iu = adapter.findUnit(unit); if (iu != null) { List<IInstallComponent> addedComponents = addInstallComponent(iu, null); // If component has not already been added if (addedComponents != null) { loadedComponents.addAll(addedComponents); } } } IInstallComponent[] components = loadedComponents.toArray(new IInstallComponent[loadedComponents.size()]); // Setup component attributes setupComponents(components); // Sort components sortComponents(); return components; } /** * Loads repository meta-data information. Components will be added for * all non-category group roots found in the repositories. * * @param repositoryLocations Locations of repositories to load * @param monitor Progress monitor or <code>null</code> * @return <code>true</code> if all repositories were loaded */ public synchronized boolean internalLoadMetadataRepositories(URI[] locations, IProgressMonitor monitor) { boolean loaded = true; if (monitor == null) { monitor = new NullProgressMonitor(); } fireRepositoryStatus(IInstallRepositoryListener.RepositoryStatus.loadingStarted); try { SubMonitor subprogress = SubMonitor.convert(monitor, InstallMessages.LoadingRepositories, locations.length * 2); // Load the repositories for (URI repositoryLocation : locations) { try { // Load the meta-data repository monitor.setTaskName(NLS.bind(InstallMessages.LoadingMetadataRepository0, repositoryLocation.toString())); getMetadataRepositoryManager().addRepository(repositoryLocation); IMetadataRepository repository = getMetadataRepositoryManager().loadRepository(repositoryLocation, subprogress.newChild(1)); metadataRepositories.add(repository); // Load components loadComponents(repository); // Fire components notification fireComponentsChanged(); } catch (Exception e) { loaded = false; Installer.log(e); getMetadataRepositoryManager().removeRepository(repositoryLocation); getArtifactRepositoryManager().removeRepository(repositoryLocation); fireRepositoryError(repositoryLocation, e.getLocalizedMessage()); // Don't attempt to load other repositories break; } } if (loaded) { setupComponentConstraints(); // Pre-calculate the install size for complete set of // of repository components. IInstallMode mode = Installer.getDefault().getInstallManager().getInstallMode(); if (!mode.isUpdate() && !mode.isUpdate()) initializeUninstallerSize(); fireRepositoryStatus(IInstallRepositoryListener.RepositoryStatus.loadingCompleted); } } catch (Exception e) { Installer.log(e); } return loaded; } /** * Loads repository artifact information. * * @param locations Locations of repositories to load * @param monitor Progress monitor * @return <code>true</code> if all repositories were loaded */ public synchronized boolean internalLoadArtifactRepositories(URI[] locations, IProgressMonitor monitor) { boolean loaded = true; SubMonitor subprogress = SubMonitor.convert(monitor, InstallMessages.LoadingRepositories, locations.length); for (URI location : locations) { try { // Load the artifact repository monitor.setTaskName(NLS.bind(InstallMessages.LoadingArtifactRepository0, location.toString())); getArtifactRepositoryManager().addRepository(location); IArtifactRepository repository = getArtifactRepositoryManager().loadRepository(location, subprogress.newChild(1)); artifactRepositories.add(repository); } catch (Exception e) { loaded = false; Installer.log(e); getArtifactRepositoryManager().removeRepository(location); } } return loaded; } /** * Sorts components according to the order they appear in the install * description eclipse.p2.requiredRoots or eclipse.p2.optionalRoots * properties. If the component does not appear in either list, it is * sorted according to its name. */ private void sortComponents() { // Build one list containing the required and optional roots IVersionedId[] requiredRoots = Installer.getDefault().getInstallManager().getInstallDescription().getRequiredRoots(); IVersionedId[] optionalRoots = Installer.getDefault().getInstallManager().getInstallDescription().getOptionalRoots(); final ArrayList<IVersionedId> roots = new ArrayList<IVersionedId>(); if (requiredRoots != null) { roots.addAll(Arrays.asList(requiredRoots)); } if (optionalRoots != null) { roots.addAll(Arrays.asList(optionalRoots)); } Comparator<IInstallComponent> componentOrderComparator = new Comparator<IInstallComponent>() { @Override public int compare(IInstallComponent arg0, IInstallComponent arg1) { int i0 = -1; int i1 = -1; // Find the index of both arguments in the // required/optional list for (int index = 0; index < roots.size(); index ++) { IVersionedId root = roots.get(index); if (arg0.getInstallUnit().getId().equals(root.getId())) { i0 = index; } if (arg1.getInstallUnit().getId().equals(root.getId())) { i1 = index; } } // If both arguments are not in list then sort by name if ((i0 == -1) && (i1 == -1)) { return arg0.getName().compareTo(arg1.getName()); } // Push first argument to end if not in list else if (i0 == -1) { return 1; } // Push second argument to end if not in list else if (i1 == -1) { return -1; } // Else sort by order in required/optional list else { return Integer.compare(i0, i1); } } }; Collections.sort(components, componentOrderComparator); } /** * Returns if a component is default. * * @param component Install component * @return <code>true</code> if component is default */ private boolean isDefaultComponent(IInstallComponent component) { boolean isDefault = false; // Default roots IVersionedId[] defaultRoots = Installer.getDefault().getInstallManager().getInstallDescription().getDefaultOptionalRoots(); if (defaultRoots != null) { for (IVersionedId defaultRoot : defaultRoots) { // Component is default root if (component.getInstallUnit().getId().equals(defaultRoot.getId())) { isDefault = true; break; } // Else check if parent of component is default root else { IInstallComponent parent = component.getParent(); while (parent != null) { if (parent.getInstallUnit().getId().equals(defaultRoot.getId())) { return true; } parent = parent.getParent(); } } } } return isDefault; } /** * Returns if a component is required. * * @param component Install component * @return <code>true</code> if component is required */ private boolean isRequiredComponent(IInstallComponent component) { boolean isRequired = false; IVersionedId[] requiredRoots = Installer.getDefault().getInstallManager().getInstallDescription().getRequiredRoots(); // Find in required roots specification if (requiredRoots != null) { for (IVersionedId requiredRoot : requiredRoots) { // Component is required root if (component.getInstallUnit().getId().equals(requiredRoot.getId())) { return true; } // Else check if parent of component is a required root else { IInstallComponent parent = component.getParent(); while (parent != null) { if (parent.getInstallUnit().getId().equals(requiredRoot.getId())) { return true; } parent = parent.getParent(); } } } } return isRequired; } /** * Sets up attributes for all loaded components. * Required components will be set to install. * If at least one component has been installed (update) then optional * components will be set to install only if they are already installed. * If no components have been installed (new install) then optional * components will be set to install if they are default. * * @param components Install components */ private void setupComponents(IInstallComponent[] components) { // Installation mode IInstallMode installMode = Installer.getDefault().getInstallManager().getInstallMode(); // Query for existing component IU's. IQueryResult<IInstallableUnit> installedResult = null; IQuery<IInstallableUnit> query = QueryUtil.createIUGroupQuery(); IProfile profile = getExistingInstallProfile(); if (profile != null) { installedResult = profile.query(query, null); } // Step 1: Set optional, default, and installed state of components for (IInstallComponent component : components) { InstallComponent comp = (InstallComponent)component; // Set optional status boolean isOptional = !isRequiredComponent(component); comp.setOptional(isOptional); // Set default components if (isOptional) { boolean isDefault = isDefaultComponent(component); comp.setDefault(isDefault); } // Required component else { comp.setDefault(true); } // Set existing IU (if available) if (installedResult != null) { Iterator<IInstallableUnit> iter = installedResult.iterator(); while (iter.hasNext()) { IInstallableUnit existingUnit = iter.next(); if (existingUnit.getId().equals(comp.getInstallUnit().getId())) { comp.setInstalledUnit(existingUnit); break; } } } } // Step 2: Set install state for non-group components. Note that due to groups, this must be done after the // optional state has been set for all components. The install state for groups will be computed later based // on the install state of its members. for (IInstallComponent component : components) { InstallComponent comp = (InstallComponent)component; // Skip groups if (comp.hasMembers()) continue; // Optional component if (comp.isOptional()) { // If component is not already installed, set it to install if it is default if (comp.getInstalledUnit() == null) { comp.setInstall(installMode.isUpdate() ? false : comp.isDefault()); } // Else set it to install else { comp.setInstall(true); // If this is a new install or upgrade, change the component to be required. This will prevent // an installation of a second product into a first product from removing a component installed by // the first product. if (!installMode.isUpdate()) { comp.setOptional(false); } } } // Required component else { comp.setInstall(true); } } // Step 3: Set the install state for group components based on the install state of members for (IInstallComponent component : components) { InstallComponent comp = (InstallComponent)component; // Is group if (comp.hasMembers()) { ArrayList<IInstallComponent> members = new ArrayList<IInstallComponent>(); getInstallComponentMembers(comp, members); boolean allInstall = true; boolean allRequired = true; // Check if all group members are set to install and if they are required for (IInstallComponent member : members) { if (!member.getInstall()) { allInstall = false; } if (member.isOptional()) { allRequired = false; } } // Set group to install if (allInstall) { comp.setInstall(true); } // Set group to be required if (allRequired) { comp.setOptional(false); } } } } /** * Sets up components according to any install constraints specified. */ private void setupComponentConstraints() { try { // Loop through the constraints IInstallConstraint[] constraints = Installer.getDefault().getInstallManager().getInstallDescription().getInstallConstraints(); if (constraints != null) { for (IInstallConstraint constraint : constraints) { // If a constraint that one component out of a set of 'one' components is specified, mark that component // as required (as there are no other components specified that can be selected) if (constraint.getConstraint() == Constraint.ONE_OF) { IInstallComponent[] targets = constraint.getTargets(); if ((targets != null) && (targets.length == 1)) { if (targets[0] != null) { ((InstallComponent)targets[0]).setOptional(false); } } } } } } catch (Exception e) { Installer.log(e); } } /** * @return Meta-data repositories */ public IMetadataRepository[] getMetadataRepositories() { return metadataRepositories.toArray(new IMetadataRepository[metadataRepositories.size()]); } /** * @return Returns the meta-data repository locations. */ public URI[] getMetadataRepositoryLocations() { ArrayList<URI> locations = new ArrayList<URI>(); for (IMetadataRepository repository : getMetadataRepositories()) { locations.add(repository.getLocation()); } return locations.toArray(new URI[locations.size()]); } /** * @return Artifact repositories */ public IArtifactRepository[] getArtifactRepositories() { return artifactRepositories.toArray(new IArtifactRepository[artifactRepositories.size()]); } /** * @return Returns the artifact repository locations. */ public URI[] getArtifactRepositoryLocations() { ArrayList<URI> locations = new ArrayList<URI>(); for (IArtifactRepository repository : getArtifactRepositories()) { locations.add(repository.getLocation()); } return locations.toArray(new URI[locations.size()]); } /** * Adds a listener to component changes. * * @param listener Listener to add */ public void addRepositoryListener(IInstallRepositoryListener listener) { repositoryListeners.add(listener); } /** * Removes a listener from component changes. * * @param listener Listener to remove */ public void removeRepositoryListener(IInstallRepositoryListener listener) { repositoryListeners.remove(listener); } /** * Returns install components. * * @param groupsOnly <code>true</code> to return group components and * components not contained in a group. <code>false</code> to return all * install components including groups. This will include component group * members. * @return Install components */ public IInstallComponent[] getInstallComponents(boolean groupsOnly) { if (groupsOnly) { ArrayList<IInstallComponent> groups = new ArrayList<IInstallComponent>(); for (IInstallComponent component : components) { if (component.getParent() == null) { groups.add(component); } } return groups.toArray(new IInstallComponent[groups.size()]); } else { return components.toArray(new IInstallComponent[components.size()]); } } /** * Gets all members of a group component. This includes all member components of contained group members. * If the component is not a group (contains no member components) then this method does nothing. * * @param component Component for members * @param componentMembers All member components. */ private void getInstallComponentMembers(IInstallComponent component, List<IInstallComponent> componentMembers) { if (component.hasMembers()) { IInstallComponent[] members = component.getMembers(); for (IInstallComponent member : members) { getInstallComponentMembers(member, componentMembers); } componentMembers.addAll(Arrays.asList(members)); } } /** * Returns if there are components to add or remove in installation. * * @return <code>true</code> if there is an install plan */ public boolean hasInstallUnits() { ArrayList<IInstallableUnit> toAdd = new ArrayList<IInstallableUnit>(); ArrayList<IInstallableUnit> toRemove = new ArrayList<IInstallableUnit>(); getInstallUnits(toAdd, toRemove); return ((toAdd.size() != 0) || (toRemove.size() != 0)); } /** * Returns all installable units including groups. * * @param toAdd Filled with installable units to add. */ public void getAllInstallUnits(List<IInstallableUnit> toAdd) { toAdd.clear(); for (IInstallComponent component : getInstallComponents(false)) { if (component.isIncluded() && component.getInstall()) { toAdd.add(component.getInstallUnit()); } } } /** * Get the installation units. * * @param toAdd Filled with installable units to add * @param toRemove Filled with installable units to remove */ public void getInstallUnits(List<IInstallableUnit> toAdd, List<IInstallableUnit> toRemove) { toAdd.clear(); toRemove.clear(); IProfile installProfile = null; try { installProfile = getInstallProfile(); } catch (ProvisionException e) { Installer.log(e); return; } // If a temporary repository is setup for the product, a root IU will be created for the product. This IU will // specify requirements on the installation IU's and be used to provision the product. // If no product repository is available, the IU's selected for installation will be provisioned directly as // roots. boolean createProductRoot = (getProductRepository() != null); // Installation mode IInstallMode mode = Installer.getDefault().getInstallManager().getInstallMode(); boolean removeProfile = Installer.getDefault().getInstallManager().getInstallDescription().getRemoveProfile(); ArrayList<IInstallableUnit> unitsToAdd = new ArrayList<IInstallableUnit>(); for (IInstallComponent component : getInstallComponents(false)) { // Unit to install IInstallableUnit installUnit = component.getInstallUnit(); // If component is included and not a group if (component.isIncluded() && !component.hasMembers()) { // Existing installed unit IInstallableUnit installedUnit = component.getInstalledUnit(); // Create product IU for IU's if (createProductRoot) { if (component.getInstall()) { unitsToAdd.add(installUnit); } // Remove any product IU's that were previously provisioned as // root IU's. if (installedUnit != null) { String rootProperty = installProfile.getInstallableUnitProperty(installedUnit, IProfile.PROP_PROFILE_ROOT_IU); if (Boolean.TRUE.toString().equals(rootProperty)) { toRemove.add(installedUnit); } } } // Provision individual IU's else { // Component marked for install if (component.getInstall()) { // If newer version to install if ((installedUnit == null) || installUnit.getVersion().compareTo(installedUnit.getVersion()) > 0) { // Add new unit unitsToAdd.add(installUnit); // If there is an existing unit, remove it if we are not // removing all existing units in the profile if ((installedUnit != null) && !removeProfile) { toRemove.add(installedUnit); } } } // Component marked for uninstall, remove older unit if present else if (installedUnit != null) { toRemove.add(installedUnit); } } } } if (installProfile != null) { // A root IU being added could require other IU's that have been previously provisioned as root IU's. // If these IU's are a newer version than the existing IU's, provisioning will fail because the required // root IU has not been requested for removal (P2 will not automatically replace root IU's). // Add any required root IU's for removal for (IInstallableUnit unit : unitsToAdd) { ArrayList<IInstallableUnit> requiredRoots = new ArrayList<IInstallableUnit>(); getExistingRequiredRootIUs(unit, requiredRoots); if (!requiredRoots.isEmpty()) { toRemove.addAll(requiredRoots); } } } // Remove all units in the profile if (removeProfile) { if (mode.isUpgrade()) { IProfile profile = getExistingInstallProfile(); if (profile != null) { IQueryResult<IInstallableUnit> query = profile.query(QueryUtil.createIUAnyQuery(), null); Iterator<IInstallableUnit> i = query.iterator(); while (i.hasNext()) { toRemove.add(i.next()); } } } } // Create product root IU if (createProductRoot) { try { // Add product IU IInstallableUnit[] productRoots = getProductRepository().createProductIu(getInstallProfile(), unitsToAdd); toAdd.add(productRoots[0]); // Remove existing product IU if one is installed if (productRoots[1] != null) { toRemove.add(productRoots[1]); } } catch (Exception e) { Installer.log(e); } } // Or provision each IU as a root else { toAdd.addAll(unitsToAdd); } } /** * Checks the requirements of a specified IU and returns any that are already installed as a root IU. * * @param unit IU to check for requirements. * @param requiredRoots Filled with root IU's that are required by the IU. */ private void getExistingRequiredRootIUs(IInstallableUnit unit, ArrayList<IInstallableUnit> requiredRoots) { try { // Get requirements for the IU IQueryResult<IInstallableUnit> requirements = getMetadataRepositoryManager().query(new RequiredIUsQuery(unit), new NullProgressMonitor()); Iterator<IInstallableUnit> iter = requirements.iterator(); ProfileAdapter profileAdapter = new ProfileAdapter(getInstallProfile()); RepositoryManagerAdapter repositoryAdapter = new RepositoryManagerAdapter(getMetadataRepositoryManager()); while (iter.hasNext()) { IInstallableUnit required = iter.next(); // Check if the required IU is already present in the profile IInstallableUnit existingRequired = profileAdapter.findUnit(required.getId()); if (existingRequired != null) { // See if the required IU is present in the repository IInstallableUnit installingUnit = repositoryAdapter.findUnit(new VersionedId(existingRequired.getId(), Version.emptyVersion)); if ((installingUnit != null) && !requiredRoots.contains(installingUnit)) { // Is the required IU a root String root = getInstallProfile().getInstallableUnitProperties(existingRequired).get(IProfile.PROP_PROFILE_ROOT_IU); boolean newerVersion = (required.getVersion().compareTo(existingRequired.getVersion()) > 0); // If the required IU is an existing root then add it if (Boolean.TRUE.toString().equals(root) && newerVersion) { if (!requiredRoots.contains(existingRequired)) { requiredRoots.add(existingRequired); getExistingRequiredRootIUs(required, requiredRoots); } } } } } } catch (Exception e) { Installer.log(e); } } /** * Returns the context for provisioning. The context will be scoped to the installation repositories. For an * update, the context will also include the installed repositories. * * @return Provisioning context */ public ProvisioningContext getProvisioningContext() { ProvisioningContext context = new ProvisioningContext(getAgent()); ArrayList<URI> metadataRepositories = new ArrayList<URI>(); ArrayList<URI> artifactRepositories = new ArrayList<URI>(); // Add installer repositories metadataRepositories.addAll(Arrays.asList(getMetadataRepositoryLocations())); artifactRepositories.addAll(Arrays.asList(getArtifactRepositoryLocations())); // Add cache meta-data repository if available if (cacheMetadataRepository != null) { metadataRepositories.add(cacheMetadataRepository.getLocation()); } // Add cache artifact repository if available if (cacheArtifactRepository != null) { artifactRepositories.add(cacheArtifactRepository.getLocation()); } // If update then include installed repositories in addition to installer repositories. // Include only local installed repositories for much improved performance. IInstallMode mode = Installer.getDefault().getInstallManager().getInstallMode(); if (mode.isUpdate()) { // Include all repositories or only local int flags = Installer.getDefault().getInstallManager().getInstallDescription().getIncludeAllRepositories() ? IRepositoryManager.REPOSITORIES_ALL : IRepositoryManager.REPOSITORIES_LOCAL; // Installed meta-data repositories IMetadataRepositoryManager metadataManager = (IMetadataRepositoryManager)getAgent().getService(IMetadataRepositoryManager.SERVICE_NAME); URI[] installedMetadataLocations = metadataManager.getKnownRepositories(flags); metadataRepositories.addAll(Arrays.asList(installedMetadataLocations)); // Installed artifact repositories IArtifactRepositoryManager artifactManager = (IArtifactRepositoryManager)agent.getService(IArtifactRepositoryManager.SERVICE_NAME); URI[] installedArtifactLocations = artifactManager.getKnownRepositories(flags); artifactRepositories.addAll(Arrays.asList(installedArtifactLocations)); } if (!metadataRepositories.isEmpty()) { context.setMetadataRepositories(metadataRepositories.toArray(new URI[metadataRepositories.size()])); } if (!artifactRepositories.isEmpty()) { context.setArtifactRepositories(artifactRepositories.toArray(new URI[artifactRepositories.size()])); } return context; } /** * Computes the install plan. * * @param monitor Progress monitor or <code>null</code> * @return Install plan or <code>null</code> if canceled. */ public IInstallPlan computeInstallPlan(IProgressMonitor monitor) { IInstallPlan installPlan = null; try { if (monitor == null) monitor = new NullProgressMonitor(); SubMonitor mon = SubMonitor.convert(monitor, 600); // Get the install units to add or remove ArrayList<IInstallableUnit> unitsToAdd = new ArrayList<IInstallableUnit>(); ArrayList<IInstallableUnit> unitsToRemove = new ArrayList<IInstallableUnit>(); getInstallUnits(unitsToAdd, unitsToRemove); // Nothing to do if (unitsToAdd.isEmpty() && unitsToRemove.isEmpty()) { return new InstallPlan(Installer.getDefault().getInstallManager().getInstallLocation(), Status.OK_STATUS, 0, 0); } // Return cached install plan if available. final String hash = getComponentsHash(); installPlan = planCache.get(hash); if (installPlan != null) { return installPlan; } mon.worked(100); if (mon.isCanceled()) return null; if ((getAgent() == null) || (getInstallLocation() == null)) return null; IProfile profile = getInstallProfile(); IPlanner planner = (IPlanner)agent.getService(IPlanner.SERVICE_NAME); IEngine engine = (IEngine)agent.getService(IEngine.SERVICE_NAME); IProfileChangeRequest request = planner.createChangeRequest(profile); request.addAll(unitsToAdd); request.removeAll(unitsToRemove); IProvisioningPlan plan = planner.getProvisioningPlan(request, getProvisioningContext(), mon.newChild(300)); IStatus status = plan.getStatus(); // Problem computing plan if (!status.isOK()) { StringBuilder buffer = new StringBuilder(); buffer.append(status.getMessage()); buffer.append('\n'); IStatus[] children = status.getChildren(); for (IStatus child : children) { buffer.append(child.getMessage()); buffer.append('\n'); } Installer.log(buffer.toString()); } if (mon.isCanceled()) return null; long installPlanSize = 0; long installPlanDownloadSize = 0; if (plan.getInstallerPlan() != null) { ISizingPhaseSet sizingPhaseSet = PhaseSetFactory.createSizingPhaseSet(); engine.perform(plan.getInstallerPlan(), sizingPhaseSet, mon.newChild(100)); installPlanSize = sizingPhaseSet.getDiskSize(); installPlanDownloadSize = sizingPhaseSet.getDownloadSize(); } else { mon.worked(100); } if (mon.isCanceled()) return null; ISizingPhaseSet sizingPhaseSet = PhaseSetFactory.createSizingPhaseSet(); engine.perform(plan, sizingPhaseSet, mon.newChild(100)); long installSize = installPlanSize + sizingPhaseSet.getDiskSize() + getUninstallerSize(); long requiredSize = installSize + installPlanDownloadSize + sizingPhaseSet.getDownloadSize(); installPlan = new InstallPlan( Installer.getDefault().getInstallManager().getInstallLocation(), status, installSize, requiredSize); planCache.put(hash, installPlan); } catch (Exception e) { monitor.setCanceled(true); Installer.log(e); } return installPlan; } /** * Returns an install component. * * @param id Install component root identifier * @return Install component or <code>null</code> */ public IInstallComponent getInstallComponent(String id) { IInstallComponent foundComponent = null; for (IInstallComponent component : components) { if (component.getInstallUnit().getId().equals(id)) { foundComponent = component; break; } } return foundComponent; } /** * Fires a repository status notification. * * @param status Status */ void fireRepositoryStatus(IInstallRepositoryListener.RepositoryStatus status) { Object[] listeners = repositoryListeners.getListeners(); for (Object listener : listeners) { try { ((IInstallRepositoryListener)listener).repositoryStatus(status); } catch (Exception e) { e.printStackTrace(); } } } /** * Fires components changed notification. */ void fireComponentsChanged() { Object[] listeners = repositoryListeners.getListeners(); for (Object listener : listeners) { try { ((IInstallRepositoryListener)listener).installComponentsChanged(); } catch (Exception e) { e.printStackTrace(); } } } /** * Fires a repository loading error. * * @param location Repository location * @param errorMessage Error information */ void fireRepositoryError(URI location, String errorMessage) { Object[] listeners = repositoryListeners.getListeners(); for (Object listener : listeners) { try { ((IInstallRepositoryListener)listener).repositoryError(location, errorMessage); } catch (Exception e) { e.printStackTrace(); } } } /** * Fires a component changed notification. * * @param component Install component */ void fireComponentChanged(IInstallComponent component) { Object[] listeners = repositoryListeners.getListeners(); for (Object listener : listeners) { try { ((IInstallRepositoryListener)listener).installComponentChanged(component); } catch (Exception e) { e.printStackTrace(); } } } /** * Returns the size of the uninstaller. If the uninstaller size computation * is still in progress, this method waits. * * @return Uninstaller size in bytes */ private long getUninstallerSize() { // Wait for uninstaller thread if running if (uninstallerSizeThread != null) { try { uninstallerSizeThread.join(); } catch (InterruptedException e) { // Ignore } } return uninstallerSize; } /** * Computes the size of the uninstaller. */ protected void initializeUninstallerSize() { IInstallMode mode = Installer.getDefault().getInstallManager().getInstallMode(); // If installing and not an update if (mode.isInstall() && !mode.isUpdate()) { if (uninstallerSizeThread == null) { uninstallerSizeThread = new Thread() { @Override public void run() { String [] uninstallFiles = Installer.getDefault().getInstallManager().getInstallDescription().getUninstallFiles(); long totalSize = 0; if (uninstallFiles != null) { for (String uninstallFilePath : uninstallFiles) { class SizeCalculationVisitor extends SimpleFileVisitor<Path> { private long size = 0; @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { size += file.toFile().length(); return FileVisitResult.CONTINUE; } public long getSize() { return size; } } try { File srcFile = Installer.getDefault().getInstallFile(uninstallFilePath); if (srcFile != null && srcFile.exists()) { SizeCalculationVisitor visitor = new SizeCalculationVisitor(); Files.walkFileTree(srcFile.toPath(), visitor); totalSize += visitor.getSize(); } } catch (Exception e) { Installer.log(e); } } } uninstallerSize = totalSize; } }; uninstallerSizeThread.start(); } } // Else not uninstaller else { uninstallerSize = 0; } } /** * Generate a string hash from the install components. * @return hash of root list */ private String getComponentsHash() { StringBuilder hash = new StringBuilder(); for (IInstallComponent component : getInstallComponents(false)) { if (component.getInstall()) { IInstallableUnit unit = component.getInstallUnit(); if (unit != null) { hash.append(unit.getId()); } } } return hash.toString(); } /** * Provisions Installable Units into a system and/or saves to the cache mirror. * * @param profile Profile for provision * @param toAdd Units to add or <code>null</code>. * @param toRemove Units to remove or <code>null</code>. * @param clearDownloadCache <code>true</code> to remove the download cache. * @param progressMonitor Progress monitor. * @throws CoreException on provisioning failure. */ public void provision(IProfile profile, List<IInstallableUnit> toAdd, List<IInstallableUnit> toRemove, boolean clearDownloadCache, IProgressMonitor progressMonitor) throws CoreException { final int WORK_SEGMENT = 100; int totalWork = 0; // Update cache work if (getUpdateCache()) { totalWork += WORK_SEGMENT; } // Install work if (getInstallLocation() != null) { totalWork += WORK_SEGMENT; } ProvisioningProgressMonitor monitor = new ProvisioningProgressMonitor(progressMonitor); monitor.beginTask("", totalWork); try { // Update cache repository if (getUpdateCache()) { monitor.setShowRemainingTime(true); updateCacheRepository(toAdd, new SubProgressMonitor(monitor, WORK_SEGMENT)); monitor.setShowRemainingTime(false); } // Install from the cache repository if (getCacheOnly() || getUpdateCache()) { unloadInstallRepositories(); } // Performing an installation File if (getInstallLocation() != null) { // Reload the cache repository try { loadCacheRepository(); } catch (Exception e) { Installer.log(e); } // Get the planner IPlanner planner = (IPlanner)agent.getService(IPlanner.SERVICE_NAME); // Provisioning context ProvisioningContext context = getProvisioningContext(); // Provisioning request IProfileChangeRequest request = planner.createChangeRequest(profile); // Units to add if ((toAdd != null) && !toAdd.isEmpty()) request.addAll(toAdd); // Units to remove if ((toRemove != null) && !toRemove.isEmpty()) request.removeAll(toRemove); // When creating a product root IU, all component IU's will be provisioned as (non-root) requirements // of the product IU. There can be a case where these component IU's were previously provisioned as // root IU's. This can happen for an upgrade if the release uses eclipse.p2.product.root=true, but the // previous version used eclipse.p2.product.root=false. // To handle this, the root property is set to false for all members of the product root IU. if (getProductRepository() != null) { try { RepositoryManagerAdapter repositoryManager = new RepositoryManagerAdapter(getMetadataRepositoryManager()); for (IInstallableUnit unitToAdd : toAdd) { ArrayList<IInstallableUnit> members = new ArrayList<IInstallableUnit>(); repositoryManager.findMemberUnits(unitToAdd, members); for (IInstallableUnit member : members) { request.setInstallableUnitProfileProperty(member, IProfile.PROP_PROFILE_ROOT_IU, Boolean.FALSE.toString()); } } } catch (Exception e) { // Don't fail the operation Installer.log(e); } } // Logging Installer.log("(Provision) Adding:"); if ((toAdd == null) || (toAdd.size() == 0)) { Installer.log("\tNone"); } else { for (IInstallableUnit unit : toAdd) { Installer.log("\t" + unit.getId() + ":" + unit.getVersion().toString()); } } Installer.log("(Provision) Removing:"); if ((toRemove == null) || (toRemove.size() == 0)) { Installer.log("\tNone"); } else { for (IInstallableUnit unit : toRemove) { Installer.log("\t" + unit.getId() + ":" + unit.getVersion().toString()); } } // Create provisioning progress monitor IProgressMonitor provisioningMonitor = new SubProgressMonitor(monitor, WORK_SEGMENT); IInstallDescription desc = Installer.getDefault().getInstallManager().getInstallDescription(); if (desc != null) { monitor.setFilter(desc.getProgressFindPatterns(), desc.getProgressReplacePatterns()); } // Provision operation IDirector director = (IDirector)agent.getService(IDirector.SERVICE_NAME); IStatus status = director.provision(request, context, provisioningMonitor); if ((status != null) && (status.getSeverity() == IStatus.ERROR)) throw new CoreException(status); if ((toAdd != null) && !toAdd.isEmpty()) { // Clear download cache if (clearDownloadCache) { try { IFileArtifactRepository cacheRepo = Util.getDownloadCacheRepo(agent); cacheRepo.removeAll(monitor); } catch (Exception e) { // Just log any failure to clear the cache as it is // non-critical to the installation. Installer.log(e); } } } } } finally { monitor.done(); } } /** * Returns members of a category IU. If the IU corresponds to an install component, all members collected from * category meta-data from all repositories will be returned. If no install component is available, the meta-data * repository manager will be queried for the members. * * @param categoryIu Category IU * @return Category member IU's */ private List<IInstallableUnit> getCategoryMembers(IInstallableUnit categoryIu) { ArrayList<IInstallableUnit> units = new ArrayList<IInstallableUnit>(); IInstallComponent categoryComponent = getInstallComponent(categoryIu.getId()); if (categoryComponent != null) { IInstallComponent[] members = categoryComponent.getMembers(); for (IInstallComponent member : members) { units.add(member.getInstallUnit()); } } else { IQuery<IInstallableUnit> categoryQuery = QueryUtil.createIUCategoryMemberQuery(categoryIu); IQueryResult<IInstallableUnit> query = getMetadataRepositoryManager().query(categoryQuery, null); Iterator<IInstallableUnit> iter = query.iterator(); while (iter.hasNext()) { IInstallableUnit member = iter.next(); units.add(member); } } return units; } /** * Returns the repository mirror application. * * @param units Installable units * @param monitor Progress monitor * @return Mirror application * @throws ProvisionException on failure */ private InstallerMirrorApplication getMirrorApplication(List<IInstallableUnit> units, IProgressMonitor monitor) throws ProvisionException { // Create the mirror application String progressText = Installer.getDefault().getInstallManager().getInstallDescription().getText(IInstallDescription.TEXT_PROGRESS_MIRRORING, InstallMessages.Progress_Saving); InstallerMirrorApplication mirrorApp = new InstallerMirrorApplication(agent, monitor, progressText); // No base-line comparison mirrorApp.setCompare(false); // Set source meta-data repositories for (URI location : getMetadataRepositoryLocations()) { RepositoryDescriptor source = new RepositoryDescriptor(); source.setLocation(location); source.setKind(RepositoryDescriptor.KIND_METADATA); source.setOptional(false); mirrorApp.addSource(source); } // Set source artifact repositories for (URI location : getArtifactRepositoryLocations()) { RepositoryDescriptor source = new RepositoryDescriptor(); source.setLocation(location); source.setKind(RepositoryDescriptor.KIND_ARTIFACT); source.setOptional(false); mirrorApp.addSource(source); } File cacheFile = getCacheLocation().toFile(); // Set mirror destination RepositoryDescriptor dest = new RepositoryDescriptor(); // Note, appending does not seem to work correctly dest.setAppend(false); dest.setLocation(cacheFile.toURI()); dest.setName(Installer.getDefault().getInstallManager().getInstallDescription().getProductName()); mirrorApp.addDestination(dest); mirrorApp.initializeRepos(null); // Set the installable units to mirror into the cache repository mirrorApp.setSourceIUs(units); // Abort on errors mirrorApp.setIgnoreErrors(false); return mirrorApp; } /** * Returns the size of the cache download. * * @param monitor Progress monitor * @return Cache size in bytes */ public InstallPlan computeCacheSize(IProgressMonitor monitor) { try { ArrayList<IInstallableUnit> unitsToAdd = new ArrayList<IInstallableUnit>(); ArrayList<IInstallableUnit> unitsToRemove = new ArrayList<IInstallableUnit>(); getInstallUnits(unitsToAdd, unitsToRemove); InstallerMirrorApplication mirrorApp = getMirrorApplication(unitsToAdd, monitor); return new InstallPlan(getCacheLocation(), mirrorApp.getDownloadSize()); } catch (Exception e) { Installer.log(e); return null; } } /** * Updates the local cache repository from the install repositories. * * @param toAdd Installable units to add to the cache. * @param monitor Installable units to remove from the cache. * @throws CoreException on failure to update the cache repository. */ private void updateCacheRepository(List<IInstallableUnit> toAdd, IProgressMonitor monitor) throws CoreException { try { // Non-category IU's ArrayList<IInstallableUnit> units = new ArrayList<IInstallableUnit>(); // Category IU's ArrayList<IInstallableUnit> categoryUnits = new ArrayList<IInstallableUnit>(); // Separate the IU's as the category IU's will be mirrored separately for (IInstallableUnit unit : toAdd) { // Category unit if (isCategoryIu(unit)) { categoryUnits.add(unit); } // Non-category unit else { units.add(unit); } } IPath cacheLocation = getCacheLocation(); // No cache location if (cacheLocation == null) return; // Get the mirror application InstallerMirrorApplication mirrorApp = this.getMirrorApplication(units, monitor); // Create the cache directory File cacheFile = getCacheLocation().toFile(); if (!cacheFile.exists()) { Files.createDirectories(cacheFile.toPath()); } // Perform the mirror IStatus status = mirrorApp.run(monitor); if (status.getSeverity() == IStatus.ERROR) { Installer.fail(InstallMessages.Error_UpdateCache); } // The normal P2 mirror operation will not mirror a category IU that is defined in multiple source // repositories with different requirements. Attempting to slice this type of IU will result in the // meta-data for the IU only containing requirements from one repository. To avoid this, the category IU's // are re-created in the destination repository. if (categoryUnits.size() > 0) { ArrayList<IInstallableUnit> categoryUnitsToAdd = new ArrayList<IInstallableUnit>(); for (IInstallableUnit unit : categoryUnits) { // Create IU description for category InstallableUnitDescription iuDesc = InstallUtils.createIuDescription( unit.getId(), unit.getVersion(), unit.isSingleton(), unit.getProperties()); // Create requirements for category member IU's List<IInstallableUnit> members = getCategoryMembers(unit); InstallUtils.addInstallableUnitRequirements(iuDesc, members, false); // Create category IU and add it to repository IInstallableUnit categoryIu = MetadataFactory.createInstallableUnit(iuDesc); categoryUnitsToAdd.add(categoryIu); } try { loadCacheRepository(); cacheMetadataRepository.addInstallableUnits(categoryUnitsToAdd); } catch (Exception e) { Installer.log(e); } } } catch (Exception e) { Installer.fail(e); } } /** * Returns the name for an installable unit. * * @param id IU identifier. * @return IU name or <code>null</code> if the IU is not found. */ public String queryIuName(String id) { IQuery<IInstallableUnit> query = QueryUtil.createIUQuery(id); IQueryResult<IInstallableUnit> result = getMetadataRepositoryManager().query(query, null); Iterator<IInstallableUnit> iter = result.iterator(); while (iter.hasNext()) { IInstallableUnit unit = iter.next(); return unit.getProperty(IInstallableUnit.PROP_NAME, null); } return null; } }