blob: 9b39c28e3122f68a5477d3929f9de8e0436c9391 [file] [log] [blame]
/*
* Copyright (c) 2014, 2015 Eike Stepper (Berlin, Germany) 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:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.p2.internal.core;
import org.eclipse.oomph.p2.P2Exception;
import org.eclipse.oomph.p2.ProfileDefinition;
import org.eclipse.oomph.p2.Repository;
import org.eclipse.oomph.p2.Requirement;
import org.eclipse.oomph.p2.core.Agent;
import org.eclipse.oomph.p2.core.BundlePool;
import org.eclipse.oomph.p2.core.P2Util;
import org.eclipse.oomph.p2.core.Profile;
import org.eclipse.oomph.p2.core.ProfileTransaction;
import org.eclipse.oomph.p2.core.ProfileTransaction.CommitContext.DeltaType;
import org.eclipse.oomph.p2.core.ProfileTransaction.CommitContext.ResolutionInfo;
import org.eclipse.oomph.util.Confirmer;
import org.eclipse.oomph.util.Confirmer.Confirmation;
import org.eclipse.oomph.util.ObjectUtil;
import org.eclipse.oomph.util.Pair;
import org.eclipse.oomph.util.PropertiesUtil;
import org.eclipse.oomph.util.ReflectUtil;
import org.eclipse.oomph.util.WorkerPool;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.EcoreUtil.EqualityHelper;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.ProgressMonitorWrapper;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository;
import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest;
import org.eclipse.equinox.internal.p2.director.SimplePlanner;
import org.eclipse.equinox.internal.p2.engine.InstallableUnitOperand;
import org.eclipse.equinox.internal.p2.engine.InstallableUnitPropertyOperand;
import org.eclipse.equinox.internal.p2.engine.Operand;
import org.eclipse.equinox.internal.p2.engine.PropertyOperand;
import org.eclipse.equinox.internal.p2.engine.ProvisioningPlan;
import org.eclipse.equinox.internal.p2.metadata.IRequiredCapability;
import org.eclipse.equinox.internal.p2.touchpoint.natives.BackupStore;
import org.eclipse.equinox.internal.p2.touchpoint.natives.IBackupStore;
import org.eclipse.equinox.internal.p2.touchpoint.natives.NativeTouchpoint;
import org.eclipse.equinox.internal.provisional.p2.director.PlanExecutionHelper;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
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.IPhaseSet;
import org.eclipse.equinox.p2.engine.IProfile;
import org.eclipse.equinox.p2.engine.IProvisioningPlan;
import org.eclipse.equinox.p2.engine.ProvisioningContext;
import org.eclipse.equinox.p2.engine.query.UserVisibleRootQuery;
import org.eclipse.equinox.p2.metadata.IArtifactKey;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.IProvidedCapability;
import org.eclipse.equinox.p2.metadata.IRequirement;
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.VersionRange;
import org.eclipse.equinox.p2.metadata.expression.IMatchExpression;
import org.eclipse.equinox.p2.planner.IPlanner;
import org.eclipse.equinox.p2.planner.IProfileChangeRequest;
import org.eclipse.equinox.p2.planner.ProfileInclusionRules;
import org.eclipse.equinox.p2.query.IQuery;
import org.eclipse.equinox.p2.query.IQueryResult;
import org.eclipse.equinox.p2.query.IQueryable;
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.osgi.service.datalocation.Location;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URI;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Eike Stepper
*/
@SuppressWarnings("restriction")
public class ProfileTransactionImpl implements ProfileTransaction
{
public static final String PROP_ADDITIONAL_POOLS = "oomph.p2.additional.pools";
public static final String ARTIFICIAL_ROOT_ID = "artificial_root";
private static final String OSGI_RESOLVER_USES_MODE = "osgi.resolver.usesMode";
private static final String SOURCE_IU_ID = "org.eclipse.oomph.p2.source.container"; //$NON-NLS-1$
private static final IRequirement BUNDLE_REQUIREMENT = MetadataFactory.createRequirement("org.eclipse.equinox.p2.eclipse.type", "bundle", null, null, false, //$NON-NLS-1$ //$NON-NLS-2$
false, false);
private static final Set<String> IMMUTABLE_PROPERTIES = new HashSet<String>(Arrays.asList(Profile.PROP_INSTALL_FEATURES, Profile.PROP_INSTALL_FOLDER,
Profile.PROP_CACHE, Profile.PROP_PROFILE_TYPE, Profile.PROP_PROFILE_DEFINITION));
private final Profile profile;
private final ProfileDefinition profileDefinition;
private final ProfileDefinition cleanProfileDefinition;
private final Map<String, String> profileProperties = new HashMap<String, String>();
private final Map<String, String> cleanProfileProperties = new HashMap<String, String>();
private final Map<IUPropertyKey, String> iuProperties = new HashMap<IUPropertyKey, String>();
private final Map<IUPropertyKey, String> cleanIUProperties = new HashMap<IUPropertyKey, String>();
private boolean removeAll;
private boolean mirrors;
private boolean committed;
public ProfileTransactionImpl(Profile profile)
{
this.profile = profile;
cleanProfileDefinition = profile.getDefinition();
profileDefinition = EcoreUtil.copy(cleanProfileDefinition);
cleanProfileProperties.putAll(profile.getProperties());
cleanProfileProperties.remove(Profile.PROP_INSTALL_FOLDER);
cleanProfileProperties.remove(Profile.PROP_CACHE);
cleanProfileProperties.remove(Profile.PROP_PROFILE_DEFINITION);
profileProperties.putAll(cleanProfileProperties);
for (IInstallableUnit iu : P2Util.asIterable(profile.query(QueryUtil.createIUAnyQuery(), new NullProgressMonitor())))
{
Map<String, String> properties = profile.getInstallableUnitProperties(iu);
if (!properties.isEmpty())
{
for (Map.Entry<String, String> property : properties.entrySet())
{
String key = property.getKey();
String value = property.getValue();
cleanIUProperties.put(new IUPropertyKey(iu, key), value);
}
}
}
iuProperties.putAll(cleanIUProperties);
mirrors = SimpleArtifactRepository.MIRRORS_ENABLED;
}
public Profile getProfile()
{
return profile;
}
public ProfileDefinition getProfileDefinition()
{
return profileDefinition;
}
public String getProfileProperty(String key)
{
return profileProperties.get(key);
}
public ProfileTransaction setProfileProperty(String key, String value)
{
if (IMMUTABLE_PROPERTIES.contains(key) && !ObjectUtil.equals(profileProperties.get(key), value))
{
throw new IllegalArgumentException("Property is immutable: " + key);
}
if (value != null)
{
profileProperties.put(key, value);
}
else
{
profileProperties.remove(key);
}
return this;
}
public ProfileTransaction removeProfileProperty(String key)
{
return setProfileProperty(key, null);
}
public String getInstallableUnitProperty(IInstallableUnit iu, String key)
{
return iuProperties.get(new IUPropertyKey(iu, key));
}
public ProfileTransaction setInstallableUnitProperty(IInstallableUnit iu, String key, String value)
{
IUPropertyKey propertyKey = new IUPropertyKey(iu, key);
if (value != null)
{
iuProperties.put(propertyKey, value);
}
else
{
iuProperties.remove(propertyKey);
}
return this;
}
public ProfileTransaction removeInstallableUnitProperty(IInstallableUnit iu, String key)
{
return setInstallableUnitProperty(iu, key, null);
}
public boolean isRemoveExistingInstallableUnits()
{
return removeAll;
}
public ProfileTransaction setRemoveExistingInstallableUnits(boolean removeAll)
{
this.removeAll = removeAll;
return this;
}
public boolean isMirrors()
{
return mirrors;
}
public ProfileTransaction setMirrors(boolean mirrors)
{
this.mirrors = mirrors;
return this;
}
public boolean isDirty()
{
if (removeAll)
{
return true;
}
if (!profileProperties.equals(cleanProfileProperties))
{
return true;
}
if (!iuProperties.equals(cleanIUProperties))
{
return true;
}
return isProfileDefinitionChanged();
}
private boolean isProfileDefinitionChanged()
{
if (profileDefinition.isIncludeSourceBundles() != cleanProfileDefinition.isIncludeSourceBundles())
{
return true;
}
EqualityHelper equalityHelper = new EqualityHelper();
if (!equals(equalityHelper, profileDefinition.getRequirements(), cleanProfileDefinition.getRequirements()))
{
return true;
}
if (!equals(equalityHelper, profileDefinition.getRepositories(), cleanProfileDefinition.getRepositories()))
{
return true;
}
return false;
}
public boolean commit() throws CoreException
{
return commit(null, null);
}
public boolean commit(IProgressMonitor monitor) throws CoreException
{
return commit(null, monitor);
}
public boolean commit(CommitContext commitContext, IProgressMonitor monitor) throws CoreException
{
if (!committed)
{
committed = true;
monitor.beginTask("", 2);
try
{
Resolution resolution = resolve(commitContext, SubMonitor.convert(monitor, 1));
if (resolution != null)
{
return resolution.commit(SubMonitor.convert(monitor, 1));
}
monitor.worked(1);
}
finally
{
monitor.done();
}
}
return false;
}
public Resolution resolve(IProgressMonitor monitor) throws CoreException
{
return resolve(null, monitor);
}
public Resolution resolve(CommitContext commitContext, IProgressMonitor monitor) throws CoreException
{
final CommitContext context = commitContext == null ? new CommitContext() : commitContext;
if (monitor == null)
{
monitor = new NullProgressMonitor();
}
final Agent agent = profile.getAgent();
final List<Runnable> cleanup = new ArrayList<Runnable>();
boolean includeSourceBundles = profileDefinition.isIncludeSourceBundles();
monitor.beginTask("", includeSourceBundles ? 75 : 70);
try
{
initMirrors(cleanup);
final List<IMetadataRepository> metadataRepositories = new ArrayList<IMetadataRepository>();
Set<URI> artifactURIs = new HashSet<URI>();
URI[] metadataURIs = collectRepositories(metadataRepositories, artifactURIs, cleanup, SubMonitor.convert(monitor, 50));
final ProfileImpl profileImpl = (ProfileImpl)profile;
final IProfile delegate = profileImpl.getDelegate();
final long timestamp = delegate.getTimestamp();
IPlanner planner = agent.getPlanner();
// In org.eclipse.equinox.internal.p2.director.SimplePlanner.getSolutionFor(ProfileChangeRequest, ProvisioningContext, IProgressMonitor)
// The removals will be add the removed IUs to the slicer,
// but we don'ts want to do that for targlets.
boolean isTarglet = Profile.TYPE_TARGLET.equals(profile.getType());
IProfileChangeRequest profileChangeRequest = isTarglet ? new ProfileChangeRequest(delegate)
{
@Override
public Collection<IInstallableUnit> getRemovals()
{
// This inspects the stack to see if this method is being called by getSolutionFor,
// In which case we want to return nothing so these removals aren't considered by the slicer.
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if ("getSolutionFor".equals(stackTrace[2].getMethodName()))
{
return Collections.emptyList();
}
return super.getRemovals();
}
} : planner.createChangeRequest(delegate);
final IInstallableUnit rootIU = adjustProfileChangeRequest(profileChangeRequest, SubMonitor.convert(monitor, 5));
final ProvisioningContext provisioningContext = context.createProvisioningContext(this, profileChangeRequest);
provisioningContext.setMetadataRepositories(metadataURIs);
provisioningContext.setArtifactRepositories(artifactURIs.toArray(new URI[artifactURIs.size()]));
// In org.eclipse.equinox.internal.p2.director.SimplePlanner.getSolutionFor(ProfileChangeRequest, ProvisioningContext, IProgressMonitor)
// it will add all the profile's IUs to the slicer.
// We don't want to do that for targlets, because we want to compute a new target platform without consider what existed before.
if (isTarglet)
{
provisioningContext.setProperty("org.eclipse.equinox.p2.internal.profileius", Boolean.FALSE.toString());
}
IQueryable<IInstallableUnit> metadata = provisioningContext.getMetadata(SubMonitor.convert(monitor, 5));
final IProvisioningPlan provisioningPlan = planner.getProvisioningPlan(profileChangeRequest, provisioningContext, SubMonitor.convert(monitor, 10));
P2CorePlugin.INSTANCE.coreException(provisioningPlan.getStatus());
IQueryable<IInstallableUnit> futureState = provisioningPlan.getFutureState();
for (IRequirement requirement : rootIU.getRequirements())
{
if (requirement instanceof IRequiredCapability)
{
IRequiredCapability requiredCapability = (IRequiredCapability)requirement;
for (IInstallableUnit installableUnit : P2Util
.asIterable(futureState.query(QueryUtil.createIUQuery(requiredCapability.getName(), requiredCapability.getRange()), null)))
{
provisioningPlan.setInstallableUnitProfileProperty(installableUnit, Profile.PROP_PROFILE_ROOT_IU, Boolean.TRUE.toString());
provisioningPlan.setInstallableUnitProfileProperty(installableUnit, SimplePlanner.INCLUSION_RULES,
ProfileInclusionRules.createStrictInclusionRule(installableUnit));
}
}
}
if (includeSourceBundles)
{
IInstallableUnit sourceContainerIU = generateSourceContainerIU(provisioningPlan, metadata, SubMonitor.convert(monitor, 5));
provisioningPlan.addInstallableUnit(sourceContainerIU);
provisioningPlan.setInstallableUnitProfileProperty(sourceContainerIU, Profile.PROP_PROFILE_ROOT_IU, Boolean.TRUE.toString());
}
final Map<IInstallableUnit, CommitContext.DeltaType> iuDeltas = new HashMap<IInstallableUnit, CommitContext.DeltaType>();
final Map<IInstallableUnit, Map<String, Pair<Object, Object>>> propertyDeltas = new HashMap<IInstallableUnit, Map<String, Pair<Object, Object>>>();
computeOperandDeltas(provisioningPlan, iuDeltas, propertyDeltas);
ResolutionInfo resolutionInfo = new ResolutionInfo()
{
public IProvisioningPlan getProvisioningPlan()
{
return provisioningPlan;
}
public IInstallableUnit getArtificialRoot()
{
return rootIU;
}
public Map<IInstallableUnit, DeltaType> getIUDeltas()
{
return iuDeltas;
}
public Map<IInstallableUnit, Map<String, Pair<Object, Object>>> getPropertyDeltas()
{
return propertyDeltas;
}
public List<IMetadataRepository> getMetadataRepositories()
{
return metadataRepositories;
}
};
if (!context.handleProvisioningPlan(resolutionInfo))
{
return null;
}
if (iuDeltas.isEmpty() && propertyDeltas.isEmpty())
{
return null;
}
return new Resolution()
{
public ProfileTransaction getProfileTransaction()
{
return ProfileTransactionImpl.this;
}
public IProvisioningPlan getProvisioningPlan()
{
return provisioningPlan;
}
public boolean commit(IProgressMonitor monitor) throws CoreException
{
final String oldUsesMode = System.setProperty(OSGI_RESOLVER_USES_MODE, "ignore");
cleanup.add(new Runnable()
{
public void run()
{
if (oldUsesMode == null)
{
System.clearProperty(OSGI_RESOLVER_USES_MODE);
}
else
{
System.setProperty(OSGI_RESOLVER_USES_MODE, oldUsesMode);
}
}
});
try
{
IPhaseSet phaseSet = context.getPhaseSet(ProfileTransactionImpl.this);
initUnsignedContentConfirmer(context, agent, cleanup);
IEngine engine = agent.getEngine();
ensureSameBackupDevice(provisioningPlan);
IStatus status = PlanExecutionHelper.executePlan(provisioningPlan, engine, phaseSet, provisioningContext,
new ExecutePlanMonitor(monitor, provisioningPlan));
context.handleExecutionResult(status);
P2CorePlugin.INSTANCE.coreException(status);
profileImpl.setDefinition(profileDefinition);
return delegate.getTimestamp() != timestamp;
}
finally
{
cleanup(cleanup);
}
}
public void rollback()
{
cleanup(cleanup);
}
};
}
catch (Throwable t)
{
cleanup(cleanup);
P2CorePlugin.INSTANCE.coreException(t);
return null;
}
finally
{
monitor.done();
}
}
private void computeOperandDeltas(final IProvisioningPlan provisioningPlan, Map<IInstallableUnit, CommitContext.DeltaType> iuDeltas,
Map<IInstallableUnit, Map<String, Pair<Object, Object>>> propertyDeltas)
{
// Undo (remove) the addition of our artificial root IU and compute the effective deltas (to remove redundancies in the operands).
Field operandsField = ReflectUtil.getField(ProvisioningPlan.class, "operands");
@SuppressWarnings("unchecked")
List<Operand> operands = (List<Operand>)ReflectUtil.getValue(operandsField, provisioningPlan);
for (Iterator<Operand> it = operands.iterator(); it.hasNext();)
{
Operand operand = it.next();
if (operand instanceof InstallableUnitOperand)
{
InstallableUnitOperand iuOperand = (InstallableUnitOperand)operand;
IInstallableUnit first = iuOperand.first();
IInstallableUnit second = iuOperand.second();
if (first == null)
{
if (second.getId().equals(ARTIFICIAL_ROOT_ID))
{
it.remove();
}
else
{
iuDeltas.put(second, CommitContext.DeltaType.ADDITION);
}
}
else
{
iuDeltas.put(first, CommitContext.DeltaType.REMOVAL);
if (second != null)
{
iuDeltas.put(second, CommitContext.DeltaType.ADDITION);
}
}
}
else if (operand instanceof InstallableUnitPropertyOperand)
{
InstallableUnitPropertyOperand iuPropertyOperand = (InstallableUnitPropertyOperand)operand;
IInstallableUnit operandIU = iuPropertyOperand.getInstallableUnit();
if (operandIU.getId().equals(ARTIFICIAL_ROOT_ID))
{
it.remove();
}
else
{
Object first = iuPropertyOperand.first();
Object second = iuPropertyOperand.second();
String key = iuPropertyOperand.getKey();
populatePropertyDeltas(operandIU, first, second, key, propertyDeltas);
}
}
else if (operand instanceof PropertyOperand)
{
PropertyOperand propertyOperand = (PropertyOperand)operand;
Object first = propertyOperand.first();
Object second = propertyOperand.second();
String key = propertyOperand.getKey();
populatePropertyDeltas(null, first, second, key, propertyDeltas);
}
}
for (Iterator<Map.Entry<IInstallableUnit, Map<String, Pair<Object, Object>>>> it = propertyDeltas.entrySet().iterator(); it.hasNext();)
{
Map.Entry<IInstallableUnit, Map<String, Pair<Object, Object>>> entry = it.next();
Set<Map.Entry<String, Pair<Object, Object>>> properties = entry.getValue().entrySet();
for (Iterator<Map.Entry<String, Pair<Object, Object>>> it2 = properties.iterator(); it2.hasNext();)
{
Map.Entry<String, Pair<Object, Object>> property = it2.next();
Pair<Object, Object> pair = property.getValue();
if (ObjectUtil.equals(pair.getElement1(), pair.getElement2()))
{
it2.remove();
}
}
if (properties.isEmpty())
{
it.remove();
}
}
}
private void populatePropertyDeltas(IInstallableUnit operandIU, Object first, Object second, String key,
Map<IInstallableUnit, Map<String, Pair<Object, Object>>> propertyDeltas)
{
Map<String, Pair<Object, Object>> propertyDelta = propertyDeltas.get(operandIU);
if (propertyDelta == null)
{
propertyDelta = new HashMap<String, Pair<Object, Object>>();
propertyDelta.put(key, new Pair<Object, Object>(first, second));
propertyDeltas.put(operandIU, propertyDelta);
}
else
{
Pair<Object, Object> pair = propertyDelta.get(key);
if (pair == null)
{
propertyDelta.put(key, new Pair<Object, Object>(first, second));
}
else
{
pair.setElement2(second);
}
}
}
private void initMirrors(final List<Runnable> cleanup)
{
final boolean wasMirrors = SimpleArtifactRepository.MIRRORS_ENABLED;
if (mirrors != wasMirrors)
{
try
{
final Field mirrorsEnabledField = ReflectUtil.getField(SimpleArtifactRepository.class, "MIRRORS_ENABLED");
ReflectUtil.setValue(mirrorsEnabledField, null, mirrors, true);
cleanup.add(new Runnable()
{
public void run()
{
try
{
ReflectUtil.setValue(mirrorsEnabledField, null, wasMirrors, true);
}
catch (Throwable ex)
{
// Ignore
}
}
});
}
catch (Throwable ex)
{
// Ignore
}
}
}
private void initUnsignedContentConfirmer(final CommitContext context, final Agent agent, final List<Runnable> cleanup)
{
final Confirmer unsignedContentConfirmer = context.getUnsignedContentConfirmer();
if (unsignedContentConfirmer != null)
{
final IProvisioningAgent provisioningAgent = agent.getProvisioningAgent();
final UIServices oldUIServices = (UIServices)provisioningAgent.getService(UIServices.SERVICE_NAME);
final UIServices newUIServices = new UIServices()
{
@Override
public AuthenticationInfo getUsernamePassword(String location)
{
if (oldUIServices != null)
{
return oldUIServices.getUsernamePassword(location);
}
return null;
}
@Override
public AuthenticationInfo getUsernamePassword(String location, AuthenticationInfo previousInfo)
{
if (oldUIServices != null)
{
return oldUIServices.getUsernamePassword(location, previousInfo);
}
return null;
}
@Override
public TrustInfo getTrustInfo(Certificate[][] untrustedChains, String[] unsignedDetail)
{
if (unsignedDetail != null && unsignedDetail.length != 0)
{
Confirmation confirmation = unsignedContentConfirmer.confirm(true, unsignedDetail);
if (!confirmation.isConfirmed())
{
return new TrustInfo(new Certificate[0], false, false);
}
// We've checked trust already; prevent oldUIServices to check it again.
unsignedDetail = null;
}
if (oldUIServices != null)
{
return oldUIServices.getTrustInfo(untrustedChains, unsignedDetail);
}
// The rest is copied from org.eclipse.equinox.internal.p2.director.app.DirectorApplication.AvoidTrustPromptService
final Certificate[] trusted;
if (untrustedChains == null)
{
trusted = null;
}
else
{
trusted = new Certificate[untrustedChains.length];
for (int i = 0; i < untrustedChains.length; i++)
{
trusted[i] = untrustedChains[i][0];
}
}
return new TrustInfo(trusted, false, true);
}
};
provisioningAgent.registerService(UIServices.SERVICE_NAME, newUIServices);
cleanup.add(new Runnable()
{
public void run()
{
provisioningAgent.unregisterService(UIServices.SERVICE_NAME, newUIServices);
if (oldUIServices != null)
{
provisioningAgent.registerService(UIServices.SERVICE_NAME, oldUIServices);
}
}
});
}
}
private URI[] collectRepositories(List<IMetadataRepository> metadataRepositories, Set<URI> artifactURIs, List<Runnable> cleanup, IProgressMonitor monitor)
throws CoreException
{
Agent agent = profile.getAgent();
final IMetadataRepositoryManager manager = agent.getMetadataRepositoryManager();
Set<String> knownRepositories = P2Util.getKnownRepositories(manager);
EList<Repository> repositories = profileDefinition.getRepositories();
URI[] metadataURIs = new URI[repositories.size()];
Set<URI> addedArtifactURIs = new HashSet<URI>();
for (int i = 0; i < metadataURIs.length; i++)
{
try
{
Repository repository = repositories.get(i);
String url = repository.getURL();
final URI uri = new URI(url);
if (!knownRepositories.contains(url))
{
cleanup.add(new Runnable()
{
public void run()
{
manager.removeRepository(uri);
}
});
}
metadataURIs[i] = uri;
if (addedArtifactURIs.add(uri))
{
artifactURIs.add(uri);
}
}
catch (Exception ex)
{
throw new P2Exception(ex);
}
}
RepositoryLoader repositoryLoader = new RepositoryLoader(manager, metadataURIs);
repositoryLoader.begin(monitor);
ProvisionException exception = repositoryLoader.getException();
if (exception != null)
{
throw exception;
}
if (repositoryLoader.isCanceled())
{
throw new OperationCanceledException();
}
for (int i = 0; i < metadataURIs.length; i++)
{
IMetadataRepository metadataRepository = manager.loadRepository(metadataURIs[i], null);
metadataRepositories.add(metadataRepository);
}
if (!"false".equalsIgnoreCase(PropertiesUtil.getProperty(PROP_ADDITIONAL_POOLS)))
{
for (BundlePool bundlePool : agent.getAgentManager().getBundlePools())
{
P2CorePlugin.checkCancelation(monitor);
if (bundlePool != profile.getBundlePool())
{
URI uri = bundlePool.getLocation().toURI();
if (addedArtifactURIs.add(uri))
{
artifactURIs.add(uri);
}
}
}
}
return metadataURIs;
}
private IInstallableUnit adjustProfileChangeRequest(final IProfileChangeRequest request, IProgressMonitor monitor) throws CoreException
{
InstallableUnitDescription rootDescription = new InstallableUnitDescription();
rootDescription.setId(ARTIFICIAL_ROOT_ID);
rootDescription.setVersion(Version.createOSGi(1, 0, 0, "v" + System.currentTimeMillis()));
rootDescription.setSingleton(true);
rootDescription.setArtifacts(new IArtifactKey[0]);
rootDescription.setProperty(InstallableUnitDescription.PROP_TYPE_GROUP, Boolean.TRUE.toString());
rootDescription.setCapabilities(new IProvidedCapability[] {
MetadataFactory.createProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, rootDescription.getId(), rootDescription.getVersion()) });
List<IRequirement> rootRequirements = new ArrayList<IRequirement>();
Map<String, IInstallableUnit> rootIUs = new HashMap<String, IInstallableUnit>();
for (IInstallableUnit rootIU : P2Util.asIterable(profile.query(new UserVisibleRootQuery(), null)))
{
if (!removeAll)
{
String id = rootIU.getId();
rootIUs.put(id, rootIU);
VersionRange versionRange = getCleanVersionRange(rootIU);
IMatchExpression<IInstallableUnit> filter = rootIU.getFilter();
IRequirement rootRequirement = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, id, versionRange, filter, false, false);
rootRequirements.add(rootRequirement);
}
request.remove(rootIU);
}
MultiStatus status = new MultiStatus(P2CorePlugin.INSTANCE.getSymbolicName(), 0, "Profile could not be changed", null);
for (Requirement requirement : profileDefinition.getRequirements())
{
P2CorePlugin.checkCancelation(monitor);
String namespace = requirement.getNamespace();
String name = requirement.getName();
VersionRange versionRange = requirement.getVersionRange();
IMatchExpression<IInstallableUnit> filter = requirement.getMatchExpression();
boolean optional = requirement.isOptional();
IRequirement rootRequirement = MetadataFactory.createRequirement(namespace, name, versionRange, filter, optional, false);
rootRequirements.add(rootRequirement);
}
rootDescription.setRequirements(rootRequirements.toArray(new IRequirement[rootRequirements.size()]));
IInstallableUnit rootUnit = MetadataFactory.createInstallableUnit(rootDescription);
request.add(rootUnit);
request.setInstallableUnitProfileProperty(rootUnit, Profile.PROP_PROFILE_ROOT_IU, Boolean.TRUE.toString());
P2CorePlugin.INSTANCE.coreException(status);
if (isProfileDefinitionChanged())
{
request.setProfileProperty(Profile.PROP_PROFILE_DEFINITION, ProfileImpl.definitionToXML(profileDefinition));
}
compare(cleanProfileProperties, profileProperties, new CompareHandler<String>()
{
public void handleAddition(String key, String value)
{
if (!IMMUTABLE_PROPERTIES.contains(key))
{
request.setProfileProperty(key, value);
}
}
public void handleRemoval(String key)
{
if (!IMMUTABLE_PROPERTIES.contains(key))
{
request.removeProfileProperty(key);
}
}
});
compare(cleanIUProperties, iuProperties, new CompareHandler<IUPropertyKey>()
{
public void handleAddition(IUPropertyKey key, String value)
{
request.setInstallableUnitProfileProperty(key.getInstallableUnit(), key.getPropertyKey(), value);
}
public void handleRemoval(IUPropertyKey key)
{
request.removeInstallableUnitProfileProperty(key.getInstallableUnit(), key.getPropertyKey());
}
});
return rootUnit;
}
private VersionRange getCleanVersionRange(IInstallableUnit rootIU)
{
String id = rootIU.getId();
Version version = rootIU.getVersion();
for (Requirement requirement : cleanProfileDefinition.getRequirements())
{
if (requirement.getName().equals(id))
{
VersionRange versionRange = requirement.getVersionRange();
if (versionRange == null || versionRange.isIncluded(version))
{
return versionRange;
}
}
}
return new VersionRange(version.toString());
}
private void ensureSameBackupDevice(final IProvisioningPlan provisioningPlan) throws CoreException
{
// This is to handle the special case in Windows where the backup store tries to move the *.exe to a different device.
// This fails when the *.exe is currently in use.
// The strategy then copies the file instead and then to delete it, but that always fails.
// If the backup store is on the same device, the move is successful and the *.exe can be successfully updated.
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=427148 for more details.
if (profile.isCurrent() && Platform.OS_WIN32.equals(Platform.getOS()))
{
try
{
Location location = Platform.getInstallLocation();
org.eclipse.emf.common.util.URI installationLocation = org.eclipse.emf.common.util.URI.createURI(FileLocator.resolve(location.getURL()).toString());
org.eclipse.emf.common.util.URI tempDir = org.eclipse.emf.common.util.URI.createFileURI(PropertiesUtil.getProperty("java.io.tmpdir"));
if (!ObjectUtil.equals(installationLocation.device(), tempDir.device()))
{
Field field = ReflectUtil.getField(NativeTouchpoint.class, "backups");
@SuppressWarnings("unchecked")
Map<IProfile, IBackupStore> backups = (Map<IProfile, IBackupStore>)ReflectUtil.getValue(field, null);
final File localTempFolder = new File(installationLocation.toFileString(), "backup");
final IProfile planProfile = provisioningPlan.getProfile();
backups.put(planProfile, new IBackupStore()
{
private BackupStore delegate;
public boolean backup(File file) throws IOException
{
loadDelegate();
return delegate.backup(file);
}
public boolean backupDirectory(File file) throws IOException
{
loadDelegate();
return delegate.backupDirectory(file);
}
public void discard()
{
if (delegate == null)
{
return;
}
delegate.discard();
}
public void restore() throws IOException
{
if (delegate == null)
{
return;
}
delegate.restore();
}
private void loadDelegate()
{
if (delegate != null)
{
return;
}
delegate = new BackupStore(localTempFolder, NativeTouchpoint.escape(planProfile.getProfileId()));
}
public String getBackupName()
{
loadDelegate();
return delegate.getBackupName();
}
public boolean backupCopy(File file) throws IOException
{
loadDelegate();
return delegate.backupCopy(file);
}
public void backupCopyAll(File file) throws IOException
{
loadDelegate();
delegate.backupCopyAll(file);
}
public void backupAll(File file) throws IOException
{
loadDelegate();
delegate.backupAll(file);
}
});
}
}
catch (IOException ex)
{
P2CorePlugin.INSTANCE.coreException(ex);
}
}
}
private static void cleanup(List<Runnable> cleanup)
{
for (Runnable runnable : cleanup)
{
try
{
runnable.run();
}
catch (Throwable t)
{
P2CorePlugin.INSTANCE.log(t);
}
}
cleanup.clear();
}
private static IInstallableUnit generateSourceContainerIU(IProvisioningPlan provisioningPlan, IQueryable<IInstallableUnit> metadata, IProgressMonitor monitor)
{
// Create and return an IU that has optional and greedy requirements on all source bundles
// related to bundle IUs in the profile
List<IRequirement> requirements = new ArrayList<IRequirement>();
IQueryResult<IInstallableUnit> ius = provisioningPlan.getFutureState().query(QueryUtil.createIUAnyQuery(), monitor);
for (IInstallableUnit iu : P2Util.asIterable(ius))
{
P2CorePlugin.checkCancelation(monitor);
// TODO What about source features?
if (iu.satisfies(BUNDLE_REQUIREMENT))
{
String id = iu.getId() + ".source";
Version version = iu.getVersion();
VersionRange versionRange = new VersionRange(version, true, version, true);
IInstallableUnit sourceIU = queryInstallableUnit(metadata, id, versionRange, monitor);
if (sourceIU != null)
{
provisioningPlan.addInstallableUnit(sourceIU);
IRequirement sourceRequirement = MetadataFactory.createRequirement("osgi.bundle", id, versionRange, null, true, false, true);
requirements.add(sourceRequirement);
}
}
}
Version sourceContainerIUVersion = getSourceContainerIUVersion(provisioningPlan.getProfile(), monitor);
IProvidedCapability capability = MetadataFactory.createProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, SOURCE_IU_ID, sourceContainerIUVersion);
InstallableUnitDescription sourceIUDescription = new MetadataFactory.InstallableUnitDescription();
sourceIUDescription.setSingleton(true);
sourceIUDescription.setId(SOURCE_IU_ID);
sourceIUDescription.setVersion(sourceContainerIUVersion);
sourceIUDescription.addRequirements(requirements);
sourceIUDescription.setCapabilities(new IProvidedCapability[] { capability });
return MetadataFactory.createInstallableUnit(sourceIUDescription);
}
private static Version getSourceContainerIUVersion(IProfile profile, IProgressMonitor monitor)
{
IQuery<IInstallableUnit> query = QueryUtil.createIUQuery(SOURCE_IU_ID);
IQueryResult<IInstallableUnit> result = profile.query(query, monitor);
if (result.isEmpty())
{
return Version.createOSGi(1, 0, 0);
}
IInstallableUnit currentSourceIU = result.iterator().next();
Integer major = (Integer)currentSourceIU.getVersion().getSegment(0);
return Version.createOSGi(major.intValue() + 1, 0, 0);
}
private static IInstallableUnit queryInstallableUnit(IQueryable<IInstallableUnit> metadata, String id, VersionRange versionRange, IProgressMonitor monitor)
{
IQuery<IInstallableUnit> iuQuery = QueryUtil.createIUQuery(id, versionRange);
IQuery<IInstallableUnit> latestQuery = QueryUtil.createLatestQuery(iuQuery);
Iterator<IInstallableUnit> iterator = metadata.query(latestQuery, monitor).iterator();
if (iterator.hasNext())
{
return iterator.next();
}
return null;
}
private static <T extends EObject> boolean equals(EqualityHelper equalityHelper, Collection<T> c1, Collection<T> c2)
{
for (T o : c1)
{
if (!contains(equalityHelper, c2, o))
{
return false;
}
}
for (T o : c2)
{
if (!contains(equalityHelper, c1, o))
{
return false;
}
}
return true;
}
private static <T extends EObject> boolean contains(EqualityHelper equalityHelper, Collection<T> c, T object)
{
for (T o : c)
{
if (equalityHelper.equals(object, o))
{
return true;
}
}
return false;
}
private static <K> void compare(Map<K, String> clean, Map<K, String> dirty, CompareHandler<K> handler)
{
for (Map.Entry<K, String> entry : dirty.entrySet())
{
K key = entry.getKey();
String dirtyValue = entry.getValue();
String cleanValue = clean.get(key);
if (cleanValue == null || !cleanValue.equals(dirtyValue))
{
handler.handleAddition(key, dirtyValue);
}
}
for (Map.Entry<K, String> entry : clean.entrySet())
{
K key = entry.getKey();
if (!dirty.containsKey(key))
{
handler.handleRemoval(key);
}
}
}
/**
* @author Eike Stepper
*/
private static final class ExecutePlanMonitor extends ProgressMonitorWrapper
{
private static final String INSTALLING_PREFIX = "Installing ";
private final Map<String, LinkedList<Version>> versions = new HashMap<String, LinkedList<Version>>();
private final IQueryable<IInstallableUnit> additions;
private ExecutePlanMonitor(IProgressMonitor monitor, IProvisioningPlan provisioningPlan)
{
super(monitor);
additions = provisioningPlan.getAdditions();
}
@Override
public void subTask(String name)
{
if (name.startsWith(INSTALLING_PREFIX))
{
String id = name.substring(INSTALLING_PREFIX.length());
Version version = getVersion(id);
if (version != null)
{
name += " [" + version + "]";
}
}
super.subTask(name);
}
private Version getVersion(String id)
{
LinkedList<Version> list = versions.get(id);
if (list != null)
{
if (list.isEmpty())
{
return null;
}
return list.remove(0);
}
Version firstVersion = null;
IQueryResult<IInstallableUnit> ius = additions.query(QueryUtil.createIUQuery(id), null);
if (!ius.isEmpty())
{
for (IInstallableUnit iu : ius)
{
if (firstVersion == null)
{
firstVersion = iu.getVersion();
}
else
{
if (list == null)
{
list = new LinkedList<Version>();
versions.put(id, list);
}
list.add(iu.getVersion());
}
}
}
return firstVersion;
}
}
/**
* @author Eike Stepper
*/
public interface CompareHandler<K>
{
public void handleAddition(K key, String value);
public void handleRemoval(K key);
}
/**
* @author Eike Stepper
*/
private static final class IUPropertyKey
{
private static final int PRIME = 31;
private final IInstallableUnit iu;
private final String propertyKey;
private final int hashCode;
public IUPropertyKey(IInstallableUnit iu, String propertyKey)
{
this.iu = iu;
this.propertyKey = propertyKey;
hashCode = PRIME * (PRIME + iu.hashCode()) + propertyKey.hashCode();
}
public IInstallableUnit getInstallableUnit()
{
return iu;
}
public String getPropertyKey()
{
return propertyKey;
}
@Override
public int hashCode()
{
return hashCode;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
IUPropertyKey other = (IUPropertyKey)obj;
if (!iu.equals(other.iu))
{
return false;
}
if (!propertyKey.equals(other.propertyKey))
{
return false;
}
return true;
}
@Override
public String toString()
{
return iu.toString() + " / " + propertyKey;
}
}
/**
* @author Ed Merks
*/
private static final class RepositoryLoader extends WorkerPool<RepositoryLoader, URI, RepositoryLoader.Worker>
{
private final IMetadataRepositoryManager manager;
private final URI[] uris;
private final List<IMetadataRepository> metadataRepositories = Collections.synchronizedList(new ArrayList<IMetadataRepository>());
private ProvisionException exception;
public RepositoryLoader(IMetadataRepositoryManager manager, URI... uris)
{
this.manager = manager;
this.uris = uris;
}
public void begin(IProgressMonitor monitor)
{
try
{
monitor.beginTask("", uris.length + 1);
monitor.worked(1);
begin("Load Repositories", monitor);
}
finally
{
monitor.done();
}
}
public ProvisionException getException()
{
return exception;
}
@Override
protected void run(String taskName, IProgressMonitor monitor)
{
perform(uris);
}
@Override
protected Worker createWorker(URI key, int workerID, boolean secondary)
{
return new Worker("Repository loader for " + key, this, key, workerID, secondary);
}
/**
* @author Ed Merks
*/
private static class Worker extends WorkerPool.Worker<URI, RepositoryLoader>
{
public Worker(String name, RepositoryLoader workPool, URI key, int id, boolean secondary)
{
super(name, workPool, key, id, secondary);
}
@Override
protected IStatus perform(IProgressMonitor monitor)
{
RepositoryLoader workPool = getWorkPool();
if (workPool.isCanceled())
{
return Status.CANCEL_STATUS;
}
try
{
IMetadataRepository metadataRepository = workPool.manager.loadRepository(getKey(), SubMonitor.convert(monitor, 1));
workPool.metadataRepositories.add(metadataRepository);
return Status.OK_STATUS;
}
catch (ProvisionException ex)
{
workPool.cancel();
workPool.exception = ex;
return P2CorePlugin.INSTANCE.getStatus(ex);
}
catch (OperationCanceledException ex)
{
return Status.CANCEL_STATUS;
}
}
}
}
}