diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/AttachmentSynchronizationTaskExecutor.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/AttachmentSynchronizationTaskExecutor.java
index 4adeaa805c79f60dffc6771992b9994fed87ce53..7aa70fadfda80b420fe8df945cfb22d12d8a8a0b 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/AttachmentSynchronizationTaskExecutor.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/AttachmentSynchronizationTaskExecutor.java
@@ -53,6 +53,8 @@ final class AttachmentSynchronizationTaskExecutor implements ITaskExecutor<Ident
     private final SyncConfig config;
 
     private final IEncapsulatedOpenBISService service;
+    
+    private AttachmentsOperationsHandler attachmentsOperationsHandler;
 
     public AttachmentSynchronizationTaskExecutor(IEncapsulatedOpenBISService service, Date lastSyncTimestamp, SyncConfig config)
     {
@@ -64,103 +66,114 @@ final class AttachmentSynchronizationTaskExecutor implements ITaskExecutor<Ident
     @Override
     public Status execute(Identifier<?> item)
     {
-        V3Utils dssFileUtils = V3Utils.create(config.getDataSourceOpenbisURL(), config.getDataSourceDSSURL());
-        String sessionToken = dssFileUtils.login(config.getUser(), config.getPassword());
+        TechId techId = null;
+        ICommonServer commonServer = ServiceFinderUtils.getCommonServer(ServiceProvider.getConfigProvider().getOpenBisServerUrl());
+        String localSessionToken = ServiceFinderUtils.login(commonServer, config.getHarvesterUser(), config.getHarvesterPass());
         if (item instanceof NewExperiment)
         {
-            List<Attachment> incomingAttachments = dssFileUtils.getExperimentAttachments(sessionToken, new ExperimentPermId(item.getPermID()));
+            Experiment experiment = service.tryGetExperiment(ExperimentIdentifierFactory.parse(item.getIdentifier()));
+            techId = new TechId(experiment.getId());
+            attachmentsOperationsHandler = new ExperimentAttachmentsOperationsHandler(config, commonServer, localSessionToken);
+        }
+        List<Attachment> incomingAttachments = attachmentsOperationsHandler.listAttachmentsDataSource(config, item.getPermID());
 
-            // place the incoming attachments in a map
-            Map<String, Attachment> incomingAttachmentMap = new HashMap<String, Attachment>();
-            for (Attachment incoming : incomingAttachments)
-            {
-                incomingAttachmentMap.put(incoming.getFileName(), incoming);
-            }
+        // place the incoming attachments in a map
+        Map<String, Attachment> incomingAttachmentMap = new HashMap<String, Attachment>();
+        for (Attachment incoming : incomingAttachments)
+        {
+            incomingAttachmentMap.put(incoming.getFileName(), incoming);
+        }
 
-            Experiment experiment = service.tryGetExperiment(ExperimentIdentifierFactory.parse(item.getIdentifier()));
-            List<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment> existingAttachments =
-                    service.listAttachments(AttachmentHolderKind.EXPERIMENT, experiment.getId());
-            Map<String, ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment> existingAttachmentMap =
-                    new HashMap<String, ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment>();
+        List<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment> existingAttachments =
+                attachmentsOperationsHandler.listAttachmentsHarvester(item.getIdentifier());
+        Map<String, ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment> existingAttachmentMap =
+                new HashMap<String, ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment>();
 
-            // place the existing attachments in a map
-            for (ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment attachment : existingAttachments)
+        // place the existing attachments in a map
+        for (ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment attachment : existingAttachments)
+        {
+            existingAttachmentMap.put(attachment.getFileName(), attachment);
+        }
+
+        for (Attachment incoming : incomingAttachments)
+        {
+            ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment existingAttachment =
+                    existingAttachmentMap.get(incoming.getFileName());
+            if (existingAttachment == null)
             {
-                existingAttachmentMap.put(attachment.getFileName(), attachment);
+                addAttachments(incoming, 1, techId, attachmentsOperationsHandler);
             }
-
-            ICommonServer commonServer = ServiceFinderUtils.getCommonServer(ServiceProvider.getConfigProvider().getOpenBisServerUrl());
-            String localSessionToken = ServiceFinderUtils.login(commonServer, config.getHarvesterUser(), config.getHarvesterPass());
-            TechId experimentId = new TechId(experiment.getId());
-            for (Attachment incoming : incomingAttachments)
+            else
             {
-                ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment existingAttachment =
-                        existingAttachmentMap.get(incoming.getFileName());
-                if (existingAttachment == null)
+                int version = existingAttachment.getVersion();
+                if (incoming.getVersion() < version)
                 {
-                    addAttachments(incoming, 1, experimentId, commonServer, localSessionToken);
+                    // Harvester has a later version of the attachment. Delete it from harvester
+                    replaceAttachment(techId, incoming);
                 }
-                else
+                else if (incoming.getVersion() == version)
                 {
-                    int version = existingAttachment.getVersion();
-                    if (incoming.getVersion() < version)
+                    // check last sync date and meta data
+                    if (incoming.getRegistrationDate().after(lastSyncTimestamp))
                     {
-                        // Harvester has a later version of the attachment. Delete it from harvester
-                        commonServer.deleteExperimentAttachments(localSessionToken, experimentId,
-                                Arrays.asList(incoming.getFileName()), "Synchronization from data source " + config.getDataSourceAlias());
-                        addAttachments(incoming, 1, experimentId, commonServer, localSessionToken);
+                        replaceAttachment(techId, incoming);
                     }
-                    else if (incoming.getVersion() == version)
+                    else
                     {
-                        // check last sync date and meta data
-                        if (incoming.getRegistrationDate().after(lastSyncTimestamp))
-                        {
-                            // Data source has the same version number but with a later registration date than the last sync timestamp:
-                            // This means, the attachment was probably deleted in the data source and re-registered. Delete it from harvester
-                            // and re-register
-                            commonServer.deleteExperimentAttachments(localSessionToken, experimentId,
-                                    Arrays.asList(incoming.getFileName()), "Synchronization from data source " + config.getDataSourceAlias());
-                            addAttachments(incoming, 1, experimentId, commonServer, localSessionToken);
-                        }
-                        else
+                        // check if meta data changed
+                        if (equalsNullable(incoming.getTitle(), existingAttachment.getTitle()) == false
+                                || equalsNullable(incoming.getDescription(), existingAttachment.getDescription()) == false)
                         {
-                            // check if meta data changed
-                            if (incoming.getTitle().equals(existingAttachment.getTitle()) == false
-                                    || incoming.getDescription().equals(existingAttachment.getDescription()))
-                            {
-                                ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment updateDTO =
-                                        new ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment();
-                                updateDTO.setFileName(existingAttachment.getFileName());
-                                updateDTO.setVersion(existingAttachment.getVersion());
-                                updateDTO.setTitle(incoming.getTitle());
-                                updateDTO.setDescription(incoming.getDescription());
-                                commonServer.updateExperimentAttachments(localSessionToken, experimentId, updateDTO);
-                            }
+                            ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment updateDTO =
+                                    new ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment();
+                            updateDTO.setFileName(existingAttachment.getFileName());
+                            updateDTO.setVersion(existingAttachment.getVersion());
+                            updateDTO.setTitle(incoming.getTitle());
+                            updateDTO.setDescription(incoming.getDescription());
+                            attachmentsOperationsHandler.updateAttachment(techId, updateDTO);
                         }
                     }
-                    else
-                    {
-                        // add all new versions from the incoming (do we need to check last sync date)
-                        // Attachment attachmentVersion = getVersion(incoming, version);
-                        addAttachments(incoming, version + 1, experimentId, commonServer, localSessionToken);
-                    }
                 }
-            }
-            // loop through existing attachments and if they no longer exist in data source, delete them.
-            for (ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment existing : existingAttachments)
-            {
-                if (incomingAttachmentMap.get(existing.getFileName()) == null)
+                else
                 {
-                    commonServer.deleteExperimentAttachments(localSessionToken, experimentId,
-                            Arrays.asList(existing.getFileName()), "Synchronization from data source " + config.getDataSourceAlias()
-                                    + " Attachment no longer exists on data source.");
+                    // add all new versions from the incoming (do we need to check last sync date)
+                    // Attachment attachmentVersion = getVersion(incoming, version);
+                    addAttachments(incoming, version + 1, techId, attachmentsOperationsHandler);
                 }
             }
         }
+        // loop through existing attachments and if they no longer exist in data source, delete them.
+        for (ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment existing : existingAttachments)
+        {
+            if (incomingAttachmentMap.get(existing.getFileName()) == null)
+            {
+                attachmentsOperationsHandler.deleteAttachment(techId, existing.getFileName());
+            }
+        }
         return null;
     }
 
-    private void addAttachments(Attachment attachment, int fromVersion, TechId techId, ICommonServer commonServer, String sessionToken)
+    private boolean equalsNullable(String s1OrNull, String s2OrNull)
+    {
+        if (s1OrNull == null)
+        {
+            return s2OrNull == null ? true : false;
+        } else if (s2OrNull == null)
+        {
+            return false;
+        } else
+        {
+            return s1OrNull.equals(s2OrNull);
+        }
+    }
+
+    private void replaceAttachment(TechId techId, Attachment incoming)
+    {
+        attachmentsOperationsHandler.deleteAttachment(techId, incoming.getFileName());
+        addAttachments(incoming, 1, techId, attachmentsOperationsHandler);
+    }
+
+    private void addAttachments(Attachment attachment, int fromVersion, TechId techId, AttachmentsOperationsHandler handler)
     {
 
         Integer version = attachment.getVersion();
@@ -182,8 +195,78 @@ final class AttachmentSynchronizationTaskExecutor implements ITaskExecutor<Ident
         NewAttachment earliestVersion = versions.pollLast();
         while (earliestVersion != null)
         {
-            commonServer.addExperimentAttachment(sessionToken, techId, earliestVersion);
+            handler.addAttachment(techId, earliestVersion);
             earliestVersion = versions.pollLast();
         }
     }
 }
+
+interface AttachmentsOperationsHandler
+{
+    List<Attachment> listAttachmentsDataSource(SyncConfig config, String permId);
+    
+    List<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment> listAttachmentsHarvester(String identifier);
+    
+    void addAttachment(TechId techId, NewAttachment attachment);
+
+    void deleteAttachment(TechId experimentId, String fileName);
+
+    void updateAttachment(TechId experimentId, ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment attachment);
+}
+
+class ExperimentAttachmentsOperationsHandler implements AttachmentsOperationsHandler
+{
+    final ICommonServer commonServer;
+
+    final String sessionToken;
+
+    final SyncConfig config;
+
+    /**
+     * @param config
+     * @param commonServer
+     * @param localSessionToken
+     */
+    public ExperimentAttachmentsOperationsHandler(SyncConfig config, ICommonServer commonServer, String sessionToken)
+    {
+        this.commonServer = commonServer;
+        this.sessionToken = sessionToken;
+        this.config = config;
+    }
+
+    @Override
+    public List<Attachment> listAttachmentsDataSource(SyncConfig config, String permId)
+    {
+        V3Utils dssFileUtils = V3Utils.create(config.getDataSourceOpenbisURL(), config.getDataSourceDSSURL());
+        String sessionToken = dssFileUtils.login(config.getUser(), config.getPassword());
+        return dssFileUtils.getExperimentAttachments(sessionToken, new ExperimentPermId(permId));
+    }
+
+    @Override
+    public List<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment> listAttachmentsHarvester(String identifier)
+    {
+        IEncapsulatedOpenBISService service = ServiceProvider.getOpenBISService();
+        Experiment experiment = service.tryGetExperiment(ExperimentIdentifierFactory.parse(identifier));
+        return service.listAttachments(AttachmentHolderKind.EXPERIMENT, experiment.getId());
+    }
+
+    @Override
+    public void addAttachment(TechId techId, NewAttachment attachment)
+    {
+        commonServer.addExperimentAttachment(sessionToken, techId, attachment);
+    }
+
+    @Override
+    public void deleteAttachment(TechId experimentId, String fileName)
+    {
+        commonServer.deleteExperimentAttachments(sessionToken, experimentId, Arrays.asList(fileName),
+                "Synchronization from data source " + config.getDataSourceAlias()
+                        + " Attachment no longer exists on data source.");
+    }
+
+    @Override
+    public void updateAttachment(TechId experimentId, ch.systemsx.cisd.openbis.generic.shared.basic.dto.Attachment attachment)
+    {
+        commonServer.updateExperimentAttachments(sessionToken, experimentId, attachment);
+    }
+}
\ No newline at end of file
diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/EntitySynchronizer.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/EntitySynchronizer.java
index 911498dccc71db1f8a41636b8cf18ad7ed3a0e1f..e3f13e43c0a3450eb272c2163dd7ef0c9bc64ee2 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/EntitySynchronizer.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/EntitySynchronizer.java
@@ -219,7 +219,7 @@ public class EntitySynchronizer
         operationLog.info("entity operation result: " + operationResult);
 
         operationLog.info("processing attachments...");
-        // processAttachments(entitiesWithAttachments, lastSyncTimestamp);
+        // processAttachments(entitiesWithAttachments);
 
         // register physical data sets without any hierarchy
         // Note that container/component and parent/child relationships are established post-reg.
@@ -1014,6 +1014,7 @@ public class EntitySynchronizer
         }
         return ProjectIdentifierFactory.parse(projectIdentifier);
     }
+
     private List<Sample> getChildSamples(Sample sampleWithExperiment)
     {
         ListSampleCriteria criteria = ListSampleCriteria.createForParent(new TechId(sampleWithExperiment.getId()));
@@ -1032,7 +1033,6 @@ public class EntitySynchronizer
         return null;
     }
 
-
     private final class DataSetRegistrationTaskExecutor implements ITaskExecutor<DataSetWithConnections>
     {
         private DataSetRegistrationSummary dsRegistrationSummary;
diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/V3Utils.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/V3Utils.java
index f3c5e4573bfa566735b74ee420654770f9da0c5d..d83f38d770acde119544c30ee69c4cbb49251609 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/V3Utils.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/V3Utils.java
@@ -23,7 +23,6 @@ import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.attachment.Attachment;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.attachment.fetchoptions.AttachmentFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions.ExperimentFetchOptions;
@@ -78,9 +77,11 @@ public class V3Utils
     public List<Attachment> getExperimentAttachments(String sessionToken, IExperimentId experimentId)
     {
         ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions();
-        AttachmentFetchOptions attachmentFetchOptions = fetchOptions.withAttachments();
-        attachmentFetchOptions.withContent();
-        attachmentFetchOptions.withPreviousVersion().withContent();
+        fetchOptions.withAttachments().withContent();
+        fetchOptions.withAttachments().withPreviousVersion().withPreviousVersionUsing(fetchOptions.withAttachments());
+        fetchOptions.withAttachments().withPreviousVersion().withContentUsing(fetchOptions.withAttachments().withContent());// attachmentFetchOptions.withContent();
+        // attachmentFetchOptions.withPreviousVersion().withPreviousVersionUsing(fetchOptions.withAttachments());
+        // .withContentUsing(attachmentFetchOptions.withContent());
         Map<IExperimentId, Experiment> experiments = as.getExperiments(sessionToken, Arrays.asList(experimentId), fetchOptions);
         if(experiments.size() == 1) {
             Experiment experiment = experiments.get(experimentId);