NEW - bug 301903: Consider a file level lock during batch execution
https://bugs.eclipse.org/bugs/show_bug.cgi?id=301903
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF
index 10ad01e..5148f87 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF
@@ -48,6 +48,7 @@
  org.eclipse.equinox.p2.repository.artifact.spi;version="[2.0.0,3.0.0)",
  org.eclipse.equinox.p2.repository.spi;version="[2.0.0,3.0.0)",
  org.eclipse.internal.provisional.equinox.p2.jarprocessor;resolution:=optional,
+ org.eclipse.osgi.service.datalocation;version="[1.3.0, 2.0.0)",
  org.eclipse.osgi.signedcontent;version="1.0.0",
  org.eclipse.osgi.util;version="1.1.0",
  org.osgi.framework;version="1.3.0",
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/CompositeArtifactRepository.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/CompositeArtifactRepository.java
index 1b0d770..473119f 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/CompositeArtifactRepository.java
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/CompositeArtifactRepository.java
@@ -229,49 +229,49 @@
 	/**
 	 * Composite repositories should be unable to directly modify their child repositories
 	 */
-	public synchronized void addDescriptor(IArtifactDescriptor descriptor) {
+	public synchronized void addDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
 		throw new UnsupportedOperationException(Messages.exception_unsupportedAddToComposite);
 	}
 
 	/**
 	 * Composite repositories should be unable to directly modify their child repositories
 	 */
-	public void addDescriptors(IArtifactDescriptor[] descriptors) {
+	public void addDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
 		throw new UnsupportedOperationException(Messages.exception_unsupportedAddToComposite);
 	}
 
 	/**
 	 * Composite repositories should be unable to directly modify their child repositories
 	 */
-	public void removeDescriptor(IArtifactKey key) {
+	public void removeDescriptor(IArtifactKey key, IProgressMonitor monitor) {
 		throw new UnsupportedOperationException(Messages.exception_unsupportedRemoveFromComposite);
 	}
 
 	/**
 	 * Composite repositories should be unable to directly modify their child repositories
 	 */
-	public void removeDescriptors(IArtifactKey[] keys) {
+	public void removeDescriptors(IArtifactKey[] keys, IProgressMonitor monitor) {
 		throw new UnsupportedOperationException(Messages.exception_unsupportedRemoveFromComposite);
 	}
 
 	/**
 	 * Composite repositories should be unable to directly modify their child repositories
 	 */
-	public void removeDescriptor(IArtifactDescriptor descriptor) {
+	public void removeDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
 		throw new UnsupportedOperationException(Messages.exception_unsupportedRemoveFromComposite);
 	}
 
 	/**
 	 * Composite repositories should be unable to directly modify their child repositories
 	 */
-	public void removeDescriptors(IArtifactDescriptor[] descriptors) {
+	public void removeDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
 		throw new UnsupportedOperationException(Messages.exception_unsupportedRemoveFromComposite);
 	}
 
 	/**
 	 * Composite repositories should be unable to directly modify their child repositories
 	 */
-	public synchronized void removeAll() {
+	public synchronized void removeAll(IProgressMonitor monitor) {
 		throw new UnsupportedOperationException(Messages.exception_unsupportedRemoveFromComposite);
 	}
 
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java
index 37dc95e..23be4f4 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java
@@ -10,6 +10,7 @@
  * 	Genuitec, LLC - support for multi-threaded downloads
  *  Cloudsmith Inc. - query indexes
  *  Sonatype Inc - ongoing development
+ *  EclipseSource - file locking and ongoing development
  *******************************************************************************/
 package org.eclipse.equinox.internal.p2.artifact.repository.simple;
 
@@ -26,6 +27,7 @@
 import org.eclipse.equinox.internal.p2.artifact.repository.*;
 import org.eclipse.equinox.internal.p2.artifact.repository.Messages;
 import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
+import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
 import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
 import org.eclipse.equinox.internal.p2.metadata.expression.CompoundIterator;
 import org.eclipse.equinox.internal.p2.metadata.index.IndexProvider;
@@ -38,11 +40,11 @@
 import org.eclipse.equinox.p2.metadata.index.IIndex;
 import org.eclipse.equinox.p2.metadata.index.IIndexProvider;
 import org.eclipse.equinox.p2.query.*;
-import org.eclipse.equinox.p2.repository.IRepository;
-import org.eclipse.equinox.p2.repository.IRunnableWithProgress;
+import org.eclipse.equinox.p2.repository.*;
 import org.eclipse.equinox.p2.repository.artifact.*;
 import org.eclipse.equinox.p2.repository.artifact.spi.AbstractArtifactRepository;
 import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor;
+import org.eclipse.osgi.service.datalocation.Location;
 import org.eclipse.osgi.util.NLS;
 
 public class SimpleArtifactRepository extends AbstractArtifactRepository implements IFileArtifactRepository, IIndexProvider<IArtifactKey> {
@@ -67,6 +69,18 @@
 	 */
 	public static final String PROP_FORCE_THREADING = "eclipse.p2.force.threading"; //$NON-NLS-1$
 
+	/**
+	 * Location of the repository lock
+	 */
+	private Location lockLocation = null;
+	
+	/**
+	 * Does this instance of the repository currently hold a lock
+	 */
+	private boolean holdsLock = false;
+	
+	private long cacheTimestamp = 0l;
+
 	public class ArtifactOutputStream extends OutputStream implements IStateful {
 		private boolean closed;
 		private long count = 0;
@@ -304,31 +318,56 @@
 
 	public SimpleArtifactRepository(IProvisioningAgent agent, String repositoryName, URI location, Map<String, String> properties) {
 		super(agent, repositoryName, REPOSITORY_TYPE, REPOSITORY_VERSION.toString(), location, null, null, properties);
-		initializeAfterLoad(location);
-		if (properties != null) {
-			if (properties.containsKey(PUBLISH_PACK_FILES_AS_SIBLINGS)) {
-				synchronized (this) {
-					String newValue = properties.get(PUBLISH_PACK_FILES_AS_SIBLINGS);
-					if (Boolean.TRUE.toString().equals(newValue)) {
-						mappingRules = PACKED_MAPPING_RULES;
-					} else {
-						mappingRules = DEFAULT_MAPPING_RULES;
+
+		boolean lockAcquired = false;
+		try {
+			if (!holdsLock() && URIUtil.isFileURI(location)) {
+				lockAcquired = lockAndLoad(true, new NullProgressMonitor());
+				if (!lockAcquired)
+					throw new IllegalStateException("Cannot acquire the lock for " + location); //$NON-NLS-1$
+			}
+
+			initializeAfterLoad(location, false); // Don't update the timestamp, it will be done during save
+			if (properties != null) {
+				if (properties.containsKey(PUBLISH_PACK_FILES_AS_SIBLINGS)) {
+					synchronized (this) {
+						String newValue = properties.get(PUBLISH_PACK_FILES_AS_SIBLINGS);
+						if (Boolean.TRUE.toString().equals(newValue)) {
+							mappingRules = PACKED_MAPPING_RULES;
+						} else {
+							mappingRules = DEFAULT_MAPPING_RULES;
+						}
+						initializeMapper();
 					}
-					initializeMapper();
 				}
 			}
+			save();
+		} finally {
+			if (lockAcquired)
+				unlock();
 		}
-		save();
 	}
 
-	public synchronized void addDescriptor(IArtifactDescriptor toAdd) {
-		if (artifactDescriptors.contains(toAdd))
-			return;
+	public synchronized void addDescriptor(IArtifactDescriptor toAdd, IProgressMonitor monitor) {
+		boolean lockAcquired = false;
+		try {
+			if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+				lockAcquired = lockAndLoad(false, monitor);
+				if (!lockAcquired)
+					return;
+			}
 
-		SimpleArtifactDescriptor internalDescriptor = createInternalDescriptor(toAdd);
-		artifactDescriptors.add(internalDescriptor);
-		mapDescriptor(internalDescriptor);
-		save();
+			if (artifactDescriptors.contains(toAdd))
+				return;
+
+			SimpleArtifactDescriptor internalDescriptor = createInternalDescriptor(toAdd);
+			artifactDescriptors.add(internalDescriptor);
+			mapDescriptor(internalDescriptor);
+			save();
+		} finally {
+			if (lockAcquired)
+				unlock();
+		}
 	}
 
 	public IArtifactDescriptor createArtifactDescriptor(IArtifactKey key) {
@@ -356,15 +395,27 @@
 		return internal;
 	}
 
-	public synchronized void addDescriptors(IArtifactDescriptor[] descriptors) {
-		for (int i = 0; i < descriptors.length; i++) {
-			if (artifactDescriptors.contains(descriptors[i]))
-				continue;
-			SimpleArtifactDescriptor internalDescriptor = createInternalDescriptor(descriptors[i]);
-			artifactDescriptors.add(internalDescriptor);
-			mapDescriptor(internalDescriptor);
+	public synchronized void addDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
+		boolean lockAcquired = false;
+		try {
+			if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+				lockAcquired = lockAndLoad(false, monitor);
+				if (!lockAcquired)
+					return;
+			}
+
+			for (int i = 0; i < descriptors.length; i++) {
+				if (artifactDescriptors.contains(descriptors[i]))
+					continue;
+				SimpleArtifactDescriptor internalDescriptor = createInternalDescriptor(descriptors[i]);
+				artifactDescriptors.add(internalDescriptor);
+				mapDescriptor(internalDescriptor);
+			}
+			save();
+		} finally {
+			if (lockAcquired)
+				unlock();
 		}
-		save();
 	}
 
 	private synchronized OutputStream addPostSteps(ProcessingStepHandler handler, IArtifactDescriptor descriptor, OutputStream destination, IProgressMonitor monitor) {
@@ -416,11 +467,17 @@
 	}
 
 	public synchronized boolean contains(IArtifactDescriptor descriptor) {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
 		SimpleArtifactDescriptor simpleDescriptor = createInternalDescriptor(descriptor);
 		return artifactDescriptors.contains(simpleDescriptor);
 	}
 
 	public synchronized boolean contains(IArtifactKey key) {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
 		return artifactMap.containsKey(key);
 	}
 
@@ -566,6 +623,11 @@
 	}
 
 	public IStatus getArtifact(IArtifactDescriptor descriptor, OutputStream destination, IProgressMonitor monitor) {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
+		if (monitor.isCanceled())
+			return Status.CANCEL_STATUS;
 		ProcessingStepHandler handler = new ProcessingStepHandler();
 		destination = processDestination(handler, descriptor, destination, monitor);
 		IStatus status = ProcessingStepHandler.checkStatus(destination);
@@ -576,10 +638,19 @@
 	}
 
 	public IStatus getRawArtifact(IArtifactDescriptor descriptor, OutputStream destination, IProgressMonitor monitor) {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
+		if (monitor.isCanceled())
+			return Status.CANCEL_STATUS;
 		return downloadArtifact(descriptor, destination, monitor);
 	}
 
 	public synchronized IArtifactDescriptor[] getArtifactDescriptors(IArtifactKey key) {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
+
 		List<IArtifactDescriptor> result = artifactMap.get(key);
 		if (result == null)
 			return new IArtifactDescriptor[0];
@@ -602,6 +673,12 @@
 	}
 
 	public IStatus getArtifacts(IArtifactRequest[] requests, IProgressMonitor monitor) {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
+		if (monitor.isCanceled())
+			return Status.CANCEL_STATUS;
+
 		final MultiStatus overallStatus = new MultiStatus(Activator.ID, IStatus.OK, null, null);
 		LinkedList<IArtifactRequest> requestsPending = new LinkedList<IArtifactRequest>(Arrays.asList(requests));
 
@@ -643,6 +720,9 @@
 	}
 
 	public synchronized IArtifactDescriptor getCompleteArtifactDescriptor(IArtifactKey key) {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
 		List<IArtifactDescriptor> descriptors = artifactMap.get(key);
 		if (descriptors == null)
 			return null;
@@ -656,6 +736,9 @@
 	}
 
 	public synchronized Set<SimpleArtifactDescriptor> getDescriptors() {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
 		return artifactDescriptors;
 	}
 
@@ -743,6 +826,10 @@
 	}
 
 	public OutputStream getOutputStream(IArtifactDescriptor descriptor) throws ProvisionException {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
+
 		assertModifiable();
 
 		// Create a copy of the original descriptor that we can manipulate and add to our repo.
@@ -815,6 +902,9 @@
 	}
 
 	public synchronized String[][] getRules() {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
 		return mappingRules;
 	}
 
@@ -824,11 +914,17 @@
 
 	// use this method to setup any transient fields etc after the object has been restored from a stream
 	public synchronized void initializeAfterLoad(URI repoLocation) {
+		this.initializeAfterLoad(repoLocation, true);
+	}
+
+	private synchronized void initializeAfterLoad(URI repoLocation, boolean updateTimestamp) {
 		setLocation(repoLocation);
 		blobStore = new BlobStore(getBlobStoreLocation(repoLocation), 128);
 		initializeMapper();
 		for (SimpleArtifactDescriptor desc : artifactDescriptors)
 			desc.setRepository(this);
+		if (updateTimestamp)
+			updateTimestamp();
 	}
 
 	private synchronized void initializeMapper() {
@@ -870,46 +966,106 @@
 		return destination;
 	}
 
-	public synchronized void removeAll() {
-		IArtifactDescriptor[] toRemove = artifactDescriptors.toArray(new IArtifactDescriptor[artifactDescriptors.size()]);
-		boolean changed = false;
-		for (int i = 0; i < toRemove.length; i++)
-			changed |= doRemoveArtifact(toRemove[i]);
-		if (changed)
-			save();
+	public synchronized void removeAll(IProgressMonitor monitor) {
+		boolean lockAcquired = false;
+		try {
+			if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+				lockAcquired = lockAndLoad(false, monitor);
+				if (!lockAcquired)
+					return;
+			}
+
+			IArtifactDescriptor[] toRemove = artifactDescriptors.toArray(new IArtifactDescriptor[artifactDescriptors.size()]);
+			boolean changed = false;
+			for (int i = 0; i < toRemove.length; i++)
+				changed |= doRemoveArtifact(toRemove[i]);
+			if (changed)
+				save();
+		} finally {
+			if (lockAcquired)
+				unlock();
+		}
 	}
 
-	public synchronized void removeDescriptor(IArtifactDescriptor descriptor) {
-		if (doRemoveArtifact(descriptor))
-			save();
+	public synchronized void removeDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+		boolean lockAcquired = false;
+		try {
+			if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+				lockAcquired = lockAndLoad(false, monitor);
+				if (!lockAcquired)
+					return;
+			}
+
+			if (doRemoveArtifact(descriptor))
+				save();
+		} finally {
+			if (lockAcquired)
+				unlock();
+		}
 	}
 
-	public synchronized void removeDescriptors(IArtifactDescriptor[] descriptors) {
-		boolean changed = false;
-		for (IArtifactDescriptor descriptor : descriptors)
-			changed |= doRemoveArtifact(descriptor);
-		if (changed)
-			save();
-	}
+	public synchronized void removeDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
+		boolean lockAcquired = false;
+		try {
+			if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+				lockAcquired = lockAndLoad(false, monitor);
+				if (!lockAcquired)
+					return;
+			}
 
-	public synchronized void removeDescriptors(IArtifactKey[] keys) {
-		boolean changed = false;
-		for (IArtifactKey key : keys) {
-			IArtifactDescriptor[] descriptors = getArtifactDescriptors(key);
+			boolean changed = false;
 			for (IArtifactDescriptor descriptor : descriptors)
 				changed |= doRemoveArtifact(descriptor);
+			if (changed)
+				save();
+		} finally {
+			if (lockAcquired)
+				unlock();
 		}
-		if (changed)
-			save();
 	}
 
-	public synchronized void removeDescriptor(IArtifactKey key) {
-		IArtifactDescriptor[] toRemove = getArtifactDescriptors(key);
-		boolean changed = false;
-		for (int i = 0; i < toRemove.length; i++)
-			changed |= doRemoveArtifact(toRemove[i]);
-		if (changed)
-			save();
+	public synchronized void removeDescriptors(IArtifactKey[] keys, IProgressMonitor monitor) {
+		boolean lockAcquired = false;
+		try {
+			if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+				lockAcquired = lockAndLoad(false, monitor);
+				if (!lockAcquired)
+					return;
+			}
+
+			boolean changed = false;
+			for (IArtifactKey key : keys) {
+				IArtifactDescriptor[] descriptors = getArtifactDescriptors(key);
+				for (IArtifactDescriptor descriptor : descriptors)
+					changed |= doRemoveArtifact(descriptor);
+			}
+			if (changed)
+				save();
+		} finally {
+			if (lockAcquired)
+				unlock();
+		}
+	}
+
+	public synchronized void removeDescriptor(IArtifactKey key, IProgressMonitor monitor) {
+		boolean lockAcquired = false;
+		try {
+			if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+				lockAcquired = lockAndLoad(false, monitor);
+				if (!lockAcquired)
+					return;
+			}
+
+			IArtifactDescriptor[] toRemove = getArtifactDescriptors(key);
+			boolean changed = false;
+			for (int i = 0; i < toRemove.length; i++)
+				changed |= doRemoveArtifact(toRemove[i]);
+			if (changed)
+				save();
+		} finally {
+			if (lockAcquired)
+				unlock();
+		}
 	}
 
 	private IStatus reportStatus(IArtifactDescriptor descriptor, OutputStream destination, IStatus status) {
@@ -989,7 +1145,7 @@
 					jOs.putNextEntry(new JarEntry(new Path(artifactsFile.getAbsolutePath()).lastSegment()));
 					os = jOs;
 				}
-				super.setProperty(IRepository.PROP_TIMESTAMP, Long.toString(System.currentTimeMillis()));
+				super.setProperty(IRepository.PROP_TIMESTAMP, Long.toString(System.currentTimeMillis()), new NullProgressMonitor());
 				new SimpleArtifactRepositoryIO(getProvisioningAgent()).write(this, os);
 			} catch (IOException e) {
 				// TODO proper exception handling
@@ -997,14 +1153,15 @@
 			} finally {
 				if (os != null)
 					os.close();
+				updateTimestamp();
 			}
 		} catch (IOException e) {
 			e.printStackTrace();
 		}
 	}
 
-	public String setProperty(String key, String newValue) {
-		String oldValue = super.setProperty(key, newValue);
+	private String doSetProperty(String key, String newValue, IProgressMonitor monitor, boolean save) {
+		String oldValue = super.setProperty(key, newValue, new NullProgressMonitor());
 		if (oldValue == newValue || (oldValue != null && oldValue.equals(newValue)))
 			return oldValue;
 		if (PUBLISH_PACK_FILES_AS_SIBLINGS.equals(key)) {
@@ -1017,14 +1174,26 @@
 				initializeMapper();
 			}
 		}
-		save();
-		//force repository manager to reload this repository because it caches properties
-		ArtifactRepositoryManager manager = (ArtifactRepositoryManager) getProvisioningAgent().getService(IArtifactRepositoryManager.SERVICE_NAME);
-		if (manager.removeRepository(getLocation()))
-			manager.addRepository(this);
+		if (save)
+			save();
 		return oldValue;
 	}
 
+	public String setProperty(String key, String newValue, IProgressMonitor monitor) {
+		boolean lockAcquired = false;
+		try {
+			if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+				lockAcquired = lockAndLoad(false, monitor);
+				if (!lockAcquired)
+					return super.getProperty(key);
+			}
+			return doSetProperty(key, newValue, monitor, true);
+		} finally {
+			if (lockAcquired)
+				unlock();
+		}
+	}
+
 	public synchronized void setRules(String[][] rules) {
 		mappingRules = rules;
 	}
@@ -1050,14 +1219,25 @@
 	}
 
 	public synchronized Iterator<IArtifactKey> everything() {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
 		snapshotNeeded = true;
 		return artifactMap.keySet().iterator();
 	}
 
 	public IStatus executeBatch(IRunnableWithProgress runnable, IProgressMonitor monitor) {
 		IStatus result = null;
+
+		boolean lockAcquired = false;
 		synchronized (this) {
 			try {
+				if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+					lockAcquired = lockAndLoad(false, monitor);
+					if (!lockAcquired)
+						return new Status(IStatus.ERROR, Activator.ID, "Could not lock artifact repository for writing", null); //$NON-NLS-1$
+				}
+
 				disableSave = true;
 				runnable.run(monitor);
 			} catch (OperationCanceledException oce) {
@@ -1073,6 +1253,9 @@
 						result = new MultiStatus(Activator.ID, IStatus.ERROR, new IStatus[] {result}, e.getMessage(), e);
 					else
 						result = new Status(IStatus.ERROR, Activator.ID, e.getMessage(), e);
+				} finally {
+					if (lockAcquired)
+						unlock();
 				}
 			}
 		}
@@ -1082,6 +1265,9 @@
 	}
 
 	public synchronized IIndex<IArtifactKey> getIndex(String memberName) {
+		if (!holdsLock() && URIUtil.isFileURI(getLocation())) {
+			load(new NullProgressMonitor());
+		}
 		if (ArtifactKey.MEMBER_ID.equals(memberName)) {
 			snapshotNeeded = true;
 			if (keyIndex == null)
@@ -1094,4 +1280,200 @@
 	public Object getManagedProperty(Object client, String memberName, Object key) {
 		return null;
 	}
+
+	/**
+	 * Locks the location and optionally loads the repository.
+	 * 
+	 * @param ignoreLoad If ignoreLoad is set to true, then the location is locked
+	 *                   but the repository is not loaded.  It is expected
+	 *                   that the caller will load the repository manually
+	 * @return Tue if the lock was acquired, false otherwise
+	 */
+	private synchronized boolean lockAndLoad(boolean ignoreLoad, IProgressMonitor monitor) {
+		if (holdsLock) {
+			throw new IllegalStateException("Locking is not reentrant"); //$NON-NLS-1$
+		}
+		holdsLock = false;
+		boolean success = true;
+
+		try {
+			try {
+				holdsLock = lock(true, monitor);
+			} catch (Exception e) {
+				e.printStackTrace();
+				return false;
+			}
+
+			if (holdsLock) {
+				if (!ignoreLoad) {
+					success = false;
+					doLoad(new NullProgressMonitor());
+					success = true;
+				}
+				return true;
+			}
+			return false;
+		} finally {
+			// If we did not successfully load the repository, make sure we free the lock.
+			// This will only happen if doLoad() throws an exception, otherwise
+			// we will set success to true, and return above
+			if (!success)
+				unlock();
+		}
+	}
+
+	/**
+	 * Actually lock the location.  This method should only be called
+	 * from LockAndLoad. If you only want to lock the repository and not
+	 * load it, see {@link SimpleArtifactRepository#lockAndLoad(boolean)}.
+	 */
+	private synchronized boolean lock(boolean wait, IProgressMonitor monitor) throws IOException {
+		if (holdsLock()) {
+			throw new IllegalStateException("Locking is not reentrant"); //$NON-NLS-1$
+		}
+
+		lockLocation = getLockLocation();
+		boolean locked = lockLocation.lock();
+		if (locked || !wait)
+			return locked;
+
+		//Someone else must have the directory locked
+		while (true) {
+			if (monitor.isCanceled())
+				return false;
+			try {
+				Thread.sleep(200); // 5x per second
+			} catch (InterruptedException e) {/*ignore*/
+			}
+			locked = lockLocation.lock();
+			if (locked)
+				return true;
+		}
+	}
+
+	/**
+	 * Returns true if this instance of SimpleArtifactRepository holds the lock,
+	 * false otherwise.
+	 */
+	private boolean holdsLock() {
+		return holdsLock;
+	}
+
+	/**URIUtil.toURI(location.toURI()
+	 * Returns the location of the lock file.
+	 */
+	private Location getLockLocation() throws IOException {
+		if (this.lockLocation != null)
+			return this.lockLocation;
+
+		// TODO: Throw an IO Exception if we cannot lock this location
+		Location anyLoc = (Location) ServiceHelper.getService(Activator.getContext(), Location.class.getName());
+		Location location = anyLoc.createLocation(null, getLockFile().toURL(), false);
+		location.set(getLockFile().toURL(), false);
+		return location;
+	}
+
+	private File getLockFile() throws IOException {
+		URI repositoryLocation = getLocation();
+		if (!URIUtil.isFileURI(repositoryLocation)) {
+			throw new IOException("Cannot lock a non file based repository"); //$NON-NLS-1$
+		}
+		URI result = URIUtil.append(repositoryLocation, ".artifactlock"); //$NON-NLS-1$
+		return URIUtil.toFile(result);
+	}
+
+	/**
+	 * Loads the repository from disk. This method will do nothing
+	 * if this instance of SimpleArtifactRepository holds the lock
+	 * because it will have loaded the repo when it acquired the lock.
+	 * 
+	 * @param monitor
+	 */
+	private void load(IProgressMonitor monitor) {
+		if (!holdsLock())
+			doLoad(monitor);
+		else
+			monitor.done();
+	}
+
+	private void updateTimestamp() {
+		if (!isModifiable())
+			return;
+		try {
+			SimpleArtifactRepositoryFactory repositoryFactory = new SimpleArtifactRepositoryFactory();
+			File localFile = repositoryFactory.getLocalFile(getLocation(), new NullProgressMonitor());
+			long lastModified = localFile.lastModified();
+			if (lastModified > 0)
+				cacheTimestamp = lastModified;
+		} catch (Exception e) {
+			// Do nothing
+		}
+	}
+
+	/**
+	 * Loads the repository from disk. If the last modified timestamp on the file <=
+	 * to our cache, then this method does nothing.  Otherwise the artifact repository
+	 * on disk is loaded, and reconciled with this instance of the artifact repository.
+	 * 
+	 * @param monitor
+	 */
+	private void doLoad(IProgressMonitor monitor) {
+
+		SimpleArtifactRepositoryFactory repositoryFactory = new SimpleArtifactRepositoryFactory();
+		IArtifactRepository repositoryOnDisk = null;
+		try {
+			SubMonitor subMonitor = SubMonitor.convert(monitor, 4);
+			try {
+				File localFile = repositoryFactory.getLocalFile(getLocation(), subMonitor.newChild(1));
+				long lastModified = localFile.lastModified();
+				if (lastModified <= cacheTimestamp)
+					return;
+				cacheTimestamp = lastModified;
+			} catch (Exception e) {
+				// Dont'r worry if we can't load
+				return;
+			}
+			try {
+				repositoryOnDisk = repositoryFactory.load(getLocation(), IRepositoryManager.REPOSITORY_HINT_MODIFIABLE, subMonitor.newChild(3), false);
+			} catch (Exception e) {
+				// Don't worry if we can't load
+				return;
+			}
+
+			if (repositoryOnDisk != null && repositoryOnDisk instanceof SimpleArtifactRepository) {
+				setName(repositoryOnDisk.getName());
+				setType(repositoryOnDisk.getType());
+				setVersion(repositoryOnDisk.getVersion());
+				setLocation(repositoryOnDisk.getLocation()); // Will this ever change, should it?
+				setDescription(repositoryOnDisk.getDescription());
+				setProvider(repositoryOnDisk.getProvider());
+				this.mappingRules = ((SimpleArtifactRepository) repositoryOnDisk).mappingRules;
+
+				// Clear the existing properties
+				//				this.setProperties(new OrderedProperties());
+				//
+				Map<String, String> prop = repositoryOnDisk.getProperties();
+				Set<Entry<String, String>> entrySet = prop.entrySet();
+				for (Entry<String, String> entry : entrySet) {
+					doSetProperty(entry.getKey(), entry.getValue(), new NullProgressMonitor(), false);
+				}
+
+				//
+				this.artifactDescriptors = ((SimpleArtifactRepository) repositoryOnDisk).artifactDescriptors;
+				this.artifactMap = ((SimpleArtifactRepository) repositoryOnDisk).artifactMap;
+			}
+		} finally {
+			monitor.done();
+		}
+		return;
+	}
+
+	private void unlock() {
+		if (lockLocation != null) {
+			// If we don't have the lock location, then we don't have the lock
+			holdsLock = false;
+			lockLocation.release();
+		}
+		lockLocation = null;
+	}
 }
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepositoryFactory.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepositoryFactory.java
index 4ebeb06..6454b29 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepositoryFactory.java
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepositoryFactory.java
@@ -40,7 +40,7 @@
 	 * Returns a file in the local file system that contains the contents of the
 	 * metadata repository at the given location.
 	 */
-	private File getLocalFile(URI location, IProgressMonitor monitor) throws IOException, ProvisionException {
+	File getLocalFile(URI location, IProgressMonitor monitor) throws IOException, ProvisionException {
 		File localFile = null;
 		URI jarLocation = SimpleArtifactRepository.getActualLocation(location, true);
 		URI xmlLocation = SimpleArtifactRepository.getActualLocation(location, false);
@@ -72,6 +72,10 @@
 	}
 
 	public IArtifactRepository load(URI location, int flags, IProgressMonitor monitor) throws ProvisionException {
+		return load(location, flags, monitor, true);
+	}
+
+	IArtifactRepository load(URI location, int flags, IProgressMonitor monitor, boolean acquireLock) throws ProvisionException {
 		long time = 0;
 		final String debugMsg = "Restoring artifact repository "; //$NON-NLS-1$
 		if (Tracing.DEBUG_METADATA_PARSING) {
@@ -100,7 +104,7 @@
 				sub.setWorkRemaining(100);
 				InputStream descriptorStream = jarStream != null ? jarStream : inStream;
 				SimpleArtifactRepositoryIO io = new SimpleArtifactRepositoryIO(getAgent());
-				SimpleArtifactRepository result = (SimpleArtifactRepository) io.read(localFile.toURL(), descriptorStream, sub.newChild(100));
+				SimpleArtifactRepository result = (SimpleArtifactRepository) io.read(location, descriptorStream, sub.newChild(100), acquireLock);
 				result.initializeAfterLoad(location);
 				if (result != null && (flags & IRepositoryManager.REPOSITORY_HINT_MODIFIABLE) > 0 && !result.isModifiable())
 					return null;
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepositoryIO.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepositoryIO.java
index dfad547..0008ad1 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepositoryIO.java
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepositoryIO.java
@@ -11,14 +11,13 @@
 package org.eclipse.equinox.internal.p2.artifact.repository.simple;
 
 import java.io.*;
-import java.net.URL;
+import java.net.URI;
 import java.util.*;
 import javax.xml.parsers.ParserConfigurationException;
 import org.eclipse.core.runtime.*;
 import org.eclipse.equinox.internal.p2.artifact.repository.Activator;
 import org.eclipse.equinox.internal.p2.artifact.repository.Messages;
-import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
-import org.eclipse.equinox.internal.p2.core.helpers.OrderedProperties;
+import org.eclipse.equinox.internal.p2.core.helpers.*;
 import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
 import org.eclipse.equinox.internal.p2.persistence.XMLParser;
 import org.eclipse.equinox.internal.p2.persistence.XMLWriter;
@@ -28,6 +27,7 @@
 import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
 import org.eclipse.equinox.p2.repository.artifact.IProcessingStepDescriptor;
 import org.eclipse.equinox.p2.repository.artifact.spi.ProcessingStepDescriptor;
+import org.eclipse.osgi.service.datalocation.Location;
 import org.eclipse.osgi.util.NLS;
 import org.osgi.framework.BundleContext;
 import org.xml.sax.*;
@@ -44,6 +44,7 @@
 public class SimpleArtifactRepositoryIO {
 
 	private final IProvisioningAgent agent;
+	private Location lockLocation = null;
 
 	public SimpleArtifactRepositoryIO(IProvisioningAgent agent) {
 		this.agent = agent;
@@ -77,15 +78,32 @@
 	 * 
 	 * This method performs buffering, and closes the stream when finished.
 	 */
-	public IArtifactRepository read(URL location, InputStream input, IProgressMonitor monitor) throws ProvisionException {
+	public IArtifactRepository read(URI location, InputStream input, IProgressMonitor monitor, boolean acquireLock) throws ProvisionException {
 		BufferedInputStream bufferedInput = null;
 		try {
 			try {
 				bufferedInput = new BufferedInputStream(input);
 				Parser repositoryParser = new Parser(Activator.getContext(), Activator.ID);
-				repositoryParser.setErrorContext(location.toExternalForm());
-				repositoryParser.parse(input);
-				IStatus result = repositoryParser.getStatus();
+				repositoryParser.setErrorContext(location.toURL().toExternalForm());
+				IStatus result = null;
+				boolean lock = false;
+				try {
+					if (URIUtil.isFileURI(location) && acquireLock)
+						lock = lock(location, true, monitor);
+					else
+						lock = true; // No need to lock
+					if (lock) {
+						repositoryParser.parse(input);
+						result = repositoryParser.getStatus();
+					} else
+						result = Status.CANCEL_STATUS;
+				} finally {
+					if (lock)
+						unlock(location);
+					else
+						result = Status.CANCEL_STATUS;
+				}
+
 				switch (result.getSeverity()) {
 					case IStatus.CANCEL :
 						throw new OperationCanceledException();
@@ -109,6 +127,50 @@
 		}
 	}
 
+	private synchronized boolean lock(URI repositoryLocation, boolean wait, IProgressMonitor monitor) throws IOException {
+		lockLocation = getLockLocation(repositoryLocation);
+		boolean locked = lockLocation.lock();
+		if (locked || !wait)
+			return locked;
+
+		//Someone else must have the directory locked
+		while (true) {
+			if (monitor.isCanceled())
+				return false;
+			try {
+				Thread.sleep(200); // 5x per second
+			} catch (InterruptedException e) {/*ignore*/
+			}
+			locked = lockLocation.lock();
+			if (locked)
+				return true;
+		}
+	}
+
+	private void unlock(URI repositoryLocation) {
+		if (lockLocation != null) {
+			lockLocation.release();
+		}
+	}
+
+	/**
+	 * Returns the location of the lock file.
+	 */
+	private Location getLockLocation(URI repositoryLocation) throws IOException {
+		Location anyLoc = (Location) ServiceHelper.getService(Activator.getContext(), Location.class.getName());
+		Location location = anyLoc.createLocation(null, getLockFile(repositoryLocation).toURL(), false);
+		location.set(getLockFile(repositoryLocation).toURL(), false);
+		return location;
+	}
+
+	private File getLockFile(URI repositoryLocation) throws IOException {
+		if (!URIUtil.isFileURI(repositoryLocation)) {
+			throw new IOException("Cannot lock a non file based repository"); //$NON-NLS-1$
+		}
+		URI result = URIUtil.append(repositoryLocation, ".artifactlock"); //$NON-NLS-1$
+		return URIUtil.toFile(result);
+	}
+
 	private interface XMLConstants extends org.eclipse.equinox.internal.p2.persistence.XMLConstants {
 
 		// Constants defining the structure of the XML for a SimpleArtifactRepository
diff --git a/bundles/org.eclipse.equinox.p2.directorywatcher/src/org/eclipse/equinox/internal/provisional/p2/directorywatcher/CachingArtifactRepository.java b/bundles/org.eclipse.equinox.p2.directorywatcher/src/org/eclipse/equinox/internal/provisional/p2/directorywatcher/CachingArtifactRepository.java
index 05720ff..66cfd17 100644
--- a/bundles/org.eclipse.equinox.p2.directorywatcher/src/org/eclipse/equinox/internal/provisional/p2/directorywatcher/CachingArtifactRepository.java
+++ b/bundles/org.eclipse.equinox.p2.directorywatcher/src/org/eclipse/equinox/internal/provisional/p2/directorywatcher/CachingArtifactRepository.java
@@ -93,18 +93,43 @@
 			artifactMap.remove(key);
 	}
 
-	public synchronized void addDescriptors(IArtifactDescriptor[] descriptors) {
-		for (int i = 0; i < descriptors.length; i++) {
-			((ArtifactDescriptor) descriptors[i]).setRepository(this);
-			descriptorsToAdd.add(descriptors[i]);
-			mapDescriptor(descriptors[i]);
+	public synchronized void addDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
+		try {
+			SubMonitor subMonitor = SubMonitor.convert(monitor, descriptors.length);
+			for (int i = 0; i < descriptors.length; i++) {
+				((ArtifactDescriptor) descriptors[i]).setRepository(this);
+				descriptorsToAdd.add(descriptors[i]);
+				mapDescriptor(descriptors[i]);
+				subMonitor.worked(1);
+			}
+		} finally {
+			if (monitor != null)
+				monitor.done();
 		}
 	}
 
-	public synchronized void addDescriptor(IArtifactDescriptor toAdd) {
-		((ArtifactDescriptor) toAdd).setRepository(this);
-		descriptorsToAdd.add(toAdd);
-		mapDescriptor(toAdd);
+	@Deprecated
+	public final synchronized void addDescriptors(IArtifactDescriptor[] descriptors) {
+		addDescriptors(descriptors, new NullProgressMonitor());
+	}
+
+	public synchronized void addDescriptor(IArtifactDescriptor toAdd, IProgressMonitor monitor) {
+		try {
+			SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
+
+			((ArtifactDescriptor) toAdd).setRepository(this);
+			descriptorsToAdd.add(toAdd);
+			mapDescriptor(toAdd);
+			subMonitor.worked(1);
+		} finally {
+			if (monitor != null)
+				monitor.done();
+		}
+	}
+
+	@Deprecated
+	public final synchronized void addDescriptor(IArtifactDescriptor toAdd) {
+		addDescriptor(toAdd, new NullProgressMonitor());
 	}
 
 	public synchronized IArtifactDescriptor[] getArtifactDescriptors(IArtifactKey key) {
@@ -140,30 +165,93 @@
 		return null;
 	}
 
+	public synchronized final void removeAll(IProgressMonitor monitor) {
+		try {
+			SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
+			IArtifactDescriptor[] toRemove = descriptorsToAdd.toArray(new IArtifactDescriptor[descriptorsToAdd.size()]);
+			for (int i = 0; i < toRemove.length; i++)
+				doRemoveArtifact(toRemove[i]);
+			subMonitor.worked(1);
+		} finally {
+			if (monitor != null)
+				monitor.done();
+		}
+	}
+
+	@Deprecated
 	public synchronized void removeAll() {
-		IArtifactDescriptor[] toRemove = descriptorsToAdd.toArray(new IArtifactDescriptor[descriptorsToAdd.size()]);
-		for (int i = 0; i < toRemove.length; i++)
-			doRemoveArtifact(toRemove[i]);
+		this.removeAll(new NullProgressMonitor());
 	}
 
-	public synchronized void removeDescriptor(IArtifactDescriptor descriptor) {
-		doRemoveArtifact(descriptor);
-	}
-
-	public synchronized void removeDescriptor(IArtifactKey key) {
-		IArtifactDescriptor[] toRemove = getArtifactDescriptors(key);
-		for (int i = 0; i < toRemove.length; i++)
-			doRemoveArtifact(toRemove[i]);
-	}
-
-	public synchronized void removeDescriptors(IArtifactDescriptor[] descriptors) {
-		for (IArtifactDescriptor descriptor : descriptors)
+	public synchronized void removeDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+		try {
+			SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
 			doRemoveArtifact(descriptor);
+			subMonitor.worked(1);
+		} finally {
+			if (monitor != null)
+				monitor.done();
+		}
 	}
 
-	public synchronized void removeDescriptors(IArtifactKey[] keys) {
-		for (IArtifactKey key : keys)
-			removeDescriptor(key);
+	@Deprecated
+	public final synchronized void removeDescriptor(IArtifactDescriptor descriptor) {
+		removeDescriptor(descriptor, new NullProgressMonitor());
+	}
+
+	public synchronized void removeDescriptor(IArtifactKey key, IProgressMonitor monitor) {
+		try {
+			IArtifactDescriptor[] toRemove = getArtifactDescriptors(key);
+			SubMonitor subMonitor = SubMonitor.convert(monitor, toRemove.length);
+			for (int i = 0; i < toRemove.length; i++) {
+				doRemoveArtifact(toRemove[i]);
+				subMonitor.worked(1);
+			}
+		} finally {
+			if (monitor != null)
+				monitor.done();
+		}
+	}
+
+	@Deprecated
+	public final synchronized void removeDescriptor(IArtifactKey key) {
+		this.removeDescriptor(key, new NullProgressMonitor());
+	}
+
+	public synchronized void removeDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
+		try {
+			SubMonitor subMonitor = SubMonitor.convert(monitor, descriptors.length);
+			for (IArtifactDescriptor descriptor : descriptors) {
+				doRemoveArtifact(descriptor);
+				subMonitor.worked(1);
+			}
+		} finally {
+			if (monitor != null)
+				monitor.done();
+		}
+	}
+
+	@Deprecated
+	public final synchronized void removeDescriptors(IArtifactDescriptor[] descriptors) {
+		removeDescriptors(descriptors, new NullProgressMonitor());
+	}
+
+	public synchronized void removeDescriptors(IArtifactKey[] keys, IProgressMonitor monitor) {
+		try {
+			SubMonitor subMonitor = SubMonitor.convert(monitor, keys.length);
+			for (IArtifactKey key : keys) {
+				removeDescriptor(key);
+				subMonitor.worked(1);
+			}
+		} finally {
+			if (monitor != null)
+				monitor.done();
+		}
+	}
+
+	@Deprecated
+	public final synchronized void removeDescriptors(IArtifactKey[] keys) {
+		removeDescriptors(keys, new NullProgressMonitor());
 	}
 
 	/**
@@ -221,10 +309,19 @@
 		return innerRepo.isModifiable();
 	}
 
-	public String setProperty(String key, String value) {
-		String result = getProperties().get(key);
-		propertyChanges.put(key, value == null ? NULL : value);
-		return result;
+	public String setProperty(String key, String value, IProgressMonitor monitor) {
+		try {
+			String result = getProperties().get(key);
+			propertyChanges.put(key, value == null ? NULL : value);
+			return result;
+		} finally {
+			if (monitor != null)
+				monitor.done();
+		}
+	}
+
+	public final String setProperty(String key, String value) {
+		return setProperty(key, value, new NullProgressMonitor());
 	}
 
 	@SuppressWarnings("rawtypes")
diff --git a/bundles/org.eclipse.equinox.p2.extensionlocation/src/org/eclipse/equinox/internal/p2/extensionlocation/ExtensionLocationArtifactRepository.java b/bundles/org.eclipse.equinox.p2.extensionlocation/src/org/eclipse/equinox/internal/p2/extensionlocation/ExtensionLocationArtifactRepository.java
index 5bdb8ad..5bcb42d 100644
--- a/bundles/org.eclipse.equinox.p2.extensionlocation/src/org/eclipse/equinox/internal/p2/extensionlocation/ExtensionLocationArtifactRepository.java
+++ b/bundles/org.eclipse.equinox.p2.extensionlocation/src/org/eclipse/equinox/internal/p2/extensionlocation/ExtensionLocationArtifactRepository.java
@@ -147,30 +147,65 @@
 		return plugins.isDirectory() || features.isDirectory();
 	}
 
+	public void addDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Deprecated
 	public void addDescriptor(IArtifactDescriptor descriptor) {
 		throw new UnsupportedOperationException();
 	}
 
+	public void addDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Deprecated
 	public void addDescriptors(IArtifactDescriptor[] descriptors) {
 		throw new UnsupportedOperationException();
 	}
 
+	public void removeAll(IProgressMonitor monitor) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Deprecated
 	public void removeAll() {
 		throw new UnsupportedOperationException();
 	}
 
+	public void removeDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Deprecated
 	public void removeDescriptor(IArtifactDescriptor descriptor) {
 		throw new UnsupportedOperationException();
 	}
 
+	public void removeDescriptor(IArtifactKey key, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Deprecated
 	public void removeDescriptor(IArtifactKey key) {
 		throw new UnsupportedOperationException();
 	}
 
+	public void removeDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Deprecated
 	public void removeDescriptors(IArtifactDescriptor[] descriptors) {
 		throw new UnsupportedOperationException();
 	}
 
+	public void removeDescriptors(IArtifactKey[] keys, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Deprecated
 	public void removeDescriptors(IArtifactKey[] keys) {
 		throw new UnsupportedOperationException();
 	}
@@ -225,18 +260,23 @@
 		return artifactRepository.getProperties();
 	}
 
-	public String setProperty(String key, String value) {
-		ensureInitialized();
-		String oldValue = artifactRepository.setProperty(key, value);
-		// if the value didn't really change then just return
-		if (oldValue == value || (oldValue != null && oldValue.equals(value)))
+	public String setProperty(String key, String value, IProgressMonitor monitor) {
+		try {
+			ensureInitialized();
+			String oldValue = artifactRepository.setProperty(key, value);
+			// if the value didn't really change then just return
+			if (oldValue == value || (oldValue != null && oldValue.equals(value)))
+				return oldValue;
+			// we want to re-initialize if we are changing the site policy or plug-in list
+			if (!SiteListener.SITE_LIST.equals(key) && !SiteListener.SITE_POLICY.equals(key))
+				return oldValue;
+			state = SiteListener.UNINITIALIZED;
+			ensureInitialized();
 			return oldValue;
-		// we want to re-initialize if we are changing the site policy or plug-in list
-		if (!SiteListener.SITE_LIST.equals(key) && !SiteListener.SITE_POLICY.equals(key))
-			return oldValue;
-		state = SiteListener.UNINITIALIZED;
-		ensureInitialized();
-		return oldValue;
+		} finally {
+			if (monitor != null)
+				monitor.done();
+		}
 	}
 
 	public IArtifactDescriptor createArtifactDescriptor(IArtifactKey key) {
diff --git a/bundles/org.eclipse.equinox.p2.extensionlocation/src/org/eclipse/equinox/internal/p2/extensionlocation/ExtensionLocationMetadataRepository.java b/bundles/org.eclipse.equinox.p2.extensionlocation/src/org/eclipse/equinox/internal/p2/extensionlocation/ExtensionLocationMetadataRepository.java
index 55f0d0c..4c0651d 100644
--- a/bundles/org.eclipse.equinox.p2.extensionlocation/src/org/eclipse/equinox/internal/p2/extensionlocation/ExtensionLocationMetadataRepository.java
+++ b/bundles/org.eclipse.equinox.p2.extensionlocation/src/org/eclipse/equinox/internal/p2/extensionlocation/ExtensionLocationMetadataRepository.java
@@ -207,17 +207,22 @@
 	 * @see org.eclipse.equinox.p2.repository.spi.AbstractRepository#setProperty(java.lang.String, java.lang.String)
 	 */
 	@Override
-	public String setProperty(String key, String value) {
-		ensureInitialized();
-		String oldValue = metadataRepository.setProperty(key, value);
-		// if the value didn't really change then just return
-		if (oldValue == value || (oldValue != null && oldValue.equals(value)))
+	public String setProperty(String key, String value, IProgressMonitor monitor) {
+		try {
+			ensureInitialized();
+			String oldValue = metadataRepository.setProperty(key, value);
+			// if the value didn't really change then just return
+			if (oldValue == value || (oldValue != null && oldValue.equals(value)))
+				return oldValue;
+			// we want to re-initialize if we are changing the site policy or plug-in list
+			if (!SiteListener.SITE_LIST.equals(key) && !SiteListener.SITE_POLICY.equals(key))
+				return oldValue;
+			state = SiteListener.UNINITIALIZED;
+			ensureInitialized();
 			return oldValue;
-		// we want to re-initialize if we are changing the site policy or plug-in list
-		if (!SiteListener.SITE_LIST.equals(key) && !SiteListener.SITE_POLICY.equals(key))
-			return oldValue;
-		state = SiteListener.UNINITIALIZED;
-		ensureInitialized();
-		return oldValue;
+		} finally {
+			if (monitor != null)
+				monitor.done();
+		}
 	}
 }
diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/LocalMetadataRepository.java b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/LocalMetadataRepository.java
index 7421338..b3ef966 100644
--- a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/LocalMetadataRepository.java
+++ b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/LocalMetadataRepository.java
@@ -314,19 +314,24 @@
 	 * @see org.eclipse.equinox.p2.repository.spi.AbstractRepository#setProperty(java.lang.String, java.lang.String)
 	 */
 	@Override
-	public String setProperty(String key, String newValue) {
-		String oldValue = null;
-		synchronized (this) {
-			oldValue = super.setProperty(key, newValue);
-			if (oldValue == newValue || (oldValue != null && oldValue.equals(newValue)))
-				return oldValue;
-			save();
+	public String setProperty(String key, String newValue, IProgressMonitor monitor) {
+		try {
+			String oldValue = null;
+			synchronized (this) {
+				oldValue = super.setProperty(key, newValue, monitor);
+				if (oldValue == newValue || (oldValue != null && oldValue.equals(newValue)))
+					return oldValue;
+				save();
+			}
+			//force repository manager to reload this repository because it caches properties
+			MetadataRepositoryManager manager = (MetadataRepositoryManager) getProvisioningAgent().getService(IMetadataRepositoryManager.SERVICE_NAME);
+			if (manager.removeRepository(getLocation()))
+				manager.addRepository(this);
+			return oldValue;
+		} finally {
+			if (monitor != null)
+				monitor.done();
 		}
-		//force repository manager to reload this repository because it caches properties
-		MetadataRepositoryManager manager = (MetadataRepositoryManager) getProvisioningAgent().getService(IMetadataRepositoryManager.SERVICE_NAME);
-		if (manager.removeRepository(getLocation()))
-			manager.addRepository(this);
-		return oldValue;
 	}
 
 	public IStatus executeBatch(IRunnableWithProgress runnable, IProgressMonitor monitor) {
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/IRepository.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/IRepository.java
index 35a722a..1bccddc 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/IRepository.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/IRepository.java
@@ -13,6 +13,7 @@
 import java.net.URI;
 import java.util.Map;
 import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.equinox.p2.core.IProvisioningAgent;
 import org.eclipse.equinox.p2.query.IQueryable;
 
@@ -204,4 +205,18 @@
 	 * @return The old property value, or <code>null</code> if there was no old value
 	 */
 	public String setProperty(String key, String value);
+
+	/**
+	 * Sets the value of the property with the given key. Returns the old property
+	 * associated with that key, if any.  Setting a value of <code>null</code> will
+	 * remove the corresponding key from the properties of this repository.
+	 * 
+	 * @param key The property key
+	 * @param value The new property value, or <code>null</code> to remove the key
+	 * @param monitor A progress monitor use to track progress and cancel the operation.  This may
+	 * be a long running operation if another process holds the lock on this location
+	 * @return The old property value, or <code>null</code> if there was no old value
+	 * @since 2.1
+	 */
+	public String setProperty(String key, String value, IProgressMonitor monitor);
 }
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/IArtifactRepository.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/IArtifactRepository.java
index 3e68f2b..28180b7 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/IArtifactRepository.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/IArtifactRepository.java
@@ -62,15 +62,38 @@
 	 * content is in this repository and the given descriptor accurately describes 
 	 * that content.
 	 * @param descriptor the descriptor to add.
+	 * @deprecated See {{@link #addDescriptor(IArtifactDescriptor, IProgressMonitor)}
 	 */
 	public void addDescriptor(IArtifactDescriptor descriptor);
 
 	/**
+	 * Add the given descriptor to the set of descriptors in this repository.  This is 
+	 * a relatively low-level operation that should be used only when the actual related 
+	 * content is in this repository and the given descriptor accurately describes 
+	 * that content.
+	 * @param descriptor the descriptor to add.
+	 * @param monitor A progress monitor use to track progress and cancel the operation.  This may
+	 * be a long running operation if another process holds the lock on this location
+	 * @since 2.1
+	 */
+	public void addDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor);
+
+	/**
 	 * Add the given artifact descriptors to this repository
 	 * @param descriptors the artifact descriptors to add
+	 * @deprecated See {{@link #addDescriptors(IArtifactDescriptor[], IProgressMonitor)}
 	 */
 	public void addDescriptors(IArtifactDescriptor[] descriptors);
 
+	/**
+	 * Add the given artifact descriptors to this repository
+	 * @param descriptors the artifact descriptors to add
+	 * @param monitor A progress monitor use to track progress and cancel the operation.  This may
+	 * be a long running operation if another process holds the lock on this location
+	 * @since 2.1
+	 */
+	public void addDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor);
+
 	/** 
 	 * Returns true if this repository contains the given descriptor.
 	 * @param descriptor the descriptor to query
@@ -151,38 +174,89 @@
 
 	/**
 	 * Remove the all keys, descriptors, and contents from this repository.
+	 * @deprecated See {@link #removeAll(IProgressMonitor)}
 	 */
 	public void removeAll();
 
 	/**
+	 * Remove the all keys, descriptors, and contents from this repository.
+	 * @param monitor A progress monitor use to track progress and cancel the operation.  This may
+	 * be a long running operation if another process holds the lock on this location
+	 * @since 2.1
+	 */
+	public void removeAll(IProgressMonitor monitor);
+
+	/**
 	 * Remove the given descriptor and its corresponding content in this repository.  
 	 * @param descriptor the descriptor to remove.
+	 * @deprecated See {@link #removeDescriptor(IArtifactDescriptor, IProgressMonitor)}
 	 */
 	public void removeDescriptor(IArtifactDescriptor descriptor);
 
 	/**
+	 * Remove the given descriptor and its corresponding content in this repository.  
+	 * @param descriptor the descriptor to remove.
+	 * @param monitor A progress monitor use to track progress and cancel the operation.  This may
+	 * be a long running operation if another process holds the lock on this location
+	 * @since 2.1
+	 */
+	public void removeDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor);
+
+	/**
 	 * Remove the given key and all related content and descriptors from this repository.  
 	 * @param key the key to remove.
+	 * @deprecated See {@link #removeDescriptor(IArtifactKey, IProgressMonitor)}
 	 */
 	public void removeDescriptor(IArtifactKey key);
 
 	/**
+	 * Remove the given key and all related content and descriptors from this repository.  
+	 * @param key the key to remove.
+	 * @param monitor A progress monitor use to track progress and cancel the operation.  This may
+	 * be a long running operation if another process holds the lock on this location
+	 * @since 2.1
+	 */
+	public void removeDescriptor(IArtifactKey key, IProgressMonitor monitor);
+
+	/**
 	 * Remove the given list of artifact descriptors and their corresponding content
 	 * in this repository.
 	 * @param descriptors the list of descriptors to remove
 	 * @since 2.1
+	 * @deprecated See {@link #removeDescriptors(IArtifactDescriptor[], IProgressMonitor)}
 	 */
 	public void removeDescriptors(IArtifactDescriptor[] descriptors);
 
 	/**
+	 * Remove the given list of artifact descriptors and their corresponding content
+	 * in this repository.
+	 * @param descriptors the list of descriptors to remove
+	 * @param monitor A progress monitor use to track progress and cancel the operation.  This may
+	 * be a long running operation if another process holds the lock on this location
+	 * @since 2.1
+	 */
+	public void removeDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor);
+
+	/**
 	 * Remove the given list of keys and all related content and descriptors from this
 	 * repository.
 	 * @param keys
 	 * @since 2.1
+	 * @deprecated See {@link #removeDescriptors(IArtifactKey[], IProgressMonitor)}
 	 */
 	public void removeDescriptors(IArtifactKey[] keys);
 
 	/**
+	 * Remove the given list of keys and all related content and descriptors from this
+	 * repository.
+	 * @param keys
+	 * @param monitor A progress monitor use to track progress and cancel the operation.  This may
+	 * be a long running operation if another process holds the lock on this location
+	 * @since 2.1
+	 */
+	public void removeDescriptors(IArtifactKey[] keys, IProgressMonitor monitor);
+
+	/**
 	 * Executes a runnable against this repository. It is up to the repository
 	 * implementor to determine what "batch process" means, for example, it may mean
 	 * that the repository index is not stored until after the runnable completes.
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/AbstractArtifactRepository.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/AbstractArtifactRepository.java
index 2bc970d..a33292d 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/AbstractArtifactRepository.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/AbstractArtifactRepository.java
@@ -50,43 +50,111 @@
 	public abstract IStatus getArtifacts(IArtifactRequest[] requests, IProgressMonitor monitor);
 
 	public abstract OutputStream getOutputStream(IArtifactDescriptor descriptor) throws ProvisionException;
+	
+	public void addDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+		assertModifiable();
+	}
 
+	/**
+	 * @deprecated 
+	 */
 	public void addDescriptor(IArtifactDescriptor descriptor) {
+		this.addDescriptor(descriptor, new NullProgressMonitor());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @since 2.2
+	 */
+	public void addDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
 		assertModifiable();
 	}
 
+	/**
+	 * @deprecated 
+	 */
 	public void addDescriptors(IArtifactDescriptor[] descriptors) {
+		this.addDescriptors(descriptors, new NullProgressMonitor());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @since 2.2
+	 */
+	public void removeDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
 		assertModifiable();
 	}
 
+	/**
+	 * @deprecated
+	 */
 	public void removeDescriptor(IArtifactDescriptor descriptor) {
+		this.removeDescriptor(descriptor, new NullProgressMonitor());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @since 2.1
+	 */
+	public void removeDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
 		assertModifiable();
 	}
 
 	/**
 	 * {@inheritDoc}
 	 * @since 2.1
+	 * @deprecated ?? Strange that we added an API and then deprecated it
 	 */
 	public void removeDescriptors(IArtifactDescriptor[] descriptors) {
-		assertModifiable();
-	}
-
-	public void removeDescriptor(IArtifactKey key) {
-		assertModifiable();
+		this.removeDescriptors(descriptors, new NullProgressMonitor());
 	}
 
 	/**
 	 * {@inheritDoc}
 	 * @since 2.1
 	 */
-	public void removeDescriptors(IArtifactKey[] keys) {
+	public void removeDescriptor(IArtifactKey key, IProgressMonitor monitor) {
 		assertModifiable();
 	}
 
-	public void removeAll() {
+	/**
+	 * @deprecated
+	 */
+	public void removeDescriptor(IArtifactKey key) {
+		this.removeDescriptor(key, new NullProgressMonitor());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @since 2.1
+	 */
+	public void removeDescriptors(IArtifactKey[] keys, IProgressMonitor monitor) {
 		assertModifiable();
 	}
 
+	/**
+	 * {@inheritDoc}
+	 * @since 2.1
+	 * @deprecated ?? Strange that we added an API and then deprecated it
+	 */
+	public void removeDescriptors(IArtifactKey[] keys) {
+		this.removeDescriptors(keys, new NullProgressMonitor());
+	}
+
+	/**
+	 * @since 2.1
+	 */
+	public void removeAll(IProgressMonitor monitor) {
+		assertModifiable();
+	}
+
+	/**
+	 * @deprecated
+	 */
+	public void removeAll() {
+		this.removeAll(new NullProgressMonitor());
+	}
+
 	public boolean equals(Object o) {
 		if (this == o) {
 			return true;
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/AbstractRepository.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/AbstractRepository.java
index e833c1c..cfe6296 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/AbstractRepository.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/AbstractRepository.java
@@ -12,7 +12,7 @@
 
 import java.net.URI;
 import java.util.Map;
-import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.*;
 import org.eclipse.equinox.internal.p2.core.helpers.OrderedProperties;
 import org.eclipse.equinox.p2.core.IProvisioningAgent;
 import org.eclipse.equinox.p2.repository.IRepository;
@@ -171,8 +171,9 @@
 
 	/**
 	 * {@inheritDoc}
+	 * @since 2.1
 	 */
-	public synchronized String setProperty(String key, String value) {
+	public synchronized String setProperty(String key, String value, IProgressMonitor monitor) {
 		assertModifiable();
 		if (key.equals(IRepository.PROP_NAME)) {
 			String oldName = getName();
@@ -183,6 +184,13 @@
 	}
 
 	/**
+	 * {@inheritDoc}
+	 */
+	public final synchronized String setProperty(String key, String value) {
+		return this.setProperty(key, value, new NullProgressMonitor());
+	}
+
+	/**
 	 * Sets the provider of this repository
 	 * 
 	 * @param provider the repository provider
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractWrappedArtifactRepository.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractWrappedArtifactRepository.java
index a1402b8..46a5860 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractWrappedArtifactRepository.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractWrappedArtifactRepository.java
@@ -31,10 +31,20 @@
 		delegate = repo;
 	}
 
+	public void addDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+		delegate.addDescriptor(descriptor, monitor);
+	}
+
+	@Deprecated
 	public void addDescriptor(IArtifactDescriptor descriptor) {
 		delegate.addDescriptor(descriptor);
 	}
 
+	public void addDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
+		delegate.addDescriptors(descriptors, monitor);
+	}
+
+	@Deprecated
 	public void addDescriptors(IArtifactDescriptor[] descriptors) {
 		delegate.addDescriptors(descriptors);
 	}
@@ -67,22 +77,47 @@
 		return delegate.getRawArtifact(descriptor, destination, monitor);
 	}
 
+	public void removeAll(IProgressMonitor monitor) {
+		delegate.removeAll(monitor);
+	}
+
+	@Deprecated
 	public void removeAll() {
 		delegate.removeAll();
 	}
 
+	public void removeDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+		delegate.removeDescriptor(descriptor, monitor);
+	}
+
+	@Deprecated
 	public void removeDescriptor(IArtifactDescriptor descriptor) {
 		delegate.removeDescriptor(descriptor);
 	}
 
+	public void removeDescriptor(IArtifactKey key, IProgressMonitor monitor) {
+		delegate.removeDescriptor(key, monitor);
+	}
+
+	@Deprecated
 	public void removeDescriptor(IArtifactKey key) {
 		delegate.removeDescriptor(key);
 	}
 
+	public void removeDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
+		delegate.removeDescriptors(descriptors, monitor);
+	}
+
+	@Deprecated
 	public void removeDescriptors(IArtifactDescriptor[] descriptors) {
 		delegate.removeDescriptors(descriptors);
 	}
 
+	public void removeDescriptors(IArtifactKey[] keys, IProgressMonitor monitor) {
+		delegate.removeDescriptors(keys, monitor);
+	}
+
+	@Deprecated
 	public void removeDescriptors(IArtifactKey[] keys) {
 		delegate.removeDescriptors(keys);
 	}
@@ -127,6 +162,10 @@
 		return delegate.isModifiable();
 	}
 
+	public String setProperty(String key, String value, IProgressMonitor monitor) {
+		return delegate.setProperty(key, value, monitor);
+	}
+
 	public String setProperty(String key, String value) {
 		return delegate.setProperty(key, value);
 	}
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestArtifactRepository.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestArtifactRepository.java
index f00cd1e..d140bc8 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestArtifactRepository.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestArtifactRepository.java
@@ -173,27 +173,27 @@
 		return new IArtifactDescriptor[] {new ArtifactDescriptor(key)};
 	}
 
-	public void addDescriptor(IArtifactDescriptor descriptor) {
+	public void addDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
 		((ArtifactDescriptor) descriptor).setRepository(this);
 		artifactDescriptors.add(descriptor);
 		keysToLocations.put(descriptor.getArtifactKey(), null);
 	}
 
-	public void removeDescriptor(IArtifactDescriptor descriptor) {
-		removeDescriptor(descriptor.getArtifactKey());
+	public void removeDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+		removeDescriptor(descriptor.getArtifactKey(), monitor);
 	}
 
-	public void removeDescriptors(IArtifactDescriptor[] descriptors) {
+	public void removeDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
 		for (IArtifactDescriptor descriptor : descriptors)
-			removeDescriptor(descriptor);
+			removeDescriptor(descriptor, monitor);
 	}
 
-	public void removeDescriptors(IArtifactKey[] keys) {
+	public void removeDescriptors(IArtifactKey[] keys, IProgressMonitor monitor) {
 		for (IArtifactKey key : keys)
-			removeDescriptor(key);
+			removeDescriptor(key, monitor);
 	}
 
-	public void removeDescriptor(IArtifactKey key) {
+	public void removeDescriptor(IArtifactKey key, IProgressMonitor monitor) {
 		for (IArtifactDescriptor nextDescriptor : artifactDescriptors) {
 			if (key.equals(nextDescriptor.getArtifactKey()))
 				artifactDescriptors.remove(nextDescriptor);
@@ -205,7 +205,7 @@
 		}
 	}
 
-	public void removeAll() {
+	public void removeAll(IProgressMonitor monitor) {
 		artifactDescriptors.clear();
 		keysToLocations.clear();
 		locationsToContents.clear();
@@ -215,7 +215,7 @@
 		return true;
 	}
 
-	public OutputStream getOutputStream(IArtifactDescriptor descriptor) {
+	public OutputStream getOutputStream(IArtifactDescriptor descriptor ) {
 		throw new UnsupportedOperationException("Method is not implemented by this repository");
 	}
 
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/AllTests.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/AllTests.java
index b22de46..7d2f21f 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/AllTests.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/AllTests.java
@@ -19,6 +19,7 @@
 
 	public static Test suite() {
 		TestSuite suite = new TestSuite(AllTests.class.getName());
+		suite.addTestSuite(ArtifactLockingTest.class);
 		suite.addTestSuite(ArtifactOutputStreamTest.class);
 		suite.addTestSuite(ArtifactRepositoryManagerTest.class);
 		suite.addTestSuite(ArtifactRepositoryMissingSizeData.class);
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/ArtifactLockingTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/ArtifactLockingTest.java
new file mode 100644
index 0000000..9aab0ae
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/ArtifactLockingTest.java
@@ -0,0 +1,477 @@
+package org.eclipse.equinox.p2.tests.artifact.repository;
+
+import java.io.File;
+import org.eclipse.core.runtime.*;
+import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactDescriptor;
+import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
+import org.eclipse.equinox.p2.core.ProvisionException;
+import org.eclipse.equinox.p2.metadata.IArtifactKey;
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.repository.IRunnableWithProgress;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager;
+import org.eclipse.equinox.p2.tests.AbstractProvisioningTest;
+
+public class ArtifactLockingTest extends AbstractProvisioningTest {
+
+	private File targetLocation;
+	private SimpleArtifactRepository repo1 = null;
+	private SimpleArtifactRepository repo2 = null;
+
+	public void setUp() throws Exception {
+		super.setUp();
+		targetLocation = File.createTempFile("bundlepool", ".repo");
+		targetLocation.delete();
+		targetLocation.mkdirs();
+		repo1 = new SimpleArtifactRepository(getAgent(), "TargetRepo", targetLocation.toURI(), null);
+		Thread.sleep(1000);
+		repo2 = new SimpleArtifactRepository(getAgent(), "TargetRepo", targetLocation.toURI(), null);
+	}
+
+	boolean canContinue = false;
+
+	public void testCancelLoad() throws InterruptedException, ProvisionException {
+		this.canContinue = false;
+		final IProgressMonitor progressMonitor = new NullProgressMonitor();
+		new Thread(new Runnable() {
+			public void run() {
+				status = repo1.executeBatch(new IRunnableWithProgress() {
+
+					public void run(IProgressMonitor monitor) throws OperationCanceledException {
+						try {
+							canContinue = true;
+							Thread.sleep(3 * 1000);
+							repo1.addDescriptor(new SimpleArtifactDescriptor(new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0"))), new NullProgressMonitor());
+							progressMonitor.setCanceled(true);
+							Thread.sleep(1000);
+						} catch (InterruptedException e) {
+							// Do nothing
+						}
+					}
+				}, new NullProgressMonitor());
+			}
+		}).start();
+
+		while (!canContinue) {
+			Thread.sleep(100);
+		}
+
+		IArtifactRepositoryManager artifactManager = (IArtifactRepositoryManager) getAgent().getService(IArtifactRepositoryManager.SERVICE_NAME);
+		boolean expected = false;
+		try {
+			artifactManager.loadRepository(targetLocation.toURI(), progressMonitor);
+		} catch (OperationCanceledException e) {
+			expected = true;
+		}
+		assertTrue("Expected an Operation Cancled Exception", expected);
+
+	}
+
+	public void testWaitForLoad() throws InterruptedException, ProvisionException {
+		this.canContinue = false;
+		new Thread(new Runnable() {
+			public void run() {
+				status = repo1.executeBatch(new IRunnableWithProgress() {
+
+					public void run(IProgressMonitor monitor) throws OperationCanceledException {
+						try {
+							canContinue = true;
+							Thread.sleep(6 * 1000);
+							repo1.addDescriptor(new SimpleArtifactDescriptor(new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0"))), new NullProgressMonitor());
+
+						} catch (InterruptedException e) {
+							// Do nothing
+						}
+					}
+				}, new NullProgressMonitor());
+			}
+		}).start();
+
+		while (!canContinue) {
+			Thread.sleep(100);
+		}
+		IArtifactRepositoryManager artifactManager = (IArtifactRepositoryManager) getAgent().getService(IArtifactRepositoryManager.SERVICE_NAME);
+		SimpleArtifactRepository artifactRepository = (SimpleArtifactRepository) artifactManager.loadRepository(targetLocation.toURI(), new NullProgressMonitor());
+		assertEquals(1, artifactRepository.getDescriptors().size());
+
+	}
+
+	// Helper variables for the following test case
+	private boolean keepRunning = true;
+	IStatus status = null;
+
+	/**
+	 * Creates 1 thread that does a long running execute batch. We then try to add descriptors to another
+	 * instance of the same repository. This will block.  We then cancel the progress monitor to test
+	 * that the block terminates.
+	 * @throws InterruptedException
+	 */
+	public void testCancel() throws InterruptedException {
+		final IProgressMonitor progressMonitor = new NullProgressMonitor();
+		this.keepRunning = true;
+
+		new Thread(new Runnable() {
+			public void run() {
+				status = repo1.executeBatch(new IRunnableWithProgress() {
+
+					public void run(IProgressMonitor monitor) throws OperationCanceledException {
+						long start = System.currentTimeMillis();
+						while (keepRunning) {
+							long current = System.currentTimeMillis();
+							if (current - start > 1000 * 10) {
+								fail("Test case never finished. Likely keep running was never set to false.");
+								return;
+							}
+							try {
+								Thread.sleep(1000);
+							} catch (InterruptedException e) {
+								// Do nothing
+							}
+						}
+					}
+				}, new NullProgressMonitor());
+			}
+		}).start();
+
+		// Give the execute batch thread a chance to start
+		Thread.sleep(1000);
+
+		// Create a thread that will stop our progress monitor
+		Thread t = new Thread(new Runnable() {
+
+			public void run() {
+				try {
+					Thread.sleep(1000);
+				} catch (InterruptedException e) {
+					// Do nothing
+				}
+				progressMonitor.setCanceled(true);
+			}
+		});
+		t.start();
+
+		// This should block
+		repo2.addDescriptor(new SimpleArtifactDescriptor(new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0"))), progressMonitor);
+		keepRunning = false;
+		if (status != null && !status.isOK()) {
+			fail(status.getMessage());
+		}
+		assertEquals(0, repo2.getDescriptors().size()); // The descriptor should not have been added
+	}
+
+	public void testCancelRead() throws InterruptedException {
+		final IProgressMonitor progressMonitor = new NullProgressMonitor();
+		this.keepRunning = true;
+
+		new Thread(new Runnable() {
+			public void run() {
+				status = repo1.executeBatch(new IRunnableWithProgress() {
+
+					public void run(IProgressMonitor monitor) throws OperationCanceledException {
+						long start = System.currentTimeMillis();
+						while (keepRunning) {
+							long current = System.currentTimeMillis();
+							if (current - start > 1000 * 10) {
+								fail("Test case never finished. Likely keep running was never set to false.");
+								return;
+							}
+							try {
+								Thread.sleep(1000);
+							} catch (InterruptedException e) {
+								// Do nothing
+							}
+						}
+					}
+				}, new NullProgressMonitor());
+			}
+		}).start();
+
+		// Give the execute batch thread a chance to start
+		Thread.sleep(1000);
+
+		// Create a thread that will stop our progress monitor
+		Thread t = new Thread(new Runnable() {
+
+			public void run() {
+				try {
+					Thread.sleep(1000);
+				} catch (InterruptedException e) {
+					// Do nothing
+				}
+				progressMonitor.setCanceled(true);
+			}
+		});
+		t.start();
+
+		// This should block
+		repo2.getDescriptors();
+		keepRunning = false;
+		if (status != null && !status.isOK()) {
+			fail(status.getMessage());
+		}
+	}
+
+	// The following variables are used in the following test case
+
+	IStatus status1 = null;
+	IStatus status2 = null;
+	boolean lockAcquired = false;
+
+	/**
+	 * This tests that two 'executeBatch' operations are not executed in 
+	 * parallel, but rather, the second one waits for the first to complete.
+	 * @throws InterruptedException
+	 */
+	public void testMultipleExecuteBatch() throws InterruptedException {
+		this.lockAcquired = false;
+		Thread t1 = new Thread(new Runnable() {
+			public void run() {
+				status1 = repo1.executeBatch(new IRunnableWithProgress() {
+
+					public void run(IProgressMonitor monitor) throws OperationCanceledException {
+						try {
+							if (lockAcquired)
+								throw new RuntimeException("Lock already acquired");
+							lockAcquired = true;
+							try {
+								Thread.sleep(1000);
+							} catch (InterruptedException e) {
+								// Do nothing
+							}
+						} finally {
+							lockAcquired = false;
+						}
+					}
+				}, new NullProgressMonitor());
+			}
+		});
+		t1.start();
+
+		Thread t2 = new Thread(new Runnable() {
+			public void run() {
+				status2 = repo2.executeBatch(new IRunnableWithProgress() {
+					public void run(IProgressMonitor monitor) throws OperationCanceledException {
+						try {
+							if (lockAcquired)
+								throw new RuntimeException("Lock already acquired");
+							lockAcquired = true;
+							try {
+								Thread.sleep(1000);
+							} catch (InterruptedException e) {
+								// Do nothing
+							}
+						} finally {
+							lockAcquired = false;
+						}
+					}
+				}, new NullProgressMonitor());
+			}
+		});
+		t2.start();
+
+		t1.join();
+		t2.join();
+
+		if (!status1.isOK() || !status2.isOK())
+			fail("Test failed, a lock acquired simultaneously by both execute batch operations");
+	}
+
+	/**
+	 * This tests that we wait for the lock to come off, we then add a descriptor
+	 * @throws InterruptedException
+	 */
+	public void testWait() throws InterruptedException {
+
+		new Thread(new Runnable() {
+			public void run() {
+				status = repo1.executeBatch(new IRunnableWithProgress() {
+
+					public void run(IProgressMonitor monitor) throws OperationCanceledException {
+						try {
+							Thread.sleep(5000);
+						} catch (InterruptedException e) {
+							// Do nothing
+						}
+
+					}
+				}, new NullProgressMonitor());
+			}
+		}).start();
+
+		// Give the execute batch thread a chance to start
+		Thread.sleep(1000);
+
+		// This should block for 5 seconds, and then add the descriptor
+		repo2.addDescriptor(new SimpleArtifactDescriptor(new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0"))), new NullProgressMonitor());
+		assertEquals(1, repo2.getDescriptors().size()); // The descriptor should not have been added
+	}
+
+	public void testMultipleAddDescriptors() throws InterruptedException {
+		Thread.sleep(1000);
+		repo1.addDescriptor(new SimpleArtifactDescriptor(new ArtifactKey("org.eclipse.test", "test1", Version.create("1.0.0"))), new NullProgressMonitor());
+		assertEquals(1, repo1.getDescriptors().size());
+		Thread.sleep(1000);
+		repo2.addDescriptor(new SimpleArtifactDescriptor(new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0"))), new NullProgressMonitor());
+		assertEquals(2, repo2.getDescriptors().size());
+	}
+
+	public void testContainsDescriptor() throws InterruptedException {
+		ArtifactKey k = new ArtifactKey("org.eclipse.test", "test1", Version.create("1.0.0"));
+		SimpleArtifactDescriptor d = new SimpleArtifactDescriptor(k);
+		Thread.sleep(1000);
+		repo1.addDescriptor(d, new NullProgressMonitor());
+		Thread.sleep(1000);
+		assertTrue(repo2.contains(d));
+	}
+
+	public void testContainsKey() throws InterruptedException {
+		ArtifactKey k = new ArtifactKey("org.eclipse.test", "test1", Version.create("1.0.0"));
+		SimpleArtifactDescriptor d = new SimpleArtifactDescriptor(k);
+		Thread.sleep(1000);
+		repo1.addDescriptor(d, new NullProgressMonitor());
+		Thread.sleep(1000);
+		assertTrue(repo2.contains(k));
+	}
+
+	public void testMultipleRemoveDescriptors() throws InterruptedException {
+		SimpleArtifactDescriptor d1 = new SimpleArtifactDescriptor(new ArtifactKey("org.eclipse.test", "test1", Version.create("1.0.0")));
+		SimpleArtifactDescriptor d2 = new SimpleArtifactDescriptor(new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0")));
+
+		Thread.sleep(1000);
+		repo1.addDescriptor(d1, new NullProgressMonitor());
+		repo1.addDescriptor(d2, new NullProgressMonitor());
+		assertEquals(2, repo1.getDescriptors().size());
+		Thread.sleep(1000);
+		repo1.removeDescriptor(d1, new NullProgressMonitor());
+		assertEquals(1, repo1.getDescriptors().size());
+		Thread.sleep(1000);
+		repo2.removeDescriptor(d2, new NullProgressMonitor());
+		assertEquals(0, repo2.getDescriptors().size());
+	}
+
+	public void testMultipleRemoveKeys() throws InterruptedException {
+		ArtifactKey k1 = new ArtifactKey("org.eclipse.test", "test1", Version.create("1.0.0"));
+		ArtifactKey k2 = new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0"));
+		SimpleArtifactDescriptor d1 = new SimpleArtifactDescriptor(k1);
+		SimpleArtifactDescriptor d2 = new SimpleArtifactDescriptor(k2);
+
+		Thread.sleep(1000);
+		repo1.addDescriptor(d1, new NullProgressMonitor());
+		repo1.addDescriptor(d2, new NullProgressMonitor());
+		assertEquals(2, repo1.getDescriptors().size());
+		Thread.sleep(1000);
+		repo1.removeDescriptor(k1, new NullProgressMonitor());
+		assertEquals(1, repo1.getDescriptors().size());
+		Thread.sleep(1000);
+		assertEquals(1, repo2.getDescriptors().size());
+		repo2.removeDescriptor(k2, new NullProgressMonitor());
+		assertEquals(0, repo2.getDescriptors().size());
+	}
+
+	public void testRemoveBulkKeys() throws InterruptedException {
+		ArtifactKey k1 = new ArtifactKey("org.eclipse.test", "test1", Version.create("1.0.0"));
+		ArtifactKey k2 = new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0"));
+		ArtifactKey k3 = new ArtifactKey("org.eclipse.test", "test3", Version.create("1.0.0"));
+		SimpleArtifactDescriptor d1 = new SimpleArtifactDescriptor(k1);
+		SimpleArtifactDescriptor d2 = new SimpleArtifactDescriptor(k2);
+		SimpleArtifactDescriptor d3 = new SimpleArtifactDescriptor(k3);
+
+		Thread.sleep(1000);
+		repo1.addDescriptor(d1, new NullProgressMonitor());
+		repo1.addDescriptor(d2, new NullProgressMonitor());
+		repo1.addDescriptor(d3, new NullProgressMonitor());
+		assertEquals(3, repo1.getDescriptors().size());
+		Thread.sleep(1000);
+		repo2.removeDescriptors(new IArtifactKey[] {k1, k2}, new NullProgressMonitor());
+		Thread.sleep(1000);
+		assertEquals(1, repo2.getDescriptors().size());
+		assertEquals(1, repo1.getDescriptors().size());
+	}
+
+	public void testRemoveBulkDescriptors() throws InterruptedException {
+		ArtifactKey k1 = new ArtifactKey("org.eclipse.test", "test1", Version.create("1.0.0"));
+		ArtifactKey k2 = new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0"));
+		ArtifactKey k3 = new ArtifactKey("org.eclipse.test", "test3", Version.create("1.0.0"));
+		SimpleArtifactDescriptor d1 = new SimpleArtifactDescriptor(k1);
+		SimpleArtifactDescriptor d2 = new SimpleArtifactDescriptor(k2);
+		SimpleArtifactDescriptor d3 = new SimpleArtifactDescriptor(k3);
+
+		Thread.sleep(1000);
+		repo1.addDescriptor(d1, new NullProgressMonitor());
+		repo1.addDescriptor(d2, new NullProgressMonitor());
+		repo1.addDescriptor(d3, new NullProgressMonitor());
+		assertEquals(3, repo1.getDescriptors().size());
+		Thread.sleep(1000);
+		repo2.removeDescriptors(new IArtifactDescriptor[] {d1, d2}, new NullProgressMonitor());
+		Thread.sleep(1000);
+		assertEquals(1, repo2.getDescriptors().size());
+		assertEquals(1, repo1.getDescriptors().size());
+	}
+
+	public void testRemoveAll() throws InterruptedException {
+		ArtifactKey k1 = new ArtifactKey("org.eclipse.test", "test1", Version.create("1.0.0"));
+		ArtifactKey k2 = new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0"));
+		SimpleArtifactDescriptor d1 = new SimpleArtifactDescriptor(k1);
+		SimpleArtifactDescriptor d2 = new SimpleArtifactDescriptor(k2);
+
+		Thread.sleep(1000);
+		repo1.addDescriptor(d1, new NullProgressMonitor());
+		repo1.addDescriptor(d2, new NullProgressMonitor());
+		assertEquals(2, repo1.getDescriptors().size());
+		Thread.sleep(1000);
+		repo2.removeAll(new NullProgressMonitor());
+		Thread.sleep(1000);
+		assertEquals(0, repo2.getDescriptors().size());
+		assertEquals(0, repo1.getDescriptors().size());
+	}
+
+	public void testReloadAdds() throws InterruptedException {
+		// Delay 1 second because some operating systems only give 1 second precision
+		Thread.sleep(1000);
+		repo1.addDescriptor(new SimpleArtifactDescriptor(new ArtifactKey("org.eclipse.test", "test1", Version.create("1.0.0"))), new NullProgressMonitor());
+		Thread.sleep(1000);
+		repo2.addDescriptor(new SimpleArtifactDescriptor(new ArtifactKey("org.eclipse.test", "test2", Version.create("1.0.0"))), new NullProgressMonitor());
+
+		assertEquals(2, repo2.getDescriptors().size());
+		// Does the fist repo get reloaded when reading the descriptors
+		assertEquals(2, repo1.getDescriptors().size());
+	}
+
+	public void _testSetProperty() throws InterruptedException {
+		// Delay 1 second because some operating systems only give 1 second precision
+		Thread.sleep(1000);
+		repo1.setProperty("foo", "bar", new NullProgressMonitor());
+		Thread.sleep(1000);
+		assertEquals("bar", repo1.getProperty("foo"));
+		assertEquals("bar", repo2.getProperty("foo"));
+	}
+
+	public void _testGetProperties() throws InterruptedException {
+		// Delay 1 second because some operating systems only give 1 second precision
+		Thread.sleep(1000);
+		repo1.setProperty("foo", "bar", new NullProgressMonitor());
+		Thread.sleep(1000);
+		assertEquals("bar", repo1.getProperties().get("foo"));
+		assertEquals("bar", repo2.getProperties().get("foo"));
+	}
+
+	public void _testSetName() throws InterruptedException {
+		// Delay 1 second because some operating systems only give 1 second precision
+		Thread.sleep(1000);
+		repo1.setName("Foo");
+		Thread.sleep(1000);
+		assertEquals("Foo", repo1.getName());
+		assertEquals("Foo", repo2.getName());
+	}
+
+	public void _testSetDescription() throws InterruptedException {
+		// Delay 1 second because some operating systems only give 1 second precision
+		Thread.sleep(1000);
+		repo1.setDescription("Foo Bar");
+		Thread.sleep(1000);
+		assertEquals("Foo Bar", repo1.getDescription());
+		assertEquals("Foo Bar", repo2.getDescription());
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/CompositeArtifactRepositoryTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/CompositeArtifactRepositoryTest.java
index 30018d2..4947f8d 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/CompositeArtifactRepositoryTest.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/CompositeArtifactRepositoryTest.java
@@ -1073,8 +1073,8 @@
 				return new Status(IStatus.ERROR, "Test", "Test - Download interrupted");
 			}
 
-			public void addDescriptor(IArtifactDescriptor descriptor) {
-				super.addDescriptor(descriptor);
+			public void addDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+				super.addDescriptor(descriptor, monitor);
 				super.addArtifact(descriptor.getArtifactKey(), contents);
 			}
 
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/SimpleArtifactRepositoryTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/SimpleArtifactRepositoryTest.java
index 44590cc..b3a24ec 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/SimpleArtifactRepositoryTest.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/SimpleArtifactRepositoryTest.java
@@ -28,8 +28,7 @@
 import org.eclipse.equinox.p2.metadata.*;
 import org.eclipse.equinox.p2.query.IQueryResult;
 import org.eclipse.equinox.p2.query.IQueryable;
-import org.eclipse.equinox.p2.repository.IRepository;
-import org.eclipse.equinox.p2.repository.IRepositoryManager;
+import org.eclipse.equinox.p2.repository.*;
 import org.eclipse.equinox.p2.repository.artifact.*;
 import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor;
 import org.eclipse.equinox.p2.repository.artifact.spi.ProcessingStepDescriptor;
@@ -361,6 +360,42 @@
 
 	}
 
+	public void _testAddDescriptorPerformance() throws Exception {
+		File folder = getTestFolder("ArtifactRepository_testAddDescriptorPerformance");
+		repositoryURI = folder.toURI();
+
+		IArtifactRepository repo = getArtifactRepositoryManager().createRepository(repositoryURI, "test", IArtifactRepositoryManager.TYPE_SIMPLE_REPOSITORY, new HashMap());
+
+		long start = System.currentTimeMillis();
+		for (int i = 0; i < 10000; i++) {
+			ArtifactDescriptor d = new ArtifactDescriptor(new ArtifactKey("osgi.bundle", "a" + i, Version.create("1.0.0")));
+			repo.addDescriptor(d);
+		}
+		long end = System.currentTimeMillis();
+		System.out.println("Total time: " + (end - start));
+	}
+
+	public void _testAddDescriptorPerformanceExecuteBatch() throws Exception {
+		File folder = getTestFolder("ArtifactRepository_testAddDescriptorPerformanceExectuteBatch");
+		repositoryURI = folder.toURI();
+
+		final IArtifactRepository repo = getArtifactRepositoryManager().createRepository(repositoryURI, "test", IArtifactRepositoryManager.TYPE_SIMPLE_REPOSITORY, new HashMap());
+
+		long start = System.currentTimeMillis();
+		repo.executeBatch(new IRunnableWithProgress() {
+
+			public void run(IProgressMonitor monitor) throws OperationCanceledException {
+				for (int i = 0; i < 10000; i++) {
+					ArtifactDescriptor d = new ArtifactDescriptor(new ArtifactKey("osgi.bundle", "a" + i, Version.create("1.0.0")));
+					repo.addDescriptor(d);
+				}
+			}
+		}, new NullProgressMonitor());
+
+		long end = System.currentTimeMillis();
+		System.out.println("Total time: " + (end - start));
+	}
+
 	public void testQuery() throws Exception {
 		File folder = getTestFolder("ArtifactRepository_testQuery");
 		repositoryURI = folder.toURI();
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/TestArtifactRepository.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/TestArtifactRepository.java
index 6b50cf7..f70f3a3 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/TestArtifactRepository.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/TestArtifactRepository.java
@@ -126,20 +126,30 @@
 		}
 		return new ArtifactOutputStream(new ByteArrayOutputStream(500), descriptor);
 	}
-
-	public void addDescriptor(IArtifactDescriptor descriptor) {
+	
+	public void addDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
 		addDescriptor(descriptor, new byte[0]);
 	}
 
+	@Deprecated
+	public final void addDescriptor(IArtifactDescriptor descriptor) {
+		this.addDescriptor(descriptor, new NullProgressMonitor());
+	}
+
 	public void addDescriptor(IArtifactDescriptor descriptor, byte[] bytes) {
 		repo.put(descriptor, bytes);
 	}
 
-	public void addDescriptors(IArtifactDescriptor[] descriptors) {
+	public void addDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
 		for (int i = 0; i < descriptors.length; i++)
 			addDescriptor(descriptors[i]);
 	}
 
+	@Deprecated
+	public final void addDescriptors(IArtifactDescriptor[] descriptors) {
+		this.addDescriptors(descriptors, new NullProgressMonitor());
+	}
+
 	public boolean contains(IArtifactDescriptor descriptor) {
 		return repo.containsKey(descriptor);
 	}
@@ -193,11 +203,16 @@
 		return artifactRequest.getResult();
 	}
 
-	public void removeDescriptor(IArtifactDescriptor descriptor) {
+	public void removeDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
 		repo.remove(descriptor);
 	}
 
-	public void removeDescriptor(IArtifactKey key) {
+	@Deprecated
+	public final void removeDescriptor(IArtifactDescriptor descriptor) {
+		this.removeDescriptor(descriptor, new NullProgressMonitor());
+	}
+
+	public void removeDescriptor(IArtifactKey key, IProgressMonitor monitor) {
 		ArrayList/*<IArtifactDescriptor>*/removeList = new ArrayList/*<IArtifactDescriptor>*/();
 		for (Iterator/*<IArtifactDescriptor>*/iterator = repo.keySet().iterator(); iterator.hasNext();) {
 			IArtifactDescriptor descriptor = (IArtifactDescriptor) iterator.next();
@@ -209,16 +224,31 @@
 		}
 	}
 
-	public void removeDescriptors(IArtifactDescriptor[] descriptors) {
+	@Deprecated
+	public final void removeDescriptor(IArtifactKey key) {
+		removeDescriptor(key, new NullProgressMonitor());
+	}
+
+	public void removeDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
 		for (IArtifactDescriptor descriptor : descriptors)
 			removeDescriptor(descriptor);
 	}
 
-	public void removeDescriptors(IArtifactKey[] keys) {
+	@Deprecated
+	public final void removeDescriptors(IArtifactDescriptor[] descriptors) {
+		this.removeDescriptors(descriptors, new NullProgressMonitor());
+	}
+
+	public void removeDescriptors(IArtifactKey[] keys, IProgressMonitor monitor) {
 		for (IArtifactKey key : keys)
 			removeDescriptor(key);
 	}
 
+	@Deprecated
+	public final void removeDescriptors(IArtifactKey[] keys) {
+		this.removeDescriptors(keys, new NullProgressMonitor());
+	}
+
 	public String getDescription() {
 		return description;
 	}
@@ -267,18 +297,27 @@
 		this.name = value;
 	}
 
-	public String setProperty(String key, String value) {
+	public String setProperty(String key, String value, IProgressMonitor monitor) {
 		return (value == null ? properties.remove(key) : properties.put(key, value));
 	}
 
+	public final String setProperty(String key, String value) {
+		return this.setProperty(key, value, new NullProgressMonitor());
+	}
+
 	public void setProvider(String value) {
 		provider = value;
 	}
 
-	public void removeAll() {
+	public void removeAll(IProgressMonitor monitor) {
 		repo.clear();
 	}
 
+	@Deprecated
+	public final void removeAll() {
+		removeAll(new NullProgressMonitor());
+	}
+
 	public Object getAdapter(Class adapter) {
 		return null;
 	}
diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/artifact/UpdateSiteArtifactRepository.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/artifact/UpdateSiteArtifactRepository.java
index 1d9733b..de16759 100644
--- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/artifact/UpdateSiteArtifactRepository.java
+++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/artifact/UpdateSiteArtifactRepository.java
@@ -13,8 +13,7 @@
 import java.io.OutputStream;
 import java.net.URI;
 import java.util.Map;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.*;
 import org.eclipse.equinox.p2.core.IProvisioningAgent;
 import org.eclipse.equinox.p2.core.ProvisionException;
 import org.eclipse.equinox.p2.metadata.IArtifactKey;
@@ -36,10 +35,20 @@
 		this.delegate = repository;
 	}
 
+	public void addDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
+	}
+
+	@Deprecated
 	public void addDescriptor(IArtifactDescriptor descriptor) {
 		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
 	}
 
+	public void addDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
+	}
+
+	@Deprecated
 	public void addDescriptors(IArtifactDescriptor[] descriptors) {
 		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
 	}
@@ -63,7 +72,7 @@
 	public IStatus getArtifacts(IArtifactRequest[] requests, IProgressMonitor monitor) {
 		return delegate.getArtifacts(requests, monitor);
 	}
-
+	
 	public OutputStream getOutputStream(IArtifactDescriptor descriptor) throws ProvisionException {
 		// TODO Auto-generated method stub
 		return null;
@@ -73,22 +82,47 @@
 		return delegate.getRawArtifact(descriptor, destination, monitor);
 	}
 
+	public void removeAll(IProgressMonitor monitor) {
+		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
+	}
+
+	@Deprecated
 	public void removeAll() {
 		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
 	}
 
+	public void removeDescriptor(IArtifactDescriptor descriptor, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
+	}
+
+	@Deprecated
 	public void removeDescriptor(IArtifactDescriptor descriptor) {
 		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
 	}
 
+	public void removeDescriptors(IArtifactDescriptor[] descriptors, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
+	}
+
+	@Deprecated
 	public void removeDescriptors(IArtifactDescriptor[] descriptors) {
 		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
 	}
 
+	public void removeDescriptor(IArtifactKey key, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
+	}
+
+	@Deprecated
 	public void removeDescriptor(IArtifactKey key) {
 		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
 	}
 
+	public void removeDescriptors(IArtifactKey[] keys, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
+	}
+
+	@Deprecated
 	public void removeDescriptors(IArtifactKey[] keys) {
 		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
 	}
@@ -141,6 +175,10 @@
 		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
 	}
 
+	public String setProperty(String key, String value, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
+	}
+
 	public String setProperty(String key, String value) {
 		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
 	}
diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/metadata/UpdateSiteMetadataRepository.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/metadata/UpdateSiteMetadataRepository.java
index 84c8a32..e28cef7 100644
--- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/metadata/UpdateSiteMetadataRepository.java
+++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/metadata/UpdateSiteMetadataRepository.java
@@ -166,6 +166,13 @@
 	/* (non-Javadoc)
 	 * @see org.eclipse.equinox.p2.repository.IRepository#setProperty(java.lang.String, java.lang.String)
 	 */
+	public String setProperty(String key, String value, IProgressMonitor monitor) {
+		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.equinox.p2.repository.IRepository#setProperty(java.lang.String, java.lang.String)
+	 */
 	public String setProperty(String key, String value) {
 		throw new UnsupportedOperationException("Repository not modifiable: " + location); //$NON-NLS-1$
 	}