/******************************************************************************* * Copyright (c) 2007, 2017 IBM Corporation 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: * IBM Corporation - initial API and implementation * Ericsson AB - Bug 400011 - [shared] Cleanup the SurrogateProfileHandler code * Red Hat, Inc. - fragments support added., Bug 460967 *******************************************************************************/ package org.eclipse.equinox.internal.p2.engine; import java.io.File; import java.lang.ref.SoftReference; import java.net.*; import java.util.*; import org.eclipse.core.runtime.*; import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.engine.IProfile; import org.eclipse.equinox.p2.metadata.*; import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription; import org.eclipse.equinox.p2.metadata.expression.ExpressionUtil; import org.eclipse.equinox.p2.metadata.expression.IMatchExpression; import org.eclipse.equinox.p2.query.*; import org.eclipse.equinox.p2.repository.IRepositoryManager; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; import org.eclipse.osgi.service.datalocation.Location; import org.eclipse.osgi.util.NLS; public class SurrogateProfileHandler implements ISurrogateProfileHandler { private static final String NATIVE_TOUCHPOINT_TYPE = "org.eclipse.equinox.p2.native"; //$NON-NLS-1$ private static final String P2_ENGINE_DIR = "p2/" + EngineActivator.ID + "/"; //$NON-NLS-1$//$NON-NLS-2$ private static final String OSGI_INSTALL_AREA = "osgi.install.area"; //$NON-NLS-1$ private static final String ECLIPSE_INI_IGNORED = "eclipse.ini.ignored"; //$NON-NLS-1$ private static final String IU_LOCKED = Integer.toString(IProfile.LOCK_UNINSTALL | IProfile.LOCK_UPDATE); private static final String PROP_SURROGATE = "org.eclipse.equinox.p2.surrogate"; //$NON-NLS-1$ private static final String PROP_BASE = "org.eclipse.equinox.p2.base"; //$NON-NLS-1$ private static final String STRICT = "STRICT"; //$NON-NLS-1$ private static final String PROP_INCLUSION_RULES = "org.eclipse.equinox.p2.internal.inclusion.rules"; //$NON-NLS-1$ private final IProvisioningAgent agent; private SimpleProfileRegistry profileRegistry; private SoftReference<IProfile> cachedProfile; private static void addSharedProfileBaseIUs(final IProfile sharedProfile, final Profile userProfile) { IQuery<IInstallableUnit> rootIUQuery = QueryUtil.createMatchQuery( // "profileProperties[$0] == 'true' || (touchpointType != null && touchpointType.id == $1)", //$NON-NLS-1$ IProfile.PROP_PROFILE_ROOT_IU, NATIVE_TOUCHPOINT_TYPE); IQueryResult<IInstallableUnit> rootIUs = sharedProfile.query(rootIUQuery, null); for (Iterator<IInstallableUnit> iterator = rootIUs.iterator(); iterator.hasNext();) { IInstallableUnit iu = iterator.next(); userProfile.addInstallableUnit(iu); userProfile.addInstallableUnitProperties(iu, sharedProfile.getInstallableUnitProperties(iu)); userProfile.setInstallableUnitProperty(iu, IProfile.PROP_PROFILE_LOCKED_IU, IU_LOCKED); userProfile.setInstallableUnitProperty(iu, PROP_BASE, Boolean.TRUE.toString()); } IInstallableUnit sharedProfileIU = createSharedProfileIU(sharedProfile); userProfile.addInstallableUnit(sharedProfileIU); userProfile.setInstallableUnitProperty(sharedProfileIU, PROP_INCLUSION_RULES, STRICT); userProfile.setInstallableUnitProperty(sharedProfileIU, PROP_BASE, Boolean.TRUE.toString()); } private static IInstallableUnit createSharedProfileIU(final IProfile sharedProfile) { InstallableUnitDescription iuDescription = new InstallableUnitDescription(); iuDescription.setId("SharedProfile_" + sharedProfile.getProfileId()); //$NON-NLS-1$ iuDescription.setVersion(Version.createOSGi(1, 0, 0, Long.toString(sharedProfile.getTimestamp()))); ArrayList<IProvidedCapability> iuCapabilities = new ArrayList<>(); IProvidedCapability selfCapability = MetadataFactory.createProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, iuDescription.getId(), iuDescription.getVersion()); iuCapabilities.add(selfCapability); iuDescription.addProvidedCapabilities(iuCapabilities); ArrayList<IRequirement> iuRequirements = new ArrayList<>(); IQueryResult<IInstallableUnit> allIUs = sharedProfile.query(QueryUtil.createIUAnyQuery(), null); for (Iterator<IInstallableUnit> iterator = allIUs.iterator(); iterator.hasNext();) { IInstallableUnit iu = iterator.next(); IMatchExpression<IInstallableUnit> iuMatcher = ExpressionUtil.getFactory().<IInstallableUnit> matchExpression(ExpressionUtil.parse("id == $0 && version == $1"), iu.getId(), iu.getVersion()); //$NON-NLS-1$ iuRequirements.add(MetadataFactory.createRequirement(iuMatcher, null, 0, 1, true)); } iuDescription.addRequirements(iuRequirements); iuDescription.setProperty(IInstallableUnit.PROP_NAME, NLS.bind(Messages.Shared_Profile, null)); IInstallableUnit sharedProfileIU = MetadataFactory.createInstallableUnit(iuDescription); return sharedProfileIU; } private static void updateProperties(final IProfile sharedProfile, Profile userProfile) { Location installLocation = ServiceHelper.getService(EngineActivator.getContext(), Location.class, Location.INSTALL_FILTER); File installFolder = new File(installLocation.getURL().getPath()); if (Boolean.parseBoolean(sharedProfile.getProperty(IProfile.PROP_ROAMING))) { userProfile.setProperty(IProfile.PROP_INSTALL_FOLDER, installFolder.getAbsolutePath()); userProfile.setProperty(IProfile.PROP_SHARED_CACHE, installFolder.getAbsolutePath()); userProfile.setProperty(IProfile.PROP_ROAMING, Boolean.FALSE.toString()); } else { String cache = sharedProfile.getProperty(IProfile.PROP_CACHE); if (cache != null) userProfile.setProperty(IProfile.PROP_SHARED_CACHE, cache); } Location configurationLocation = ServiceHelper.getService(EngineActivator.getContext(), Location.class, Location.CONFIGURATION_FILTER); File configurationFolder = new File(configurationLocation.getURL().getPath()); userProfile.setProperty(IProfile.PROP_CONFIGURATION_FOLDER, configurationFolder.getAbsolutePath()); // We need to check that the configuration folder is not a file system root. // some of the profiles resources are stored as siblings to the configuration folder. // also see bug 230384 if (configurationFolder.getParentFile() == null) throw new IllegalArgumentException("Configuration folder must not be a file system root."); //$NON-NLS-1$ userProfile.setProperty(IProfile.PROP_CACHE, configurationFolder.getParentFile().getAbsolutePath()); File launcherConfigFile = new File(configurationFolder, ECLIPSE_INI_IGNORED); userProfile.setProperty(IProfile.PROP_LAUNCHER_CONFIGURATION, launcherConfigFile.getAbsolutePath()); } public SurrogateProfileHandler(IProvisioningAgent agent) { this.agent = agent; } private synchronized SimpleProfileRegistry getProfileRegistry() { if (profileRegistry == null) { String installArea = EngineActivator.getContext().getProperty(OSGI_INSTALL_AREA); try { URL registryURL = new URL(installArea + P2_ENGINE_DIR + SimpleProfileRegistry.DEFAULT_STORAGE_DIR); File sharedRegistryDirectory = URIUtil.toFile(URIUtil.toURI(registryURL)); profileRegistry = new SimpleProfileRegistry(agent, sharedRegistryDirectory, null, false); } catch (MalformedURLException e) { //this is not possible because we know the above URL is valid } catch (URISyntaxException e) { //this is not possible because we know the above URL is valid } } return profileRegistry; } // this method must not try to lock the profile registry private IProfile getSharedProfile(String id) { SimpleProfileRegistry registry = getProfileRegistry(); long[] timestamps = registry.listProfileTimestamps(id); if (timestamps.length == 0) return null; long currentTimestamp = timestamps[timestamps.length - 1]; //see if we have a cached profile if (cachedProfile != null) { IProfile profile = cachedProfile.get(); if (profile != null && profile.getProfileId().equals(id) && profile.getTimestamp() == currentTimestamp) return profile; } final Profile profile = (Profile) registry.getProfile(id, currentTimestamp); if (profile != null) cachedProfile = new SoftReference<IProfile>(profile); if (!EngineActivator.EXTENDED) { return profile; } setUpRepos(); return profile; } /** * Removes repositories from fragments locations as they might be obsolete and adds them back. */ private void setUpRepos() { //clean old junk IMetadataRepositoryManager metaManager = (IMetadataRepositoryManager) agent.getService(IMetadataRepositoryManager.SERVICE_NAME); URI[] knownRepositories = metaManager.getKnownRepositories(IRepositoryManager.REPOSITORIES_LOCAL); for (URI uri : knownRepositories) { if ("true".equals(metaManager.getRepositoryProperty(uri, EngineActivator.P2_FRAGMENT_PROPERTY))) { //$NON-NLS-1$ metaManager.removeRepository(uri); } } IArtifactRepositoryManager artifactManager = (IArtifactRepositoryManager) agent.getService(IArtifactRepositoryManager.SERVICE_NAME); knownRepositories = artifactManager.getKnownRepositories(IRepositoryManager.REPOSITORIES_LOCAL); for (URI uri : knownRepositories) { if ("true".equals(artifactManager.getRepositoryProperty(uri, EngineActivator.P2_FRAGMENT_PROPERTY))) { //$NON-NLS-1$ artifactManager.removeRepository(uri); } } File[] fragments = EngineActivator.getExtensionsDirectories(); for (File f : fragments) { metaManager.addRepository(f.toURI()); metaManager.setRepositoryProperty(f.toURI(), EngineActivator.P2_FRAGMENT_PROPERTY, Boolean.TRUE.toString()); artifactManager.addRepository(f.toURI()); artifactManager.setRepositoryProperty(f.toURI(), EngineActivator.P2_FRAGMENT_PROPERTY, Boolean.TRUE.toString()); } } @Override public IProfile createProfile(String id) { final Profile sharedProfile = (Profile) getSharedProfile(id); if (sharedProfile == null) return null; if (!EngineActivator.EXTENDED) { Profile userProfile = new Profile(agent, id, null, sharedProfile.getProperties()); userProfile.setProperty(PROP_SURROGATE, Boolean.TRUE.toString()); userProfile.setSurrogateProfileHandler(this); updateProperties(sharedProfile, userProfile); addSharedProfileBaseIUs(sharedProfile, userProfile); return userProfile; } File[] extensionLocations = EngineActivator.getExtensionsDirectories(); Set<IInstallableUnit> added = new HashSet<>(); for (File extension : extensionLocations) { try { IMetadataRepositoryManager metaManager = (IMetadataRepositoryManager) agent.getService(IMetadataRepositoryManager.SERVICE_NAME); IMetadataRepository repo = metaManager.loadRepository(extension.toURI(), new NullProgressMonitor()); Set<IInstallableUnit> installableUnits = repo.query(QueryUtil.createIUAnyQuery(), new NullProgressMonitor()).toUnmodifiableSet(); for (IInstallableUnit unit : installableUnits) { Collection<IProvidedCapability> capabilities = unit.getProvidedCapabilities(); boolean featureOrBundle = false; for (IProvidedCapability cap : capabilities) { if ("org.eclipse.equinox.p2.eclipse.type".equals(cap.getNamespace())) { //$NON-NLS-1$ if ("bundle".equals(cap.getName()) //$NON-NLS-1$ || "source".equals(cap.getName()) //$NON-NLS-1$ || "feature".equals(cap.getName())) { //$NON-NLS-1$ featureOrBundle = true; } } else if (Boolean.TRUE.equals(Boolean.valueOf(unit.getProperties().get("org.eclipse.equinox.p2.type.group")))) { //$NON-NLS-1$ featureOrBundle = true; } } if (featureOrBundle && !added.contains(unit)) { added.add(unit); sharedProfile.addInstallableUnit(unit); } Map<String, String> iuProperties = unit.getProperties(); if (iuProperties != null && !iuProperties.isEmpty()) { sharedProfile.addInstallableUnitProperties(unit, iuProperties); } } } catch (ProvisionException e) { LogHelper.log(new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.SurrogateProfileHandler_1, extension), e)); } } Profile userProfile = new Profile(agent, id, null, sharedProfile.getProperties()); userProfile.setProperty(PROP_SURROGATE, Boolean.TRUE.toString()); userProfile.setSurrogateProfileHandler(this); updateProperties(sharedProfile, userProfile); addSharedProfileBaseIUs(sharedProfile, userProfile); return userProfile; } @Override public boolean isSurrogate(IProfile profile) { return Boolean.parseBoolean(profile.getProperty(PROP_SURROGATE)); } @Override public IQueryResult<IInstallableUnit> queryProfile(IProfile profile, IQuery<IInstallableUnit> query, IProgressMonitor monitor) { IProfile sharedProfile = getSharedProfile(profile.getProfileId()); if (sharedProfile == null) return profile.query(query, monitor); // TODO: Should consider using a sequenced iterator here instead of collecting Collector<IInstallableUnit> result = new Collector<>(); result.addAll(sharedProfile.query(query, monitor)); result.addAll(profile.query(query, monitor)); return result; } }