Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDJ Houghton2010-05-25 20:32:52 +0000
committerDJ Houghton2010-05-25 20:32:52 +0000
commit1c09231497945057ea7cbeb9122921727ff5f065 (patch)
tree023e9db195c9d2a2a222d9acbb2d3cfb41f29ef6
parentb1b46b26628960da7dc3d64db4100edcf79c46af (diff)
downloadrt.equinox.p2-1c09231497945057ea7cbeb9122921727ff5f065.tar.gz
rt.equinox.p2-1c09231497945057ea7cbeb9122921727ff5f065.tar.xz
rt.equinox.p2-1c09231497945057ea7cbeb9122921727ff5f065.zip
Bug 270195 - [reconciler] Problem removing IUs being depended on from software installed by the UIv20100525
-rw-r--r--bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/ProfileChangeRequest.java1
-rw-r--r--bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/Messages.java5
-rw-r--r--bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/ProfileSynchronizer.java282
-rw-r--r--bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/messages.properties6
4 files changed, 224 insertions, 70 deletions
diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/ProfileChangeRequest.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/ProfileChangeRequest.java
index 5bf18bc17..ac2ee1884 100644
--- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/ProfileChangeRequest.java
+++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/ProfileChangeRequest.java
@@ -235,6 +235,7 @@ public class ProfileChangeRequest implements Cloneable, IProfileChangeRequest {
result.propertiesToAdd = propertiesToAdd == null ? null : (HashMap<String, String>) propertiesToAdd.clone();
result.iuPropertiesToAdd = iuPropertiesToAdd == null ? null : (HashMap<IInstallableUnit, Map<String, String>>) iuPropertiesToAdd.clone();
result.iuPropertiesToRemove = iuPropertiesToRemove == null ? null : (HashMap<IInstallableUnit, List<String>>) iuPropertiesToRemove.clone();
+ result.additionalRequirements = additionalRequirements == null ? null : (ArrayList<IRequirement>) additionalRequirements.clone();
return result;
}
diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/Messages.java b/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/Messages.java
index 79296ff8f..4d3cde89c 100644
--- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/Messages.java
+++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/Messages.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2009 IBM Corporation and others.
+ * Copyright (c) 2008, 2010 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
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Sonatype Inc. - Ongoing development
*******************************************************************************/
package org.eclipse.equinox.internal.p2.reconciler.dropins;
@@ -23,6 +24,8 @@ public class Messages extends NLS {
public static String metadata_repo_manager_not_registered;
public static String error_reading_link;
public static String error_resolving_link;
+ public static String remove_root;
+ public static String remove_all_roots;
static {
// initialize resource bundle
diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/ProfileSynchronizer.java b/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/ProfileSynchronizer.java
index 08304e3ed..5e5bea0bb 100644
--- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/ProfileSynchronizer.java
+++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/ProfileSynchronizer.java
@@ -34,6 +34,7 @@ import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
import org.eclipse.equinox.p2.repository.artifact.IFileArtifactRepository;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
import org.eclipse.osgi.service.environment.EnvironmentInfo;
+import org.eclipse.osgi.util.NLS;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
@@ -46,6 +47,9 @@ public class ProfileSynchronizer {
private static final String PROFILE_TIMESTAMP = "PROFILE"; //$NON-NLS-1$
private static final String NO_TIMESTAMP = "-1"; //$NON-NLS-1$
private static final String PROP_FROM_DROPINS = "org.eclipse.equinox.p2.reconciler.dropins"; //$NON-NLS-1$
+ private static final String INCLUSION_RULES = "org.eclipse.equinox.p2.internal.inclusion.rules"; //$NON-NLS-1$
+ private static final String INCLUSION_OPTIONAL = "OPTIONAL"; //$NON-NLS-1$
+ private static final String INCLUSION_STRICT = "STRICT"; //$NON-NLS-1$
private static final String CACHE_EXTENSIONS = "org.eclipse.equinox.p2.cache.extensions"; //$NON-NLS-1$
private static final String PIPE = "|"; //$NON-NLS-1$
@@ -56,12 +60,23 @@ public class ProfileSynchronizer {
private Map<String, String> timestamps;
private final IProvisioningAgent agent;
+ /*
+ * Specialized profile change request so we can keep track of IUs which have moved
+ * locations on disk.
+ */
static class ReconcilerProfileChangeRequest extends ProfileChangeRequest {
- boolean isMove = false;
+ List<IInstallableUnit> toMove = new ArrayList<IInstallableUnit>();
- public ReconcilerProfileChangeRequest(IProfile profile, boolean isMove) {
+ public ReconcilerProfileChangeRequest(IProfile profile) {
super(profile);
- this.isMove = isMove;
+ }
+
+ void moveAll(Collection<IInstallableUnit> list) {
+ toMove.addAll(list);
+ }
+
+ Collection<IInstallableUnit> getMoves() {
+ return toMove;
}
}
@@ -79,6 +94,7 @@ public class ProfileSynchronizer {
/*
* Synchronize the profile with the list of metadata repositories.
+ * TODO fix progress monitoring (although in practice the user doesn't see it or have a chance to cancel)
*/
public IStatus synchronize(IProgressMonitor monitor) {
readTimestamps();
@@ -88,53 +104,161 @@ public class ProfileSynchronizer {
ProvisioningContext context = getContext();
context.setProperty(EXPLANATION, new Boolean(Tracing.DEBUG_RECONCILER).toString());
- boolean done = false;
- while (!done) {
- // figure out if we really have anything to install/uninstall
- ReconcilerProfileChangeRequest request = createProfileChangeRequest(context);
- String updatedCacheExtensions = synchronizeCacheExtensions();
- if (request == null) {
- if (updatedCacheExtensions != null) {
- IStatus engineResult = setProperty(CACHE_EXTENSIONS, updatedCacheExtensions, context, null);
- if (engineResult.getSeverity() != IStatus.ERROR && engineResult.getSeverity() != IStatus.CANCEL)
- writeTimestamps();
- return engineResult;
- }
+ String updatedCacheExtensions = synchronizeCacheExtensions();
+
+ // figure out if we really have anything to install/uninstall.
+ ReconcilerProfileChangeRequest request = createProfileChangeRequest(context);
+ if (request == null) {
+ if (updatedCacheExtensions == null)
return Status.OK_STATUS;
- }
- if (updatedCacheExtensions != null)
- request.setProfileProperty(CACHE_EXTENSIONS, updatedCacheExtensions);
+ IStatus engineResult = setProperty(CACHE_EXTENSIONS, updatedCacheExtensions, context, null);
+ if (engineResult.getSeverity() != IStatus.ERROR && engineResult.getSeverity() != IStatus.CANCEL)
+ writeTimestamps();
+ return engineResult;
+ }
+ if (updatedCacheExtensions != null)
+ request.setProfileProperty(CACHE_EXTENSIONS, updatedCacheExtensions);
- SubMonitor sub = SubMonitor.convert(monitor, 100);
- try {
- //create the provisioning plan
- IProvisioningPlan plan = createProvisioningPlan(request, context, sub.newChild(50));
- IStatus status = plan.getStatus();
- if (status.getSeverity() == IStatus.ERROR || status.getSeverity() == IStatus.CANCEL)
- return status;
- debug(request, plan);
-
- // if there is no work to do then just write out the timestamps and return.
- if (plan.getAdditions().query(QueryUtil.createIUAnyQuery(), null).isEmpty() && plan.getRemovals().query(QueryUtil.createIUAnyQuery(), null).isEmpty()) {
- writeTimestamps();
- return status;
- }
+ // if some of the IUs move locations then construct a special plan and execute that first
+ IStatus moveResult = performRemoveForMovedIUs(request, context, monitor);
+ if (moveResult.getSeverity() == IStatus.ERROR || moveResult.getSeverity() == IStatus.CANCEL)
+ return moveResult;
- //invoke the engine to perform installs/uninstalls
- IStatus engineResult = executePlan(plan, context, sub.newChild(50));
- if (engineResult.getSeverity() == IStatus.ERROR || engineResult.getSeverity() == IStatus.CANCEL)
- return engineResult;
+ // now create a plan for the rest of the work and execute it
+ IStatus addRemoveResult = performAddRemove(request, context, monitor);
+ if (addRemoveResult.getSeverity() == IStatus.ERROR || addRemoveResult.getSeverity() == IStatus.CANCEL)
+ return addRemoveResult;
- } finally {
- done = !request.isMove;
- sub.done();
- }
- }
// write out the new timestamps (for caching) and apply the configuration
writeTimestamps();
return applyConfiguration(false);
}
+ /*
+ * Return a list of the roots in the profile.
+ */
+ private IQueryResult<IInstallableUnit> getStrictRoots() {
+ return profile.query(new IUProfilePropertyQuery(INCLUSION_RULES, INCLUSION_STRICT), null);
+ }
+
+ /*
+ * Convert the profile change request into operands and have the engine execute them. There
+ * is fancy logic here in case we are trying to remove IUs which are depended on by something
+ * which is installed via the UI. Since the bundle has been removed from the file-system it is a forced
+ * removal so we have to uninstall the UI-installed IU.
+ */
+ private IStatus performAddRemove(ReconcilerProfileChangeRequest request, ProvisioningContext context, IProgressMonitor monitor) {
+ // if we have moves then we have previously removed them.
+ // now we need to add them back (at the new location)
+ for (IInstallableUnit iu : request.getMoves()) {
+ request.add(iu);
+ request.setInstallableUnitProfileProperty(iu, PROP_FROM_DROPINS, Boolean.TRUE.toString());
+ request.setInstallableUnitInclusionRules(iu, ProfileInclusionRules.createOptionalInclusionRule(iu));
+ request.setInstallableUnitProfileProperty(iu, IProfile.PROP_PROFILE_LOCKED_IU, Integer.toString(IProfile.LOCK_UNINSTALL));
+ }
+
+ Collection<IInstallableUnit> additions = request.getAdditions();
+ Collection<IInstallableUnit> removals = request.getRemovals();
+ // see if there is any work to do
+ if (additions.isEmpty() && removals.isEmpty())
+ return Status.OK_STATUS;
+
+ // TODO See bug 270195. Eventually we will attempt to remove strictly installed IUs if their
+ // dependent bundles have been deleted.
+ boolean removeStrictRoots = false;
+ if (removeStrictRoots)
+ return performStrictRootRemoval(request, context, monitor);
+ IProvisioningPlan plan = createProvisioningPlan(request, context, monitor);
+ debug(request, plan);
+ return executePlan(plan, context, monitor);
+ }
+
+ // TODO re-enable after resolving bug 270195.
+ private IStatus performStrictRootRemoval(ReconcilerProfileChangeRequest request, ProvisioningContext context, IProgressMonitor monitor) {
+ Collection<IInstallableUnit> removals = request.getRemovals();
+ // if we don't have any removals then we don't have to worry about potentially
+ // invalidating things we already have installed, removal of roots, etc so just
+ // create a regular plan.
+ IProvisioningPlan plan = null;
+ if (removals.isEmpty()) {
+ plan = createProvisioningPlan(request, context, monitor);
+ debug(request, plan);
+ } else {
+ // We are now creating a backup of the original request that will be used to create the final plan (where no optional magic is used)
+ ProfileChangeRequest finalRequest = (ProfileChangeRequest) request.clone();
+
+ // otherwise collect the roots, pretend they are optional, and see
+ // if the resulting plan affects them
+ Set<IInstallableUnit> strictRoots = getStrictRoots().toUnmodifiableSet();
+ Collection<IRequirement> forceNegation = new ArrayList<IRequirement>(removals.size());
+ for (IInstallableUnit iu : removals)
+ forceNegation.add(createNegation(iu));
+ request.addExtraRequirements(forceNegation);
+
+ // set all the profile roots to be optional to see how they would be effected by the plan
+ for (IInstallableUnit iu : strictRoots)
+ request.setInstallableUnitProfileProperty(iu, INCLUSION_RULES, INCLUSION_OPTIONAL);
+
+ // get the tentative plan back from the planner
+ plan = createProvisioningPlan(request, context, monitor);
+ debug(request, plan);
+ if (!plan.getStatus().isOK())
+ return plan.getStatus();
+
+ // Analyze the plan to see if any of the strict roots are being uninstalled.
+ int removedRoots = 0;
+ for (IInstallableUnit initialRoot : strictRoots) {
+ // if the root wasn't uninstalled, then continue
+ if (plan.getRemovals().query(QueryUtil.createIUQuery(initialRoot), null).isEmpty())
+ continue;
+ // otherwise add its removal to the change request, along with a negation and
+ // change of strict to optional for their inclusion rule.
+ finalRequest.remove(initialRoot);
+ finalRequest.setInstallableUnitProfileProperty(initialRoot, INCLUSION_RULES, INCLUSION_OPTIONAL);
+ IRequirement negation = createNegation(initialRoot);
+ Collection<IRequirement> extra = new ArrayList<IRequirement>();
+ extra.add(negation);
+ request.addExtraRequirements(extra);
+ LogHelper.log(new Status(IStatus.INFO, Activator.ID, NLS.bind(Messages.remove_root, initialRoot.getId(), initialRoot.getVersion())));
+ removedRoots++;
+ }
+
+ // Check for the case where all the strict roots are being removed.
+ if (removedRoots == strictRoots.size())
+ return new Status(IStatus.ERROR, Activator.ID, Messages.remove_all_roots);
+ plan = createProvisioningPlan(finalRequest, context, monitor);
+ if (!plan.getStatus().isOK()) {
+ System.out.println("original request"); //$NON-NLS-1$
+ System.out.println(request);
+ System.out.println("final request"); //$NON-NLS-1$
+ System.out.println(finalRequest);
+ throw new IllegalStateException("The second plan is not resolvable."); //$NON-NLS-1$
+ }
+ }
+
+ // execute the plan and return the status
+ return executePlan(plan, context, monitor);
+ }
+
+ /*
+ * If the request contains IUs to be moved then create and execute a plan which
+ * removes them. Otherwise just return.
+ */
+ private IStatus performRemoveForMovedIUs(ReconcilerProfileChangeRequest request, ProvisioningContext context, IProgressMonitor monitor) {
+ Collection<IInstallableUnit> moves = request.getMoves();
+ if (moves.isEmpty())
+ return Status.OK_STATUS;
+ IEngine engine = (IEngine) agent.getService(IEngine.SERVICE_NAME);
+ IProvisioningPlan plan = engine.createPlan(profile, context);
+ for (IInstallableUnit unit : moves)
+ plan.removeInstallableUnit(unit);
+ return executePlan(plan, context, monitor);
+ }
+
+ /*
+ * Write out the timestamps of various repositories and folders/file to help
+ * us cache and detect cases where we don't have to perform a reconciliation.
+ */
private void writeTimestamps() {
timestamps.clear();
timestamps.put(PROFILE_TIMESTAMP, Long.toString(profile.getTimestamp()));
@@ -167,6 +291,10 @@ public class ProfileSynchronizer {
}
}
+ /*
+ * Check timestamps and return true if the profile is considered to be up-to-date or
+ * false if we should perform a reconciliation.
+ */
private boolean isUpToDate() {
// the user might want to force a reconciliation
if ("true".equals(Activator.getContext().getProperty("osgi.checkConfiguration"))) //$NON-NLS-1$//$NON-NLS-2$
@@ -205,6 +333,9 @@ public class ProfileSynchronizer {
return true;
}
+ /*
+ * Read the values of the stored timestamps that we use for caching.
+ */
private void readTimestamps() {
File file = Activator.getContext().getDataFile(TIMESTAMPS_FILE_PREFIX + profile.getProfileId().hashCode());
try {
@@ -339,7 +470,7 @@ public class ProfileSynchronizer {
* as part of equality)
*/
public ReconcilerProfileChangeRequest createProfileChangeRequest(ProvisioningContext context) {
- ReconcilerProfileChangeRequest request = new ReconcilerProfileChangeRequest(profile, false);
+ ReconcilerProfileChangeRequest request = new ReconcilerProfileChangeRequest(profile);
boolean resolve = Boolean.valueOf(profile.getProperty("org.eclipse.equinox.p2.resolve")).booleanValue(); //$NON-NLS-1$
if (resolve)
@@ -418,41 +549,37 @@ public class ProfileSynchronizer {
return null;
}
- // if we have just a regular add/remove then set up the change request as per normal
- if (toMove.isEmpty()) {
- context.setExtraInstallableUnits(toAdd);
- request.addAll(toAdd);
- request.removeAll(toRemove);
- } else {
- // if we had some bundles which moved locations then we need to create a move change request
- // and remove the moved bundles first. The caller of this method will take care of calling us again
- // to re-add the bundles at their new location (and other bundles which need adding)
- request = new ReconcilerProfileChangeRequest(profile, true);
- request.removeAll(toMove);
- }
-
- // force removal of all moved and removed IUs, which will also remove anything which depends on them
- // see: bug 306424#c6 and bug 308934.
- Collection<IRequirement> extraReqs = new ArrayList<IRequirement>();
- for (IInstallableUnit unit : request.getRemovals()) {
- IRequirement negation = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, unit.getId(), //
- new VersionRange(unit.getVersion(), true, unit.getVersion(), true), null, 0, 0, false);
- extraReqs.add(negation);
- }
- request.addExtraRequirements(extraReqs);
+ context.setExtraInstallableUnits(toAdd);
+ request.addAll(toAdd);
+ request.removeAll(toRemove);
+ request.moveAll(toMove);
+
debug(request);
return request;
}
- private void debug(ProfileChangeRequest request, IProvisioningPlan plan) {
+ /*
+ * Create and return a negated requirement saying that the given IU must not exist in the profile.
+ */
+ private IRequirement createNegation(IInstallableUnit unit) {
+ return MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, unit.getId(), //
+ new VersionRange(unit.getVersion(), true, unit.getVersion(), true), null, 0, 0, false);
+ }
+
+ /*
+ * If in debug mode, print out information which tells us whether or not the given
+ * provisioning plan matches the request.
+ */
+ private void debug(ReconcilerProfileChangeRequest request, IProvisioningPlan plan) {
if (!Tracing.DEBUG_RECONCILER)
return;
final String PREFIX = "[reconciler] [plan] "; //$NON-NLS-1$
// get the request
List<IInstallableUnit> toAdd = new ArrayList<IInstallableUnit>(request.getAdditions());
List<IInstallableUnit> toRemove = new ArrayList<IInstallableUnit>(request.getRemovals());
- // remove from the request everything that is in the plan
+ List<IInstallableUnit> toMove = new ArrayList<IInstallableUnit>(request.getMoves());
+ // remove from the request everything that is in the plan
for (Iterator<IInstallableUnit> iterator = plan.getRemovals().query(QueryUtil.createIUAnyQuery(), null).iterator(); iterator.hasNext();) {
IInstallableUnit iu = iterator.next();
toRemove.remove(iu);
@@ -461,6 +588,10 @@ public class ProfileSynchronizer {
IInstallableUnit iu = iterator.next();
toAdd.remove(iu);
}
+ // Move operations are treated as doing a remove/add. The removes have already happened
+ // and at this point we are adding the moved IUs back at their new location. Remove the moved
+ // IUs from the added list because this will just confuse the user.
+ toAdd.removeAll(toMove);
// if anything is left in the request, then something is wrong with the plan
if (toAdd.size() == 0 && toRemove.size() == 0)
@@ -480,7 +611,7 @@ public class ProfileSynchronizer {
/*
* If debugging is turned on, then print out the details for the given profile change request.
*/
- private void debug(ProfileChangeRequest request) {
+ private void debug(ReconcilerProfileChangeRequest request) {
if (!Tracing.DEBUG_RECONCILER)
return;
final String PREFIX = "[reconciler] "; //$NON-NLS-1$
@@ -518,6 +649,14 @@ public class ProfileSynchronizer {
}
}
+ Collection<IInstallableUnit> toMove = request.getMoves();
+ if (toMove == null || toMove.isEmpty()) {
+ Tracing.debug(PREFIX + "No installable units to move."); //$NON-NLS-1$
+ } else {
+ for (IInstallableUnit move : toMove)
+ Tracing.debug(PREFIX + "Moving IU: " + move.getId() + ' ' + move.getVersion()); //$NON-NLS-1$
+ }
+
Collection<IRequirement> extra = request.getExtraRequirements();
if (extra == null || extra.isEmpty()) {
Tracing.debug(PREFIX + "No extra requirements."); //$NON-NLS-1$
@@ -540,11 +679,17 @@ public class ProfileSynchronizer {
return allRepos;
}
+ /*
+ * Create and return a provisioning plan for the given change request.
+ */
private IProvisioningPlan createProvisioningPlan(ProfileChangeRequest request, ProvisioningContext provisioningContext, IProgressMonitor monitor) {
IPlanner planner = (IPlanner) agent.getService(IPlanner.SERVICE_NAME);
return planner.getProvisioningPlan(request, provisioningContext, monitor);
}
+ /*
+ * Call the engine to set the given property on the profile.
+ */
private IStatus setProperty(String key, String value, ProvisioningContext provisioningContext, IProgressMonitor monitor) {
IEngine engine = (IEngine) agent.getService(IEngine.SERVICE_NAME);
IProvisioningPlan plan = engine.createPlan(profile, provisioningContext);
@@ -553,6 +698,9 @@ public class ProfileSynchronizer {
return engine.perform(plan, phaseSet, monitor);
}
+ /*
+ * Execute the given plan.
+ */
private IStatus executePlan(IProvisioningPlan plan, ProvisioningContext provisioningContext, IProgressMonitor monitor) {
IEngine engine = (IEngine) agent.getService(IEngine.SERVICE_NAME);
IPhaseSet phaseSet = PhaseSetFactory.createDefaultPhaseSetExcluding(new String[] {PhaseSetFactory.PHASE_COLLECT, PhaseSetFactory.PHASE_CHECK_TRUST});
diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/messages.properties b/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/messages.properties
index c8b618c8f..148b27a86 100644
--- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/messages.properties
+++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/src/org/eclipse/equinox/internal/p2/reconciler/dropins/messages.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2007, 2009 IBM Corporation and others.
+# Copyright (c) 2007, 2010 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
@@ -7,6 +7,7 @@
#
# Contributors:
# IBM Corporation - initial API and implementation
+# Sonatype Inc. - Ongoing development
###############################################################################
artifact_repo_manager_not_registered=ArtifactRepositoryManager not registered.
errorLoadingRepository=Error occurred while loading repository at {0}.
@@ -14,4 +15,5 @@ errorProcessingConfg=Exception while processing configuration.
metadata_repo_manager_not_registered=MetadataRepositoryManager not registered.
error_reading_link = Error occurred while reading link file at {0}.
error_resolving_link = Error occurred while resolving linked folder {0} from {1}.
- \ No newline at end of file
+remove_root = The installable unit {0} version {1} has been uninstalled during the reconciliation.
+remove_all_roots = The reconciliation has been aborted because the changes made to the installation are causing the loss of all roots. \ No newline at end of file

Back to the top