blob: 85162fa9a9e18e60f91034a0cabd123f1f6bd2db [file] [log] [blame]
/*******************************************************************************
* 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;
}
}