diff --git a/source/java/org/alfresco/repo/content/replication/AggregatingContentStore.java b/source/java/org/alfresco/repo/content/replication/AggregatingContentStore.java
new file mode 100644
index 0000000000..d28f018d32
--- /dev/null
+++ b/source/java/org/alfresco/repo/content/replication/AggregatingContentStore.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2013-2013 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.content.replication;
+
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.content.AbstractContentStore;
+import org.alfresco.repo.content.ContentContext;
+import org.alfresco.repo.content.ContentStore;
+import org.alfresco.repo.content.caching.CachingContentStore;
+import org.alfresco.service.cmr.repository.ContentIOException;
+import org.alfresco.service.cmr.repository.ContentReader;
+import org.alfresco.service.cmr.repository.ContentWriter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Aggregating Content Store
+ *
+ * A content store implementation that aggregates a set of stores. Content is not
+ * persisted by this store, but rather it relies on any number of
+ * child {@link org.alfresco.repo.content.ContentStore stores} to provide access
+ * to content readers and writers.
+ *
+ * The order in which the stores appear in the list of stores participating is
+ * important. The first store in the list is known as the primary store.
+
+ * Content is written to the primary store only. The other stores are
+ * only used to retrieve content and the primary store is not updated with
+ * the content.
+ *
+ * @author Derek Hulley
+ * @author Mark Rogers
+ * @see CachingContentStore
+ */
+public class AggregatingContentStore extends AbstractContentStore
+{
+ private static Log logger = LogFactory.getLog(AggregatingContentStore.class);
+
+ private ContentStore primaryStore;
+ private List secondaryStores;
+
+ private Lock readLock;
+ private Lock writeLock;
+
+ /**
+ * Default constructor
+ */
+ public AggregatingContentStore()
+ {
+ ReadWriteLock storeLock = new ReentrantReadWriteLock();
+ readLock = storeLock.readLock();
+ writeLock = storeLock.writeLock();
+ }
+
+ /**
+ * Set the primary store that content will be replicated to or from
+ *
+ * @param primaryStore the primary content store
+ */
+ public void setPrimaryStore(ContentStore primaryStore)
+ {
+ this.primaryStore = primaryStore;
+ }
+
+ /**
+ * Set the secondary stores that this component will replicate to or from
+ *
+ * @param stores a list of stores to replicate to or from
+ */
+ public void setSecondaryStores(List secondaryStores)
+ {
+ this.secondaryStores = secondaryStores;
+ }
+
+ /**
+ * @return Returns true if the primary store supports writing
+ */
+ public boolean isWriteSupported()
+ {
+ return primaryStore.isWriteSupported();
+ }
+
+ /**
+ * @return Returns true if the primary store supports the URL
+ */
+ @Override
+ public boolean isContentUrlSupported(String contentUrl)
+ {
+ return primaryStore.isContentUrlSupported(contentUrl);
+ }
+
+ /**
+ * @return Return the primary store root location
+ */
+ @Override
+ public String getRootLocation()
+ {
+ return primaryStore.getRootLocation();
+ }
+
+ /**
+ * Forwards the call directly to the first store in the list of stores.
+ */
+ public ContentReader getReader(String contentUrl) throws ContentIOException
+ {
+ if (primaryStore == null)
+ {
+ throw new AlfrescoRuntimeException("ReplicatingContentStore not initialised");
+ }
+
+ // get a read lock so that we are sure that no replication is underway
+ readLock.lock();
+ try
+ {
+ // get a reader from the primary store
+ ContentReader primaryReader = primaryStore.getReader(contentUrl);
+
+ // give it straight back if the content is there
+ if (primaryReader.exists())
+ {
+ return primaryReader;
+ }
+
+ // the content is not in the primary reader so we have to go looking for it
+ ContentReader secondaryContentReader = null;
+ for (ContentStore store : secondaryStores)
+ {
+ ContentReader reader = store.getReader(contentUrl);
+ if (reader.exists())
+ {
+ // found the content in a secondary store
+ return reader;
+ }
+ }
+
+ return primaryReader;
+ }
+ finally
+ {
+ readLock.unlock();
+ }
+ }
+
+ public ContentWriter getWriter(ContentContext ctx)
+ {
+ // get the writer
+ ContentWriter writer = primaryStore.getWriter(ctx);
+
+ return writer;
+ }
+
+ /**
+ * Performs a delete on the local store and if outbound replication is on, propogates
+ * the delete to the other stores too.
+ *
+ * @return Returns the value returned by the delete on the primary store.
+ */
+ public boolean delete(String contentUrl) throws ContentIOException
+ {
+ // delete on the primary store
+ boolean deleted = primaryStore.delete(contentUrl);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Deleted content for URL: " + contentUrl);
+ }
+ return deleted;
+ }
+
+ /**
+ * Iterates over results as given by the primary store and all secondary stores. It is up to the handler to eliminate
+ * duplicates that will occur between the primary and secondary stores.
+ */
+ @SuppressWarnings("deprecation")
+ public void getUrls(Date createdAfter, Date createdBefore, ContentUrlHandler handler) throws ContentIOException
+ {
+ // add in URLs from primary store
+ primaryStore.getUrls(createdAfter, createdBefore, handler);
+
+ // add in URLs from secondary stores (they are visible for reads)
+ for (ContentStore secondaryStore : secondaryStores)
+ {
+ secondaryStore.getUrls(createdAfter, createdBefore, handler);
+ }
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Iterated over content URLs: \n" +
+ " created after: " + createdAfter + "\n" +
+ " created before: " + createdBefore);
+ }
+ }
+}