diff --git a/common/source/java/ch/systemsx/cisd/common/test/InvocationRecordingWrapper.java b/common/source/java/ch/systemsx/cisd/common/test/InvocationRecordingWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..6061013b4f68fbe177659c9fce1100ea35ba4810
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/test/InvocationRecordingWrapper.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2013 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.common.test;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.systemsx.cisd.common.shared.basic.string.CommaSeparatedListBuilder;
+
+/**
+ * Helper class which allows to record method invocations.
+ * 
+ * @author Franz-Josef Elmer
+ */
+public class InvocationRecordingWrapper<T>
+{
+    /**
+     * Creates an instance which contains a proxy of the specified object.
+     * 
+     * @param type Interface the specified object implements. The proxy will implement the same
+     *            interface.
+     * @param returnTypesToWrap Interfaces return values of invocations might implement. Invocations
+     *            on such return values will also be recorded. This is done recursively. If the
+     *            return value is an array or a list of such types the elements of the returned
+     *            array/list will also be wrapped.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> InvocationRecordingWrapper<T> wrap(final T object, Class<T> type,
+            final Class<?>... returnTypesToWrap)
+    {
+        final InvocationRecordingWrapper<T> wrapper = new InvocationRecordingWrapper<T>();
+        wrapper.proxy = (T) createProxy(wrapper, null, object, type, returnTypesToWrap);
+        return wrapper;
+    }
+
+    private static Object createProxy(final InvocationRecordingWrapper<?> wrapper,
+            final String prefix, final Object object, Class<?> type,
+            final Class<?>... returnTypesToWrap)
+    {
+        return Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]
+            { type }, new InvocationHandler()
+            {
+                @Override
+                public Object invoke(Object obj, Method method, Object[] parameters)
+                        throws Throwable
+                {
+                    String record = wrapper.record(prefix, method, parameters);
+                    Object returnValue = method.invoke(object, parameters);
+                    if (returnValue != null)
+                    {
+                        Class<?> returnValueClass = returnValue.getClass();
+                        Class<?> matchingClass =
+                                tryGetMatchingClass(returnValueClass, returnTypesToWrap);
+                        if (matchingClass != null)
+                        {
+                            return createProxy(wrapper, record, returnValue, matchingClass,
+                                    returnTypesToWrap);
+                        }
+                        if (returnValueClass.isArray())
+                        {
+                            return handleArray(wrapper, record, returnValue, returnValueClass,
+                                    returnTypesToWrap);
+                        }
+                        if (returnValue instanceof List)
+                        {
+                            return handleList(wrapper, record, returnValue, returnTypesToWrap);
+                        }
+                    }
+                    return returnValue;
+                }
+            });
+    }
+
+    private static Object handleList(final InvocationRecordingWrapper<?> wrapper, String prefix,
+            Object returnValue, final Class<?>... returnTypesToWrap)
+    {
+        List<?> returnList = (List<?>) returnValue;
+        if (returnList.isEmpty())
+        {
+            return returnValue;
+        }
+        List<Object> result = new ArrayList<Object>(returnList.size());
+        for (int i = 0; i < returnList.size(); i++)
+        {
+            Object element = returnList.get(i);
+            Class<?> mc = tryGetMatchingClass(element.getClass(), returnTypesToWrap);
+            if (mc == null)
+            {
+                result.add(element);
+            } else
+            {
+                result.add(createProxy(wrapper, prefix + ".get(" + i + ")", element, mc,
+                        returnTypesToWrap));
+            }
+        }
+        return result;
+    }
+
+    private static Object handleArray(final InvocationRecordingWrapper<?> wrapper, String prefix,
+            Object returnValue, Class<?> returnValueClass, final Class<?>... returnTypesToWrap)
+    {
+        Class<?> componentType = returnValueClass.getComponentType();
+        Class<?> clazz = tryGetMatchingClass(componentType, returnTypesToWrap);
+        if (clazz == null)
+        {
+            return returnValue;
+        }
+        int size = Array.getLength(returnValue);
+        Object newArray = Array.newInstance(componentType, size);
+        for (int i = 0; i < size; i++)
+        {
+            Array.set(
+                    newArray,
+                    i,
+                    createProxy(wrapper, prefix + "[" + i + "]", Array.get(returnValue, i), clazz,
+                            returnTypesToWrap));
+        }
+        return newArray;
+    }
+
+    private static Class<?> tryGetMatchingClass(Class<?> clazz, Class<?>... classes)
+    {
+        for (Class<?> c : classes)
+        {
+            if (c.isAssignableFrom(clazz))
+            {
+                return c;
+            }
+        }
+        return null;
+    }
+
+    private List<String> records = new ArrayList<String>();
+
+    private T proxy;
+
+    private InvocationRecordingWrapper()
+    {
+    }
+
+    /**
+     * Returns the proxy of the original object.
+     */
+    public T getProxy()
+    {
+        return proxy;
+    }
+
+    /**
+     * Returns all recorded invocations as a list.
+     */
+    public List<String> getRecords()
+    {
+        return records;
+    }
+
+    /**
+     * Returns all recorded invocations as a multi-line text where each line contains an invocation.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder builder = new StringBuilder();
+        for (String record : records)
+        {
+            if (builder.length() > 0)
+            {
+                builder.append("\n");
+            }
+            builder.append(record);
+        }
+        return builder.toString();
+    }
+
+    private String record(String prefix, Method method, Object[] parameters)
+    {
+        CommaSeparatedListBuilder builder = new CommaSeparatedListBuilder();
+        if (parameters != null)
+        {
+            for (Object parameter : parameters)
+            {
+                builder.append(parameter);
+            }
+        }
+        String record =
+                (prefix == null ? "" : prefix + ".") + method.getName() + "(" + builder + ")";
+        records.add(record);
+        return record;
+    }
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/ArchivingExecutor.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/ArchivingExecutor.java
index ec0ff72cc8dc2ca5703c87aba4e9b9fa2eef92cd..d679681db98ca6b8cff0668ea00684b959fa00fe 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/ArchivingExecutor.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/ArchivingExecutor.java
@@ -29,7 +29,7 @@ import ch.systemsx.cisd.common.logging.ISimpleLogger;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.logging.LogLevel;
-import ch.systemsx.cisd.etlserver.plugins.AutoArchiverTask;
+import ch.systemsx.cisd.common.string.Template;
 import ch.systemsx.cisd.openbis.dss.generic.shared.ArchiverTaskContext;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IArchiverPlugin;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IDataSetDirectoryProvider;
@@ -67,12 +67,16 @@ class ArchivingExecutor implements IPostRegistrationTaskExecutor
 
     private final boolean updateStatus;
 
-    ArchivingExecutor(String dataSetCode, boolean updateStatus, IEncapsulatedOpenBISService service,
-            IArchiverPlugin archiver, IDataSetDirectoryProvider dataSetDirectoryProvider,
+    private Template notificationTemplate;
+
+    ArchivingExecutor(String dataSetCode, boolean updateStatus, Template notificationTemplate,
+            IEncapsulatedOpenBISService service, IArchiverPlugin archiver,
+            IDataSetDirectoryProvider dataSetDirectoryProvider,
             IHierarchicalContentProvider hierarchicalContentProvider)
     {
         this.dataSetCode = dataSetCode;
         this.updateStatus = updateStatus;
+        this.notificationTemplate = notificationTemplate;
         this.service = service;
         this.archiver = archiver;
         this.dataSetDirectoryProvider = dataSetDirectoryProvider;
@@ -120,32 +124,30 @@ class ArchivingExecutor implements IPostRegistrationTaskExecutor
             ProcessingStatus processingStatus = archiver.archive(dataSetAsList, context, false);
             if (false == processingStatus.getErrorStatuses().isEmpty())
             {
-                notifyAdministrator(processingStatus);
+                notifyAdministrator(processingStatus, notificationTemplate.createFreshCopy());
+            }
+            if (updateStatus)
+            {
+                service.compareAndSetDataSetStatus(dataSetCode, BACKUP_PENDING, AVAILABLE, true);
             }
-            service.compareAndSetDataSetStatus(dataSetCode, BACKUP_PENDING, AVAILABLE, true);
         }
     }
 
-    private void notifyAdministrator(ProcessingStatus processingStatus)
+    private void notifyAdministrator(ProcessingStatus processingStatus, Template template)
     {
-        StringBuilder message = new StringBuilder();
-        String failedMessage =
-                String.format("Eager archiving of dataset '%s' has failed. \n", dataSetCode);
-        message.append(failedMessage);
+        template.bind("dataSet", dataSetCode);
+        StringBuilder builder = new StringBuilder();
         for (Status status : processingStatus.getErrorStatuses())
         {
             if (status.tryGetErrorMessage() != null)
             {
-                message.append("Error encountered : " + status.tryGetErrorMessage());
-                message.append("\n");
+                builder.append("Error encountered : " + status.tryGetErrorMessage());
+                builder.append("\n");
             }
         }
-        String footer =
-                String.format("If you wish to archive the dataset in the future, "
-                        + "you can configure an '%s'.", AutoArchiverTask.class.getSimpleName());
-        message.append(footer);
+        template.bind("errors", builder.toString());
 
-        notificationLog.error(message);
+        notificationLog.error(template.createText());
     }
 
     @Override
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/ArchivingPostRegistrationTask.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/ArchivingPostRegistrationTask.java
index e48f26b4f5f1fd99d2146ccc26686436abde6f52..a28b7d18957d463069697bb0f5309ca55f7865c1 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/ArchivingPostRegistrationTask.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/ArchivingPostRegistrationTask.java
@@ -19,6 +19,8 @@ package ch.systemsx.cisd.etlserver.postregistration;
 
 import java.util.Properties;
 
+import ch.systemsx.cisd.common.string.Template;
+import ch.systemsx.cisd.etlserver.plugins.AutoArchiverTask;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IArchiverPlugin;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IDataSetDirectoryProvider;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IDataStoreServiceInternal;
@@ -47,8 +49,12 @@ public class ArchivingPostRegistrationTask extends AbstractPostRegistrationTaskF
                 dataStoreService.getDataSetDirectoryProvider();
         IHierarchicalContentProvider hierarchicalContentProvider =
                 ServiceProvider.getHierarchicalContentProvider();
-        return new ArchivingExecutor(dataSetCode, true, service, archiver, dataSetDirectoryProvider,
-                hierarchicalContentProvider);
+        Template notificationTemplate =
+                new Template("Eager archiving of dataset '${dataSet}' has failed.\n${errors}\n"
+                        + "If you wish to archive the dataset in the future, "
+                        + "you can configure an '" + AutoArchiverTask.class.getSimpleName() + "'.");
+        return new ArchivingExecutor(dataSetCode, true, notificationTemplate, service, archiver,
+                dataSetDirectoryProvider, hierarchicalContentProvider);
     }
 
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/SecondCopyPostRegistrationTask.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/SecondCopyPostRegistrationTask.java
index 2db42b3db1b4737f87a9df102014d92f9aa1925a..5a73dfaa7fa9cd0b107ce65c890ab6ed2fec49a9 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/SecondCopyPostRegistrationTask.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/SecondCopyPostRegistrationTask.java
@@ -21,6 +21,7 @@ import java.util.List;
 import java.util.Properties;
 
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
+import ch.systemsx.cisd.common.string.Template;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.AbstractArchiverProcessingPlugin;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.DataSetFileOperationsManager;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.IDataSetFileOperationsManager;
@@ -33,6 +34,7 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.IDataSetStatusUpdater;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IDataStoreServiceInternal;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IHierarchicalContentProvider;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IShareIdManager;
 import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetArchivingStatus;
 
@@ -48,6 +50,7 @@ public class SecondCopyPostRegistrationTask extends AbstractPostRegistrationTask
     private final IHierarchicalContentProvider hierarchicalContentProvider;
     private final IDataSetDirectoryProvider dataSetDirectoryProvider;
     private final IArchiverPlugin archiver;
+    private final Template notificationTemplate;
 
     public SecondCopyPostRegistrationTask(Properties properties, IEncapsulatedOpenBISService service)
     {
@@ -71,13 +74,18 @@ public class SecondCopyPostRegistrationTask extends AbstractPostRegistrationTask
         dataSetDirectoryProvider = dataStoreService.getDataSetDirectoryProvider();
         File storeRoot = dataSetDirectoryProvider.getStoreRoot();
         properties.setProperty(AbstractArchiverProcessingPlugin.SYNCHRONIZE_ARCHIVE, "false");
-        archiver = new Archiver(properties, storeRoot, fileOperationManager);
+        notificationTemplate =
+                new Template(
+                        "Creating a second copy of dataset '${dataSet}' has failed.\n${errors}");
+        archiver =
+                new Archiver(properties, storeRoot, service, fileOperationManager, dataStoreService
+                        .getDataSetDirectoryProvider().getShareIdManager());
     }
 
     @Override
     public IPostRegistrationTaskExecutor createExecutor(String dataSetCode)
     {
-        return new ArchivingExecutor(dataSetCode, false, service, archiver, dataSetDirectoryProvider,
+        return new ArchivingExecutor(dataSetCode, false, notificationTemplate, service, archiver, dataSetDirectoryProvider,
                 hierarchicalContentProvider);
     }
     
@@ -86,11 +94,12 @@ public class SecondCopyPostRegistrationTask extends AbstractPostRegistrationTask
 
         private static final long serialVersionUID = 1L;
 
-        Archiver(Properties properties, File storeRoot,
-                IDataSetFileOperationsManager fileOperationsManager)
+        Archiver(Properties properties, File storeRoot, IEncapsulatedOpenBISService service,
+                IDataSetFileOperationsManager fileOperationsManager, IShareIdManager shareIdManager)
         {
             super(properties, storeRoot, fileOperationsManager, RsyncArchiver.DeleteAction.DELETE,
                     ChecksumVerificationCondition.IF_AVAILABLE);
+            setService(service);
             setStatusUpdater(new IDataSetStatusUpdater()
                 {
                     @Override
@@ -99,6 +108,7 @@ public class SecondCopyPostRegistrationTask extends AbstractPostRegistrationTask
                     {
                     }
                 });
+            setShareIdManager(shareIdManager);
         }
 
     }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/AbstractArchiverProcessingPlugin.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/AbstractArchiverProcessingPlugin.java
index 504926852e11d679f708045bc3632fc841bd7f1e..014772bf61fcf6b641c86c95fd635f605dc51b00 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/AbstractArchiverProcessingPlugin.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/AbstractArchiverProcessingPlugin.java
@@ -547,10 +547,20 @@ public abstract class AbstractArchiverProcessingPlugin extends AbstractDatastore
         return service;
     }
 
-    public void setStatusUpdater(IDataSetStatusUpdater statusUpdater)
+    protected void setStatusUpdater(IDataSetStatusUpdater statusUpdater)
     {
         this.statusUpdater = statusUpdater;
     }
+    
+    protected void setShareIdManager(IShareIdManager shareIdManager)
+    {
+        this.shareIdManager = shareIdManager;
+    }
+    
+    protected void setService(IEncapsulatedOpenBISService service)
+    {
+        this.service = service;
+    }
 
     /**
      * @author Franz-Josef Elmer
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java
index c5214a04721ab31dba94f02f9defca42353e66bc..72fca00e0c3c094e8b7b6b438a5cc257daf4331a 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java
@@ -186,9 +186,9 @@ public class RsyncArchiver extends AbstractArchiverProcessingPlugin
                         new File(context.getDirectoryProvider().getStoreRoot(), STAGING_FOLDER
                                 + "/" + dataSetCode);
                 temp.mkdirs();
+                IHierarchicalContent archivedContent = null;
                 try
                 {
-                    IHierarchicalContent archivedContent;
                     // We want to perform the check if the archived content is correct
                     // (filesizes/checksums)
                     // For this we want to have the archived content locally. If it is not available
@@ -214,6 +214,11 @@ public class RsyncArchiver extends AbstractArchiverProcessingPlugin
                     status = checkHierarchySizeAndChecksums(root, archivedRoot, checksumVerificationCondition);
                 } finally
                 {
+                    content.close();
+                    if (archivedContent != null)
+                    {
+                        archivedContent.close();
+                    }
                     FileUtils.deleteQuietly(temp);
                 }
             }
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/postregistration/SecondCopyPostRegistrationTaskTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/postregistration/SecondCopyPostRegistrationTaskTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d15acd6e433a7e2d3bf3bfb1d22fe939bff5f2b
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/postregistration/SecondCopyPostRegistrationTaskTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2013 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.etlserver.postregistration;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Properties;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
+import ch.systemsx.cisd.common.filesystem.FileUtilities;
+import ch.systemsx.cisd.common.logging.BufferedAppender;
+import ch.systemsx.cisd.common.logging.LogUtils;
+import ch.systemsx.cisd.common.test.InvocationRecordingWrapper;
+import ch.systemsx.cisd.openbis.common.io.hierarchical_content.DefaultFileBasedHierarchicalContentFactory;
+import ch.systemsx.cisd.openbis.common.io.hierarchical_content.api.IHierarchicalContent;
+import ch.systemsx.cisd.openbis.common.io.hierarchical_content.api.IHierarchicalContentNode;
+import ch.systemsx.cisd.openbis.dss.generic.shared.DataSetDirectoryProvider;
+import ch.systemsx.cisd.openbis.dss.generic.shared.HierarchicalContentProvider;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IConfigProvider;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IDataSetDirectoryProvider;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IDataStoreServiceInternal;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IHierarchicalContentProvider;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IShareIdManager;
+import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProviderTestWrapper;
+import ch.systemsx.cisd.openbis.dss.generic.shared.content.IContentCache;
+import ch.systemsx.cisd.openbis.dss.generic.shared.content.IDssServiceRpcGenericFactory;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalDataLocationNode;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PhysicalDataSet;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.DataSetBuilder;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.DataStoreBuilder;
+import ch.systemsx.cisd.openbis.generic.shared.dto.OpenBISSessionHolder;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+public class SecondCopyPostRegistrationTaskTest extends AbstractFileSystemTestCase
+{
+    private static final String DATA_STORE_CODE = "DSS";
+
+    private static final String EXAMPLE_CONTENT = "Hello world!";
+
+    private static final String DATA_SET1_LOCATION = "a/b/c/ds1";
+
+    private static final String DATA_SET1_EXAMPLE_FILE_PATH = DATA_SET1_LOCATION
+            + "/original/hello.txt";
+
+    private static final String DATA_SET1 = "ds1";
+
+    private static final String SHARE_ID = "1";
+
+    private BufferedAppender logRecorder;
+
+    private Mockery context;
+
+    private IEncapsulatedOpenBISService service;
+
+    private IDataStoreServiceInternal dataStoreService;
+
+    private File destination;
+
+    private SecondCopyPostRegistrationTask task;
+
+    private File store;
+
+    private IShareIdManager shareIdManager;
+
+    private IConfigProvider configProvider;
+
+    private IContentCache contentCache;
+
+    private InvocationRecordingWrapper<IHierarchicalContentProvider> contentProviderRecordingWrapper;
+
+    @BeforeMethod
+    public void beforeMethod()
+    {
+        logRecorder = new BufferedAppender("%-5p %c - %m%n", Level.INFO);
+        logRecorder.addFilter(new Filter()
+            {
+                @Override
+                public int decide(LoggingEvent event)
+                {
+                    String loggerName = event.getLoggerName();
+                    return loggerName.contains("RsyncCopier") ? Filter.DENY : Filter.ACCEPT;
+                }
+            });
+        context = new Mockery();
+        service = context.mock(IEncapsulatedOpenBISService.class);
+        dataStoreService = context.mock(IDataStoreServiceInternal.class);
+        shareIdManager = context.mock(IShareIdManager.class);
+        configProvider = context.mock(IConfigProvider.class);
+        contentCache = context.mock(IContentCache.class);
+        IDssServiceRpcGenericFactory dssServiceFactory =
+                context.mock(IDssServiceRpcGenericFactory.class);
+        store = new File(workingDirectory, "store");
+        final IDataSetDirectoryProvider dirProvider =
+                new DataSetDirectoryProvider(store, shareIdManager);
+        context.checking(new Expectations()
+            {
+                {
+                    allowing(dataStoreService).getDataSetDirectoryProvider();
+                    will(returnValue(dirProvider));
+
+                    allowing(configProvider).getStoreRoot();
+                    will(returnValue(store));
+
+                    allowing(configProvider).getDataStoreCode();
+                    will(returnValue(DATA_STORE_CODE));
+                }
+            });
+        OpenBISSessionHolder sessionHolder = new OpenBISSessionHolder();
+        sessionHolder.setDataStoreCode(DATA_STORE_CODE);
+        contentProviderRecordingWrapper = InvocationRecordingWrapper.<IHierarchicalContentProvider>wrap(
+                new HierarchicalContentProvider(service, shareIdManager, configProvider,
+                        contentCache, new DefaultFileBasedHierarchicalContentFactory(),
+                        dssServiceFactory, sessionHolder, null), IHierarchicalContentProvider.class, IHierarchicalContent.class, IHierarchicalContentNode.class);
+        File exampleFile = new File(store, SHARE_ID + "/" + DATA_SET1_EXAMPLE_FILE_PATH);
+        exampleFile.getParentFile().mkdirs();
+        FileUtilities.writeToFile(exampleFile, EXAMPLE_CONTENT);
+        destination = new File(workingDirectory, "second-copy-destination");
+        Properties properties = new Properties();
+        properties.setProperty("destination", destination.getAbsolutePath());
+        task =
+                new SecondCopyPostRegistrationTask(properties, service, dataStoreService,
+                        contentProviderRecordingWrapper.getProxy());
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void afterMethod()
+    {
+        ServiceProviderTestWrapper.restoreApplicationContext();
+        if (logRecorder != null)
+        {
+            logRecorder.reset();
+        }
+        if (context != null)
+        {
+            // The following line of code should also be called at the end of each test method.
+            // Otherwise one do not known which test failed.
+            context.assertIsSatisfied();
+        }
+    }
+
+    @Test
+    public void testHappyCase()
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(service).listDataSetsByCode(Arrays.asList(DATA_SET1));
+                    PhysicalDataSet ds1 =
+                            new DataSetBuilder(42).code(DATA_SET1)
+                                    .store(new DataStoreBuilder(DATA_STORE_CODE).getStore())
+                                    .fileFormat("TXT").location(DATA_SET1_LOCATION).getDataSet();
+                    will(returnValue(Arrays.asList(ds1)));
+
+                    one(service).tryGetDataSetLocation(DATA_SET1);
+                    will(returnValue(new ExternalDataLocationNode(ds1)));
+
+                    allowing(shareIdManager).getShareId(DATA_SET1);
+                    will(returnValue(SHARE_ID));
+
+                    one(shareIdManager).lock(DATA_SET1);
+                    one(shareIdManager).releaseLock(DATA_SET1);
+
+                    one(service)
+                            .updateShareIdAndSize(DATA_SET1, SHARE_ID, EXAMPLE_CONTENT.length());
+                }
+            });
+
+        IPostRegistrationTaskExecutor executor = task.createExecutor(DATA_SET1, false);
+        executor.execute();
+
+        assertEquals(
+                "INFO  OPERATION.AbstractDatastorePlugin - Archiving of the "
+                        + "following datasets has been requested: [Dataset 'ds1']\n"
+                        + "INFO  OPERATION.DataSetFileOperationsManager - "
+                        + "Copy dataset 'ds1' from '" + store.getPath() + "/1/a/b/c/ds1' to '"
+                        + destination.getAbsolutePath() + "/a/b/c", logRecorder.getLogContent());
+        assertEquals(
+                EXAMPLE_CONTENT,
+                FileUtilities.loadToString(
+                        new File(store, SHARE_ID + "/" + DATA_SET1_EXAMPLE_FILE_PATH)).trim());
+        assertEquals(EXAMPLE_CONTENT,
+                FileUtilities.loadToString(new File(destination, DATA_SET1_EXAMPLE_FILE_PATH))
+                        .trim());
+        assertEquals(
+                "asContent(ds1)\n"
+                        + "asContent(ds1).getRootNode()\n"
+                        + "asContent(ds1).getRootNode().getRelativePath()\n"
+                        + "asContent(ds1).getRootNode().isDirectory()\n"
+                        + "asContent(ds1).getRootNode().getChildNodes()\n"
+                        + "asContent(ds1).getRootNode().getChildNodes().get(0).getRelativePath()\n"
+                        + "asContent(ds1).getRootNode().getChildNodes().get(0).isDirectory()\n"
+                        + "asContent(ds1).getRootNode().getChildNodes().get(0).getChildNodes()\n"
+                        + "asContent(ds1).getRootNode().getChildNodes().get(0).getChildNodes().get(0).getRelativePath()\n"
+                        + "asContent(ds1).getRootNode().getChildNodes().get(0).getChildNodes().get(0).isDirectory()\n"
+                        + "asContent(ds1).getRootNode().getChildNodes().get(0).getChildNodes().get(0).getFileLength()\n"
+                        + "asContent(ds1).getRootNode().getChildNodes().get(0).getChildNodes().get(0).isChecksumCRC32Precalculated()\n"
+                        + "asContent(ds1).close()", contentProviderRecordingWrapper.toString());
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testDestinationIsNotADirectory() throws IOException
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(service).listDataSetsByCode(Arrays.asList(DATA_SET1));
+                    PhysicalDataSet ds1 =
+                            new DataSetBuilder(42).code(DATA_SET1)
+                                    .store(new DataStoreBuilder(DATA_STORE_CODE).getStore())
+                                    .fileFormat("TXT").location(DATA_SET1_LOCATION).getDataSet();
+                    will(returnValue(Arrays.asList(ds1)));
+
+                    allowing(shareIdManager).getShareId(DATA_SET1);
+                    will(returnValue(SHARE_ID));
+
+                    one(service)
+                            .updateShareIdAndSize(DATA_SET1, SHARE_ID, EXAMPLE_CONTENT.length());
+                }
+            });
+
+        destination.createNewFile();
+        IPostRegistrationTaskExecutor executor = task.createExecutor(DATA_SET1, false);
+        executor.execute();
+
+        assertEquals("INFO  OPERATION.AbstractDatastorePlugin - Archiving of the "
+                + "following datasets has been requested: [Dataset 'ds1']\n"
+                + "INFO  OPERATION.DataSetFileOperationsManager - Copy dataset 'ds1' from '"
+                + store.getPath() + "/1/a/b/c/ds1' to '" + destination.getAbsolutePath()
+                + "/a/b/c\n" + "ERROR OPERATION.AbstractDatastorePlugin - Archiving failed :path '"
+                + destination.getAbsolutePath() + "/a/b/c' does not exist\n"
+                + "java.io.IOException: path '" + destination.getAbsolutePath()
+                + "/a/b/c' does not exist\n" + "ERROR OPERATION.AbstractDatastorePlugin - "
+                + "Archiving for dataset ds1 finished with the status: "
+                + "ERROR: \"Archiving failed :path '" + destination.getAbsolutePath()
+                + "/a/b/c' does not exist\".\n" + "ERROR NOTIFY.ArchivingExecutor - "
+                + "Creating a second copy of dataset 'ds1' has failed.\n"
+                + "Error encountered : Archiving failed :path '" + destination.getAbsolutePath()
+                + "/a/b/c' does not exist",
+                LogUtils.removeEmbeddedStackTrace(logRecorder.getLogContent()));
+        assertEquals(
+                EXAMPLE_CONTENT,
+                FileUtilities.loadToString(
+                        new File(store, SHARE_ID + "/" + DATA_SET1_EXAMPLE_FILE_PATH)).trim());
+        assertEquals(false, new File(destination, DATA_SET1_EXAMPLE_FILE_PATH).exists());
+        assertEquals("", contentProviderRecordingWrapper.toString());
+        context.assertIsSatisfied();
+    }
+
+}