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 f9152d3b38095a54cef8c5ac62cdb82f1d5c2462..c52737eaa3d983557429b636506e93f8dfdf6683 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
@@ -39,6 +39,7 @@ import org.apache.commons.codec.binary.Hex;
 import org.apache.commons.collections4.map.MultiKeyMap;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.time.StopWatch;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 import org.w3c.dom.Document;
@@ -84,6 +85,7 @@ import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronize
 import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronizer.translator.INameTranslator;
 import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronizer.translator.PrefixBasedNameTranslator;
 import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronizer.util.DSPropertyUtils;
+import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronizer.util.Monitor;
 import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronizer.util.V3Utils;
 import ch.systemsx.cisd.cifex.shared.basic.UserFailureException;
 import ch.systemsx.cisd.common.concurrent.ParallelizedExecutor;
@@ -198,6 +200,7 @@ public class EntitySynchronizer
 
     private void registerDataSets(ResourceListParserData data, List<String> notSyncedAttachmentsHolders) throws IOException
     {
+        Monitor monitor = new Monitor("Register data sets", operationLog);
         // register physical data sets without any hierarchy
         // Note that container/component and parent/child relationships are established post-reg.
         // setParentDataSetsOnTheChildren(data);
@@ -237,10 +240,12 @@ public class EntitySynchronizer
         skippedDataSets.addAll(notRegisteredDataSetCodes);
         skippedDataSets.addAll(blackListedDataSetCodes);
         establishDataSetRelationships(data.getDataSetsToProcess(), skippedDataSets, physicalDSMap);
+        monitor.log();
     }
 
     private List<String> registerAttachments(ResourceListParserData data, MultiKeyMap<String, String> newEntities)
     {
+        Monitor monitor = new Monitor("Register attachments", operationLog);
         operationLog.info("Processing attachments...");
         List<IncomingEntity<?>> attachmentHoldersToProcess =
                 data.filterAttachmentHoldersByLastModificationDate(lastSyncTimestamp, attachmentHolderCodesToRetry);
@@ -262,12 +267,14 @@ public class EntitySynchronizer
                             : "synchronization of attachments for "
                                     + notSyncedAttachmentsHolders.size() + " entitities FAILED."));
         }
+        monitor.log();
         return notSyncedAttachmentsHolders;
     }
 
     private MultiKeyMap<String, String> registerEntities(ResourceListParserData data)
     {
-        AtomicEntityOperationDetails entityOperationDetails = createEntityOperationDetails(data);
+        Monitor monitor = new Monitor("Register entities", operationLog);
+        AtomicEntityOperationDetails entityOperationDetails = createEntityOperationDetails(data, monitor);
 
         MultiKeyMap<String, String> newEntities = new MultiKeyMap<String, String>();
         if (config.isDryRun() == false)
@@ -276,17 +283,18 @@ public class EntitySynchronizer
             newEntities = getNewEntities(entityOperationDetails);
             operationLog.info("Entity operation result: " + operationResult);
         }
+        monitor.log();
         return newEntities;
     }
 
-    private AtomicEntityOperationDetails createEntityOperationDetails(ResourceListParserData data)
+    private AtomicEntityOperationDetails createEntityOperationDetails(ResourceListParserData data, Monitor monitor)
     {
         AtomicEntityOperationDetailsBuilder builder = new AtomicEntityOperationDetailsBuilder();
 
         processSpaces(data, builder);
         processProjects(data, builder);
         processExperiments(data, builder);
-        processSamples(data, builder);
+        processSamples(data, builder, monitor);
         processMaterials(data, builder);
 
         AtomicEntityOperationDetails entityOperationDetails = builder.getDetails();
@@ -311,24 +319,26 @@ public class EntitySynchronizer
 
     private ResourceListParserData parseResourceList(Document doc) throws XPathExpressionException
     {
+        Monitor monitor = new Monitor("Parsing resource list", operationLog);
         // Parse the resource list: This sends back all projects,
         // experiments, samples and data sets contained in the XML together with their last modification date to be used for filtering
         operationLog.info("Parsing the resource list xml document...");
         INameTranslator nameTranslator = new PrefixBasedNameTranslator(config.getDataSourceAlias());
 
         ResourceListParser parser = ResourceListParser.create(nameTranslator, dataStoreCode);
-        ResourceListParserData data = parser.parseResourceListDocument(doc);
+        ResourceListParserData data = parser.parseResourceListDocument(doc, monitor);
+        monitor.log();
         return data;
     }
 
     private Document getResourceList() throws Exception
     {
-        
+        Monitor monitor = new Monitor("Retrieving resoure list", operationLog);
         DataSourceConnector dataSourceConnector =
                 new DataSourceConnector(config.getDataSourceURI(), config.getAuthenticationCredentials(), operationLog);
-        operationLog.info("\nRetrieving the resource list...");
+        operationLog.info("Retrieving the resource list...");
         Document doc = dataSourceConnector.getResourceListAsXMLDoc(Arrays.asList(ArrayUtils.EMPTY_STRING_ARRAY));
-        
+        monitor.log();
         return doc;
     }
 
@@ -736,6 +746,7 @@ public class EntitySynchronizer
 
     private void registerMasterData(MasterData masterData)
     {
+        Monitor monitor = new Monitor("Register master data", operationLog);
         operationLog.info("Registering master data...");
         if (config.isVerbose() == true)
         {
@@ -753,6 +764,7 @@ public class EntitySynchronizer
         MasterDataSynchronizer masterDataSyncronizer =
                 new MasterDataSynchronizer(config.getHarvesterUser(), config.getHarvesterPass(), config.isDryRun(), config.isVerbose(), operationLog);
         masterDataSyncronizer.synchronizeMasterData(masterData);
+        monitor.log();
     }
 
     private void verboseLogMasterData(Set<String> types, String typeName)
@@ -769,6 +781,7 @@ public class EntitySynchronizer
 
     private void processDeletions(ResourceListParserData data) throws NoSuchAlgorithmException, UnsupportedEncodingException
     {
+        Monitor monitor = new Monitor("Delete entities", operationLog);
         String sessionToken = ServiceProvider.getOpenBISService().getSessionToken();
         IEntityRetriever entityRetriever =
                 SkinnyEntityRetriever.createWithSessionToken(ServiceProvider.getV3ApplicationService(), sessionToken);
@@ -899,6 +912,7 @@ public class EntitySynchronizer
 
         if (config.isDryRun() == true)
         {
+            monitor.log();
             return;
         }
 
@@ -980,6 +994,7 @@ public class EntitySynchronizer
                     getDirectoryProvider().getDataSetDirectory(physicalDS);
             SegmentedStoreUtils.deleteDataSetInstantly(physicalDS.getCode(), datasetDir, new Log4jSimpleLogger(operationLog));
         }
+        monitor.log();
     }
 
     private void verboseLogDeletions(Collection<String> identifiers, String entityKind)
@@ -1170,14 +1185,20 @@ public class EntitySynchronizer
         return prjUpdate;
     }
 
-    private void processSamples(ResourceListParserData data, AtomicEntityOperationDetailsBuilder builder)
+    private void processSamples(ResourceListParserData data, AtomicEntityOperationDetailsBuilder builder, Monitor monitor)
     {
         // process samples
         Map<String, IncomingSample> samplesToProcess = data.getSamplesToProcess();
         Map<SampleIdentifier, NewSample> samplesToUpdate = new HashMap<SampleIdentifier, NewSample>();
         Set<String> sampleWithUpdatedParents = new HashSet<String>();
+        int count = 0;
+        int n = samplesToProcess.size();
         for (IncomingSample sample : samplesToProcess.values())
         {
+            if (++count % 100 == 0)
+            {
+                monitor.log(String.format("%7d/%d sample: %s", count, n, sample.getIdentifer()));
+            }
             NewSample incomingSample = sample.getSample();
             if (sample.getLastModificationDate().after(lastSyncTimestamp))
             {
diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/ResourceListParser.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/ResourceListParser.java
index cef2b91320e6a5a9af54eab8413bf78e3dd2046f..e55a13d8399de4f277bf0ef339e7f451e59b211e 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/ResourceListParser.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/ResourceListParser.java
@@ -26,7 +26,9 @@ import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.TimeZone;
+import java.util.TreeMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -55,6 +57,7 @@ import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronize
 import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronizer.ResourceListParserData.MaterialWithLastModificationDate;
 import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronizer.translator.DefaultNameTranslator;
 import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronizer.translator.INameTranslator;
+import ch.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronizer.util.Monitor;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityProperty;
@@ -112,7 +115,71 @@ public class ResourceListParser
         return create(new DefaultNameTranslator(), dataStoreCode);
     }
 
-    public ResourceListParserData parseResourceListDocument(Document doc) throws XPathExpressionException
+    public ResourceListParserData parseResourceListDocument(Document doc, Monitor monitor) throws XPathExpressionException
+    {
+        XPath xpath = createXPath();
+        Date resourceListTimestamp = getResourceListTimestamp(doc, xpath);
+        data.setResourceListTimestamp(resourceListTimestamp);
+
+        monitor.log();
+        NodeList nodes = doc.getDocumentElement().getChildNodes();
+        for (int i = 0; i < nodes.getLength(); i++)
+        {
+            Node node = nodes.item(i);
+            Map<String, List<Node>> childrenByType = getChildrenByType(node);
+            List<Node> locNodes = childrenByType.get("loc");
+            if (locNodes == null)
+            {
+                continue;
+            }
+            String uri = locNodes.get(0).getTextContent();
+            if (uri.endsWith("MASTER_DATA/MASTER_DATA/M"))
+            {
+                parseMasterData(doc, xpath, uri);
+            }
+        }
+        for (int i = 0, n = nodes.getLength(); i < n; i++)
+        {
+            Node node = nodes.item(i);
+            Map<String, List<Node>> childrenByType = getChildrenByType(node);
+            List<Node> locNodes = childrenByType.get("loc");
+            if (locNodes == null)
+            {
+                continue;
+            }
+            String uri = locNodes.get(0).getTextContent();
+            if (uri.endsWith("MASTER_DATA/MASTER_DATA/M") == false && uri.endsWith("/M"))
+            {
+                List<Node> lastmodNodes = childrenByType.get("lastmod");
+                if (lastmodNodes == null)
+                {
+                    continue;
+                }
+                String lastModDataStr = lastmodNodes.get(0).getTextContent().trim();
+                try
+                {
+                    Date lastModificationDate = convertFromW3CDate(lastModDataStr);
+                    List<Node> xdNodes = childrenByType.get("x:xd");
+                    if (xdNodes == null)
+                    {
+                        continue;
+                    }
+                    if ((i + 1) % 10000 == 0)
+                    {
+                        monitor.log(String.format("%7d/%d uri: %s", i + 1, n, uri));
+                    }
+                    parseMetaData(uri, lastModificationDate, xdNodes.get(0));
+                } catch (ParseException e)
+                {
+                    throw new XPathExpressionException("Last modification date cannot be parsed:" + lastModDataStr);
+                }
+            }
+        }
+
+        return data;
+    }
+
+    private XPath createXPath()
     {
         XPath xpath = XPathFactory.newInstance().newXPath();
         xpath.setNamespaceContext(new NamespaceContext()
@@ -144,16 +211,7 @@ public class ResourceListParser
                     throw new UnsupportedOperationException("Not implemented!!!");
                 }
             });
-        Date resourceListTimestamp = getResourceListTimestamp(doc, xpath);
-        data.setResourceListTimestamp(resourceListTimestamp);
-
-        List<String> uris = getResourceLocations(doc, xpath);
-        for (String uri : uris)
-        {
-            parseUriMetaData(doc, xpath, uri);
-        }
-
-        return data;
+        return xpath;
     }
 
     private Date getResourceListTimestamp(Document doc, XPath xpath) throws XPathExpressionException
@@ -170,27 +228,6 @@ public class ResourceListParser
         }
     }
 
-    private List<String> getResourceLocations(Document doc, XPath xpath) throws XPathExpressionException
-    {
-        XPathExpression expr = xpath.compile("/s:urlset/s:url/s:loc");// "//*[local-name()='loc']/text()"); //"//s:loc/text()"
-
-        Object result = expr.evaluate(doc, XPathConstants.NODESET);
-        NodeList nodes = (NodeList) result;
-        List<String> list = new ArrayList<String>();
-        for (int i = 0; i < nodes.getLength(); i++)
-        {
-            String uri = nodes.item(i).getTextContent();
-            if (uri.endsWith("MASTER_DATA/MASTER_DATA/M"))
-            {
-                parseMasterData(doc, xpath, uri);
-            } else if (uri.endsWith("/M"))
-            {
-                list.add(uri);
-            }
-        }
-        return list;
-    }
-
     private void parseMasterData(Document doc, XPath xpath, String uri) throws XPathExpressionException
     {
         MasterDataParser mdParser = MasterDataParser.create(nameTranslator);
@@ -207,48 +244,48 @@ public class ResourceListParser
         masterData.setPropertyAssignmentsToProcess(mdParser.getEntityPropertyAssignments());
     }
 
-    private void parseUriMetaData(Document doc, XPath xpath, String uri) throws XPathExpressionException
+    private void parseMetaData(String uri, Date lastModificationDate, Node xdNode) throws XPathExpressionException
     {
-        Date lastModificationDate = extractLastModificationDate(doc, xpath, uri);
-
-        Node xdNode = extractXdNode(doc, xpath, uri);
         String entityKind = xdNode.getAttributes().getNamedItem("kind").getTextContent();
 
         if (SyncEntityKind.PROJECT.getLabel().equals(entityKind))
         {
-            parseProjectMetaData(xpath, extractPermIdFromURI(uri), xdNode, lastModificationDate);
+            parseProjectMetaData(extractPermIdFromURI(uri), xdNode, lastModificationDate);
         } else if (SyncEntityKind.EXPERIMENT.getLabel().equals(entityKind))
         {
-            parseExperimentMetaData(xpath, extractPermIdFromURI(uri), xdNode, lastModificationDate);
+            parseExperimentMetaData(extractPermIdFromURI(uri), xdNode, lastModificationDate);
         } else if (SyncEntityKind.SAMPLE.getLabel().equals(entityKind))
         {
-            parseSampleMetaData(xpath, extractPermIdFromURI(uri), xdNode, lastModificationDate);
+            parseSampleMetaData(extractPermIdFromURI(uri), xdNode, lastModificationDate);
         } else if (SyncEntityKind.DATA_SET.getLabel().equals(entityKind))
         {
-            parseDataSetMetaData(xpath, extractDataSetCodeFromURI(uri), xdNode, lastModificationDate);
+            parseDataSetMetaData(extractDataSetCodeFromURI(uri), xdNode, lastModificationDate);
         } else if (SyncEntityKind.MATERIAL.getLabel().equals(entityKind))
         {
-            parseMaterialMetaData(xpath, extractMaterialCodeFromURI(uri), xdNode, lastModificationDate);
+            parseMaterialMetaData(extractMaterialCodeFromURI(uri), xdNode, lastModificationDate);
         }
     }
 
-    private Date extractLastModificationDate(Document doc, XPath xpath, String uri) throws XPathExpressionException
+    private Map<String, List<Node>> getChildrenByType(Node node)
     {
-        XPathExpression expr = xpath.compile("//s:url/s:loc[normalize-space(.)='" + uri + "']//following-sibling::s:lastmod[1]");
-        Node lastModNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
-        if (lastModNode == null)
+        Map<String, List<Node>> result = new TreeMap<>();
+        if (node.hasChildNodes())
         {
-            throw new XPathExpressionException("The resource list should contain 1 lastmod element per resource");
-        }
-
-        String lastModDataStr = lastModNode.getTextContent().trim();
-        try
-        {
-            return convertFromW3CDate(lastModDataStr);
-        } catch (ParseException e)
-        {
-            throw new XPathExpressionException("Last modification date cannot be parsed:" + lastModDataStr);
+            NodeList childNodes = node.getChildNodes();
+            for (int i = 0, n = childNodes.getLength(); i < n; i++)
+            {
+                Node childNode = childNodes.item(i);
+                String nodeName = childNode.getNodeName().toLowerCase();
+                List<Node> list = result.get(nodeName);
+                if (list == null)
+                {
+                    list = new ArrayList<>();
+                    result.put(nodeName, list);
+                }
+                list.add(childNode);
+            }
         }
+        return result;
     }
 
     private Date convertFromW3CDate(String lastModDataStr) throws ParseException
@@ -258,19 +295,7 @@ public class ResourceListParser
         return df1.parse(lastModDataStr);
     }
 
-    private Node extractXdNode(Document doc, XPath xpath, String uri) throws XPathExpressionException
-    {
-        // alternative expression: //s:url/s:loc[normalize-space(.)='" + uri + "']/../x:xd");
-        XPathExpression expr = xpath.compile("//s:url/s:loc[normalize-space(.)='" + uri + "']//following-sibling::x:xd[1]");
-        Node xdNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
-        if (xdNode == null)
-        {
-            throw new XPathExpressionException("The resource list should contain 1 xd element per resource");
-        }
-        return xdNode;
-    }
-
-    private void parseDataSetMetaData(XPath xpath, String permId, Node xdNode, Date lastModificationDate)
+    private void parseDataSetMetaData(String permId, Node xdNode, Date lastModificationDate)
     {
         String code = extractCode(xdNode);
         String sampleIdentifier = extractAttribute(xdNode, "sample", true);
@@ -299,8 +324,8 @@ public class ResourceListParser
 
         IncomingDataSet incomingDataSet = new IncomingDataSet(ds, lastModificationDate);
         data.getDataSetsToProcess().put(permId, incomingDataSet);
-        incomingDataSet.setConnections(parseConnections(xpath, xdNode));
-        ds.setDataSetProperties(parseDataSetProperties(xpath, xdNode));
+        incomingDataSet.setConnections(parseConnections(xdNode));
+        ds.setDataSetProperties(parseDataSetProperties(xdNode));
     }
 
     private String extractAttribute(Node xdNode, String attrName, boolean nullAllowed)
@@ -366,7 +391,7 @@ public class ResourceListParser
                 expCode);
     }
 
-    private void parseProjectMetaData(XPath xpath, String permId, Node xdNode, Date lastModificationDate)
+    private void parseProjectMetaData(String permId, Node xdNode, Date lastModificationDate)
     {
 
         String code = extractCode(xdNode);
@@ -377,8 +402,8 @@ public class ResourceListParser
         newProject.setPermID(permId);
         IncomingProject incomingProject = new IncomingProject(newProject, lastModificationDate);
         data.getProjectsToProcess().put(permId, incomingProject);
-        incomingProject.setConnections(parseConnections(xpath, xdNode));
-        incomingProject.setHasAttachments(hasAttachments(xpath, xdNode));
+        incomingProject.setConnections(parseConnections(xdNode));
+        incomingProject.setHasAttachments(hasAttachments(xdNode));
     }
 
     private ExperimentIdentifier createExperimentIdentifier(String spaceId, String prjCode, String expCode)
@@ -415,7 +440,7 @@ public class ResourceListParser
         return new SpaceIdentifier(translatedSpaceId);
     }
 
-    private void parseMaterialMetaData(XPath xpath, String permId, Node xdNode, Date lastModificationDate)
+    private void parseMaterialMetaData(String permId, Node xdNode, Date lastModificationDate)
     {
         String code = nameTranslator.translate(extractCode(xdNode));
         String type = extractType(xdNode);
@@ -423,10 +448,10 @@ public class ResourceListParser
         MaterialWithLastModificationDate materialWithLastModDate =
                 new MaterialWithLastModificationDate(newMaterial, lastModificationDate);
         data.getMaterialsToProcess().put(code, type, materialWithLastModDate);
-        newMaterial.setProperties(parseProperties(xpath, xdNode));
+        newMaterial.setProperties(parseProperties(xdNode));
     }
 
-    private List<Connection> parseConnections(XPath xpath, Node xdNode)
+    private List<Connection> parseConnections(Node xdNode)
     {
         List<Connection> conns = new ArrayList<Connection>();
         Element docElement = (Element) xdNode;
@@ -445,21 +470,17 @@ public class ResourceListParser
         return conns;
     }
 
-    private boolean hasAttachments(XPath xpath, Node xdNode)
+    private boolean hasAttachments(Node xdNode)
     {
         Element docElement = (Element) xdNode;
         NodeList connsNode = docElement.getElementsByTagName("x:binaryData");
         // if a sample/experiment/project node has binaryData element, it can only be because of attachments
-        if (connsNode.getLength() == 1)
-        {
-            return true;
-        }
-        return false;
+        return connsNode.getLength() == 1;
     }
 
-    private List<NewProperty> parseDataSetProperties(XPath xpath, Node xdNode)
+    private List<NewProperty> parseDataSetProperties(Node xdNode)
     {
-        EntityProperty[] entityProperties = parseProperties(xpath, xdNode);
+        EntityProperty[] entityProperties = parseProperties(xdNode);
         List<NewProperty> dsProperties = new ArrayList<NewProperty>();
         for (EntityProperty entityProperty : entityProperties)
         {
@@ -468,7 +489,7 @@ public class ResourceListParser
         return dsProperties;
     }
 
-    private EntityProperty[] parseProperties(XPath xpath, Node xdNode)
+    private EntityProperty[] parseProperties(Node xdNode)
     {
 
         List<EntityProperty> entityProperties = new ArrayList<EntityProperty>();
@@ -533,7 +554,7 @@ public class ResourceListParser
         return new MaterialIdentifier(code, typeCode).toString();
     }
 
-    private void parseExperimentMetaData(XPath xpath, String permId, Node xdNode, Date lastModificationDate)
+    private void parseExperimentMetaData(String permId, Node xdNode, Date lastModificationDate)
     {
         String code = extractCode(xdNode);
         String type = extractType(xdNode);
@@ -544,12 +565,12 @@ public class ResourceListParser
         newExp.setPermID(permId);
         IncomingExperiment incomingExperiment = new IncomingExperiment(newExp, lastModificationDate);
         data.getExperimentsToProcess().put(permId, incomingExperiment);
-        incomingExperiment.setConnections(parseConnections(xpath, xdNode));
-        incomingExperiment.setHasAttachments(hasAttachments(xpath, xdNode));
-        newExp.setProperties(parseProperties(xpath, xdNode));
+        incomingExperiment.setConnections(parseConnections(xdNode));
+        incomingExperiment.setHasAttachments(hasAttachments(xdNode));
+        newExp.setProperties(parseProperties(xdNode));
     }
 
-    private void parseSampleMetaData(XPath xpath, String permId, Node xdNode, Date lastModificationDate)
+    private void parseSampleMetaData(String permId, Node xdNode, Date lastModificationDate)
     {
         String code = extractCode(xdNode);
         String type = extractType(xdNode);
@@ -585,9 +606,9 @@ public class ResourceListParser
         }
         IncomingSample incomingSample = new IncomingSample(newSample, lastModificationDate);
         data.getSamplesToProcess().put(permId, incomingSample);
-        incomingSample.setHasAttachments(hasAttachments(xpath, xdNode));
-        incomingSample.setConnections(parseConnections(xpath, xdNode));
-        newSample.setProperties(parseProperties(xpath, xdNode));
+        incomingSample.setHasAttachments(hasAttachments(xdNode));
+        incomingSample.setConnections(parseConnections(xdNode));
+        newSample.setProperties(parseProperties(xdNode));
     }
 
     private String extractType(Node xdNode)
diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/util/Monitor.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/util/Monitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..36d8202f2cd02a0eea63938c765fbd9cb339b566
--- /dev/null
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dss/plugins/sync/harvester/synchronizer/util/Monitor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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.ethz.sis.openbis.generic.server.dss.plugins.sync.harvester.synchronizer.util;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.time.StopWatch;
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.filesystem.FileUtilities;
+import ch.systemsx.cisd.common.logging.BufferedAppender;
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.openbis.util.LogRecordingUtils;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+public class Monitor
+{
+    private String name;
+
+    private Logger operationLog;
+
+    private StopWatch stopWatch;
+
+    public Monitor(String name, Logger operationLog)
+    {
+        this.name = name;
+        this.operationLog = operationLog;
+        stopWatch = new StopWatch();
+        stopWatch.start();
+        operationLog.info("-------- monitoring started for '" + name + "'.");
+    }
+
+    public void log()
+    {
+        logInfo(createReport());
+    }
+
+    public void log(String message)
+    {
+        logInfo(createReport() + " " + message);
+    }
+    
+    private void logInfo(String info)
+    {
+        operationLog.info("[" + name + "] " + info);
+    }
+
+    private String createReport()
+    {
+        Runtime runtime = Runtime.getRuntime();
+        String max = FileUtilities.byteCountToDisplaySize(runtime.maxMemory());
+        String free = FileUtilities.byteCountToDisplaySize(runtime.freeMemory());
+        String used = FileUtilities.byteCountToDisplaySize(runtime.maxMemory() - runtime.freeMemory());
+        long time = stopWatch.getTime(TimeUnit.SECONDS);
+        return String.format("[time: %5d sec, used: %9s, free: %9s, max: %9s]", time, used, free, max);
+    }
+}