From 02a37ea77a19c67c79b36c769f1bd5588a02c77d Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Wed, 27 Oct 2010 06:36:11 +0000
Subject: [PATCH] SE-308, SE-311 improved algorithm for gathering raw data
 samples and data sets

SVN: 18453
---
 .../client/api/v1/ProteomicsDataApiTest.java  |  29 ++-
 .../server/ProteomicsDataServiceInternal.java |  70 ++++--
 .../server/api/v1/ProteomicsDataService.java  |  54 ++++-
 .../business/BusinessObjectFactory.java       |  13 +-
 .../server/business/DataSetManager.java       | 167 -------------
 .../server/business/ExperimentLoader.java     |  77 ++++++
 .../business/IBusinessObjectFactory.java      |   2 +
 .../server/business/ISampleLoader.java        |  31 +++
 .../server/business/SampleLoader.java         | 121 +++++++++
 .../shared/api/v1/dto/DataSet.java            | 114 +++++++++
 .../shared/api/v1/dto/Experiment.java         |   2 +-
 .../api/v1/dto/MsInjectionDataInfo.java       |  29 ++-
 .../validator/ParentSampleValidator.java      |  48 ++++
 .../validator/RawDataSampleValidator.java     |  15 +-
 .../shared/dto/MsInjectionSample.java         |  25 +-
 .../java/phosphonetx-applicationContext.xml   |   3 +
 .../web/server/RawDataSampleProviderTest.java |   4 +-
 .../ProteomicsDataServiceInternalTest.java    | 159 ++++++------
 .../server/ProteomicsDataServiceTest.java     |   6 -
 .../business/AbstractLoaderTestCase.java      |  56 +++++
 .../server/business/DataSetManagerTest.java   | 229 ------------------
 .../server/business/ExperimentLoaderTest.java |  79 ++++++
 .../server/business/SampleLoaderTest.java     |  71 ++++++
 .../validator/RawDataSampleValidatorTest.java |   3 +-
 24 files changed, 883 insertions(+), 524 deletions(-)
 delete mode 100644 rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/DataSetManager.java
 create mode 100644 rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ExperimentLoader.java
 create mode 100644 rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ISampleLoader.java
 create mode 100644 rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/SampleLoader.java
 create mode 100644 rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/DataSet.java
 create mode 100644 rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/ParentSampleValidator.java
 create mode 100644 rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/AbstractLoaderTestCase.java
 delete mode 100644 rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/DataSetManagerTest.java
 create mode 100644 rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ExperimentLoaderTest.java
 create mode 100644 rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/SampleLoaderTest.java

diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/api/v1/ProteomicsDataApiTest.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/api/v1/ProteomicsDataApiTest.java
index f42913c27a1..0ed59a89edd 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/api/v1/ProteomicsDataApiTest.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/api/v1/ProteomicsDataApiTest.java
@@ -19,8 +19,10 @@ package ch.systemsx.cisd.openbis.plugin.phosphonetx.client.api.v1;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.DataSet;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.DataStoreServerProcessingPluginInfo;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.Experiment;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.MsInjectionDataInfo;
@@ -55,8 +57,20 @@ public class ProteomicsDataApiTest
             {
                 System.out.println("   " + info.getMsInjectionSampleCode() + " -> "
                         + info.getBiologicalSampleIdentifier() + " -> "
-                        + info.getBiologicalExperimentIdentifier() + ": latest data sets: "
-                        + latestDataSets);
+                        + info.getBiologicalExperimentIdentifier());
+                Experiment experiment = info.getBiologicalExperiment();
+                if (experiment != null)
+                {
+                    System.out.println("   biological experiment: "
+                            + experiment.getCode() + " "
+                            + experiment.getProperties());
+                }
+                System.out.println("   latest data sets: " + info.getLatestDataSetRegistrationDates());
+                Set<DataSet> dataSets = info.getDataSets();
+                for (DataSet dataSet : dataSets)
+                {
+                    print(dataSet, "         ");
+                }
             }
         }
         
@@ -100,4 +114,15 @@ public class ProteomicsDataApiTest
         
         facade.logout();
     }
+    
+    private static void print(DataSet dataSet, String indentation)
+    {
+        System.out.println(indentation + dataSet.getCode() + " " + dataSet.getType() + " "
+                + dataSet.getRegistrationDate() + " " + dataSet.getProperties());
+        Set<DataSet> children = dataSet.getChildren();
+        for (DataSet child : children)
+        {
+            print(child, indentation + "  ");
+        }
+    }
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceInternal.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceInternal.java
index 61fc559f258..588908834ee 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceInternal.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceInternal.java
@@ -21,6 +21,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 import ch.rinn.restrictions.Private;
@@ -30,7 +31,6 @@ import ch.systemsx.cisd.common.spring.IInvocationLoggerContext;
 import ch.systemsx.cisd.openbis.generic.server.AbstractServer;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IExternalDataTable;
-import ch.systemsx.cisd.openbis.generic.server.business.bo.samplelister.ISampleLister;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IEntityTypeDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IExperimentDAO;
@@ -41,8 +41,6 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStoreServiceKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListOrSearchSampleCriteria;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListSampleCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServicePE;
@@ -50,13 +48,14 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.translator.ExperimentTranslator;
-import ch.systemsx.cisd.openbis.generic.shared.translator.SampleTypeTranslator;
-import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business.DataSetManager;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business.ExperimentLoader;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business.IBusinessObjectFactory;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business.ISampleLoader;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.IProteomicsDataServiceInternal;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.authorization.validator.ParentSampleValidator;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.authorization.validator.RawDataSampleValidator;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.MsInjectionSample;
 
@@ -73,26 +72,34 @@ public class ProteomicsDataServiceInternal extends AbstractServer<IProteomicsDat
 
     @Private
     static final String RAW_DATA_SAMPLE_TYPE = "MS_INJECTION";
+    
+    private static final IValidator<Sample> PARENT_SAMPLE_VALIDATOR = new ParentSampleValidator();
 
     private static final IValidator<MsInjectionSample> RAW_DATA_SAMPLE_VALIDATOR =
             new RawDataSampleValidator();
     
     private static final IValidator<Experiment> EXPERIMENT_VALIDATOR = new ExperimentValidator();
 
-    private ICommonBusinessObjectFactory businessObjectFactory;
+    private ICommonBusinessObjectFactory commonBoFactory;
 
     private ISessionManager<Session> sessionManagerFromConstructor;
 
+    private ExperimentLoader experimentLoader;
+
+    private IBusinessObjectFactory boFactory;
+
     public ProteomicsDataServiceInternal()
     {
     }
 
     public ProteomicsDataServiceInternal(ISessionManager<Session> sessionManager, IDAOFactory daoFactory,
-            ICommonBusinessObjectFactory businessObjectFactory)
+            ICommonBusinessObjectFactory businessObjectFactory, IBusinessObjectFactory boFactory)
     {
         super(sessionManager, daoFactory);
         sessionManagerFromConstructor = sessionManager;
-        this.businessObjectFactory = businessObjectFactory;
+        this.commonBoFactory = businessObjectFactory;
+        this.boFactory = boFactory;
+        experimentLoader = new ExperimentLoader(getDAOFactory());
     }
 
     public void replaceAutoWiredSesseionManagerByConstructorSessionManager()
@@ -188,23 +195,38 @@ public class ProteomicsDataServiceInternal extends AbstractServer<IProteomicsDat
 
     private List<MsInjectionSample> loadAllRawDataSamples(Session session)
     {
-        ISampleLister sampleLister = businessObjectFactory.createSampleLister(session);
-        ListSampleCriteria criteria = new ListSampleCriteria();
-        SampleTypePE sampleTypePE =
-                getDAOFactory().getSampleTypeDAO().tryFindSampleTypeByCode(RAW_DATA_SAMPLE_TYPE);
-        criteria.setSampleType(SampleTypeTranslator.translate(sampleTypePE, null));
-        criteria.setIncludeSpace(true);
-        criteria.setSpaceCode(SPACE_CODE);
-        ListOrSearchSampleCriteria criteria2 = new ListOrSearchSampleCriteria(criteria);
-        criteria2.setEnrichDependentSamplesWithProperties(true);
-        List<Sample> samples = sampleLister.list(criteria2);
-        DataSetManager manager = new DataSetManager();
+        List<Sample> samples = loadAccessableSamples(session);
+        List<Sample> parentSamples = new ArrayList<Sample>();
         for (Sample sample : samples)
         {
-            manager.addSample(sample);
+            parentSamples.add(sample.getGeneratedFrom());
+        }
+        experimentLoader.enrichWithExperiments(parentSamples);
+        Map<Sample, List<ExternalData>> dataSetsBySamples =
+                commonBoFactory.createDatasetLister(session).listAllDataSetsFor(samples);
+        List<MsInjectionSample> result = new ArrayList<MsInjectionSample>();
+        for (Entry<Sample, List<ExternalData>> entry : dataSetsBySamples.entrySet())
+        {
+            result.add(new MsInjectionSample(entry.getKey(), entry.getValue()));
+        }
+        return result;
+    }
+
+    protected List<Sample> loadAccessableSamples(Session session)
+    {
+        ISampleLoader sampleLoader = boFactory.createSampleLoader(session);
+        List<Sample> samples =
+                sampleLoader.listSamplesWithParentsByTypeAndSpace(RAW_DATA_SAMPLE_TYPE, SPACE_CODE);
+        PersonPE person = session.tryGetPerson();
+        List<Sample> validSamples = new ArrayList<Sample>();
+        for (Sample sample : samples)
+        {
+            if (PARENT_SAMPLE_VALIDATOR.isValid(person, sample))
+            {
+                validSamples.add(sample);
+            }
         }
-        manager.gatherDataSets(businessObjectFactory.createDatasetLister(session));
-        return manager.getSamples();
+        return validSamples;
     }
 
     private void processDataSets(Session session, String dataSetProcessingKey,
@@ -212,7 +234,7 @@ public class ProteomicsDataServiceInternal extends AbstractServer<IProteomicsDat
     {
         String dataStoreServerCode = findDataStoreServer(dataSetProcessingKey);
         IExternalDataTable externalDataTable =
-                businessObjectFactory.createExternalDataTable(session);
+                commonBoFactory.createExternalDataTable(session);
         externalDataTable.processDatasets(dataSetProcessingKey, dataStoreServerCode, dataSetCodes,
                 parameterBindings);
     }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/api/v1/ProteomicsDataService.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/api/v1/ProteomicsDataService.java
index 4f94af701e8..3f9451803c2 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/api/v1/ProteomicsDataService.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/api/v1/ProteomicsDataService.java
@@ -20,6 +20,7 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -49,6 +50,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO;
 import ch.systemsx.cisd.openbis.generic.shared.util.DataTypeUtils;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.IProteomicsDataServiceInternal;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.IProteomicsDataService;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.DataSet;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.DataStoreServerProcessingPluginInfo;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.MsInjectionDataInfo;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.PropertyKey;
@@ -122,8 +124,17 @@ public class ProteomicsDataService extends AbstractServer<IProteomicsDataService
         if (experiment != null)
         {
             info.setBiologicalExperimentIdentifier(experiment.getIdentifier());
+            info.setBiologicalExperiment(translate(experiment));
         }
         info.setBiologicalSampleProperties(translate(bioSample.getProperties()));
+        List<ExternalData> dataSets = sample.getDataSets();
+        Set<DataSet> transformedDataSets = new HashSet<DataSet>();
+        for (ExternalData dataSet : dataSets)
+        {
+            DataSet transformedDataSet = transform(dataSet);
+            transformedDataSets.add(transformedDataSet);
+        }
+        info.setDataSets(transformedDataSets);
         Map<String, Date> latestDataSetRegistrationDates = new HashMap<String, Date>();
         for (Entry<String, ExternalData> entry : sample.getLatestDataSets().entrySet())
         {
@@ -133,6 +144,25 @@ public class ProteomicsDataService extends AbstractServer<IProteomicsDataService
         return info;
     }
 
+    private DataSet transform(ExternalData dataSet)
+    {
+        DataSet transformedDataSet = new DataSet();
+        transformedDataSet.setId(dataSet.getId());
+        transformedDataSet.setCode(dataSet.getCode());
+        transformedDataSet.setType(dataSet.getDataSetType().getCode());
+        transformedDataSet.setRegistrationDate(dataSet.getRegistrationDate());
+        transformedDataSet.setProperties(translate(dataSet.getProperties()));
+        List<ExternalData> children = dataSet.getChildren();
+        if (children != null && children.isEmpty() == false)
+        {
+            for (ExternalData child : children)
+            {
+                transformedDataSet.addChild(transform(child));
+            }
+        }
+        return transformedDataSet;
+    }
+
     public List<DataStoreServerProcessingPluginInfo> listDataStoreServerProcessingPluginInfos(
             String sessionToken)
     {
@@ -195,13 +225,7 @@ public class ProteomicsDataService extends AbstractServer<IProteomicsDataService
             for (Experiment experiment : experiments)
             {
                 ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.Experiment e =
-                        new ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.Experiment();
-                e.setId(experiment.getId());
-                e.setCode(experiment.getCode());
-                e.setProjectCode(experiment.getProject().getCode());
-                e.setSpaceCode(experiment.getProject().getSpace().getCode());
-                e.setRegistrationDate(experiment.getRegistrationDate());
-                e.setProperties(translate(experiment.getProperties()));
+                        translate(experiment);
                 result.add(e);
             }
             return result;
@@ -211,6 +235,20 @@ public class ProteomicsDataService extends AbstractServer<IProteomicsDataService
         }
     }
 
+    private ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.Experiment translate(
+            Experiment experiment)
+    {
+        ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.Experiment e =
+                new ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto.Experiment();
+        e.setId(experiment.getId());
+        e.setCode(experiment.getCode());
+        e.setProjectCode(experiment.getProject().getCode());
+        e.setSpaceCode(experiment.getProject().getSpace().getCode());
+        e.setRegistrationDate(experiment.getRegistrationDate());
+        e.setProperties(translate(experiment.getProperties()));
+        return e;
+    }
+
     public void processSearchData(String sessionToken, String userID, String dataSetProcessingKey,
             long[] searchExperimentIDs)
     {
@@ -260,7 +298,7 @@ public class ProteomicsDataService extends AbstractServer<IProteomicsDataService
 
     public int getMinorVersion()
     {
-        return 2;
+        return 3;
     }
 
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/BusinessObjectFactory.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/BusinessObjectFactory.java
index 966240e03b1..fab14dfcd6a 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/BusinessObjectFactory.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/BusinessObjectFactory.java
@@ -16,6 +16,7 @@
 
 package ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business;
 
+import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.samplelister.ISampleLister;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
@@ -31,11 +32,14 @@ public class BusinessObjectFactory  extends AbstractPluginBusinessObjectFactory
 {
     private final IDAOFactory daoFactory;
     private final IPhosphoNetXDAOFactory specificDAOFactory;
+    private final ICommonBusinessObjectFactory businessObjectFactory;
 
-    public BusinessObjectFactory(IDAOFactory daoFactory, IPhosphoNetXDAOFactory specificDAOFactory)
+    public BusinessObjectFactory(IDAOFactory daoFactory, IPhosphoNetXDAOFactory specificDAOFactory,
+            ICommonBusinessObjectFactory businessObjectFactory)
     {
         this.daoFactory = daoFactory;
         this.specificDAOFactory = specificDAOFactory;
+        this.businessObjectFactory = businessObjectFactory;
     }
 
     public ISampleLister createSampleLister(Session session)
@@ -88,4 +92,11 @@ public class BusinessObjectFactory  extends AbstractPluginBusinessObjectFactory
         return new SampleProvider(session, this);
     }
 
+    public ISampleLoader createSampleLoader(Session session)
+    {
+        return new SampleLoader(session, daoFactory, businessObjectFactory);
+    }
+    
+    
+
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/DataSetManager.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/DataSetManager.java
deleted file mode 100644
index 01869aa2226..00000000000
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/DataSetManager.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2010 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.openbis.plugin.phosphonetx.server.business;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.openbis.generic.server.business.bo.datasetlister.IDatasetLister;
-import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
-import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.MsInjectionSample;
-
-/**
- * Class managing indirect relation from Data Sets to Samples. 
- *
- * @author Franz-Josef Elmer
- */
-public class DataSetManager
-{
-    private final Map<Long, MsInjectionSample> samples = new LinkedHashMap<Long, MsInjectionSample>();
-    
-    public void addSample(Sample plainSample)
-    {
-        MsInjectionSample sample = new MsInjectionSample(plainSample);
-        samples.put(plainSample.getId(), sample);
-        Experiment experiment = plainSample.getExperiment();
-        if (experiment == null)
-        {
-            throw new UserFailureException("Sample '" + plainSample.getIdentifier() + "' of type '"
-                    + plainSample.getSampleType() + "' does not belong to an experiment.");
-        }
-    }
-
-    public List<MsInjectionSample> getSamples()
-    {
-        return new ArrayList<MsInjectionSample>(samples.values());
-    }
-    
-    public void gatherDataSets(IDatasetLister datasetLister)
-    {
-        List<ExternalData> dataSets = getDataSets(datasetLister);
-        Map<Long, Long> dataSetSampleMap = mapDataSetsOnSamples(dataSets, datasetLister);
-        for (ExternalData dataSet : dataSets)
-        {
-            Long sampleID = dataSetSampleMap.get(dataSet.getId());
-            if (sampleID != null)
-            {
-                MsInjectionSample sample = samples.get(sampleID);
-                if (sample != null)
-                {
-                    sample.addLatestDataSet(dataSet);
-                }
-            }
-        }
-    }
-
-    private List<ExternalData> getDataSets(IDatasetLister datasetLister)
-    {
-        Set<TechId> experimentIds = new LinkedHashSet<TechId>();
-        for (MsInjectionSample sample : samples.values())
-        {
-            experimentIds.add(new TechId(sample.getSample().getExperiment().getId()));
-        }
-        List<ExternalData> result = new ArrayList<ExternalData>();
-        List<ExternalData> dataSets = datasetLister.listByExperimentTechIds(experimentIds);
-        for (ExternalData dataSet : dataSets)
-        {
-            result.add(dataSet);
-        }
-        return result;
-    }
-    
-    private Map<Long, Long> mapDataSetsOnSamples(List<ExternalData> dataSets,
-            IDatasetLister datasetLister)
-    {
-        List<ExternalData> descendentDataSets = new ArrayList<ExternalData>();
-        Map<Long, Long> dataSetSampleMap = new HashMap<Long, Long>();
-        Set<Long> ids = new LinkedHashSet<Long>();
-        for (ExternalData dataSet : dataSets)
-        {
-            Sample sample = dataSet.getSample();
-            if (sample != null)
-            {
-                dataSetSampleMap.put(dataSet.getId(), sample.getId());
-            } else
-            {
-                descendentDataSets.add(dataSet);
-                ids.add(dataSet.getId());
-            }
-        }
-        Map<Long, Set<Long>> parentIds = datasetLister.listParentIds(ids);
-        while (descendentDataSets.isEmpty() == false)
-        {
-            boolean nothingRemoved = true;
-            for (Iterator<ExternalData> iterator = descendentDataSets.iterator(); iterator
-                    .hasNext();)
-            {
-                ExternalData dataSet = iterator.next();
-                Set<Long> parent = parentIds.get(dataSet.getId());
-                if (parent == null)
-                {
-                    iterator.remove();
-                    nothingRemoved = false;
-                    continue;
-                }
-                if (parent.size() != 1)
-                {
-                    throw new UserFailureException("Data set '" + dataSet.getCode() + "' has "
-                            + parent.size() + " instead of one parent data set.");
-
-                }
-                Long sampleID = dataSetSampleMap.get(parent.iterator().next());
-                if (sampleID != null)
-                {
-                    dataSetSampleMap.put(dataSet.getId(), sampleID);
-                    iterator.remove();
-                    nothingRemoved = false;
-                }
-            }
-            assertSomeDataSetsAreRemoved(nothingRemoved, descendentDataSets);
-        }
-        return dataSetSampleMap;
-    }
-
-    private void assertSomeDataSetsAreRemoved(boolean nothingRemoved,
-            List<ExternalData> descendentDataSets)
-    {
-        if (nothingRemoved)
-        {
-            StringBuilder builder = new StringBuilder();
-            for (ExternalData externalData : descendentDataSets)
-            {
-                if (builder.length() > 0)
-                {
-                    builder.append(", ");
-                }
-                builder.append(externalData.getCode());
-            }
-            throw new UserFailureException("Following data sets have wrong parents: "
-                    + builder.toString().trim());
-        }
-    }
-
-}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ExperimentLoader.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ExperimentLoader.java
new file mode 100644
index 00000000000..8243021f1a0
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ExperimentLoader.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2010 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.openbis.plugin.phosphonetx.server.business;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
+import ch.systemsx.cisd.openbis.generic.shared.translator.ExperimentTranslator;
+import ch.systemsx.cisd.openbis.generic.shared.translator.ExperimentTranslator.LoadableFields;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class ExperimentLoader
+{
+    private final IDAOFactory daoFactory;
+
+    public ExperimentLoader(IDAOFactory daoFactory)
+    {
+        this.daoFactory = daoFactory;
+        
+    }
+    
+    public void enrichWithExperiments(Collection<Sample> samples)
+    {
+        Map<Long, List<Sample>> samplesByID = new LinkedHashMap<Long, List<Sample>>();
+        for (Sample sample : samples)
+        {
+            Experiment experiment = sample.getExperiment();
+            if (experiment != null)
+            {
+                Long id = experiment.getId();
+                List<Sample> list = samplesByID.get(id);
+                if (list == null)
+                {
+                    list = new ArrayList<Sample>();
+                    samplesByID.put(id, list);
+                }
+                list.add(sample);
+            }
+        }
+        List<ExperimentPE> experiments =
+                daoFactory.getExperimentDAO().listExperimentsWithProperties(samplesByID.keySet());
+        for (ExperimentPE experiment : experiments)
+        {
+            Experiment e = ExperimentTranslator.translate(experiment, "", LoadableFields.PROPERTIES);
+            List<Sample> list = samplesByID.get(experiment.getId());
+            for (Sample sample : list)
+            {
+                sample.setExperiment(e);
+            }
+        }
+    }
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IBusinessObjectFactory.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IBusinessObjectFactory.java
index 25acc3f21d6..8d649e2fd66 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IBusinessObjectFactory.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/IBusinessObjectFactory.java
@@ -46,4 +46,6 @@ public interface IBusinessObjectFactory
     public ISampleIDProvider createSampleIDProvider(Session session);
     
     public ISampleProvider createSampleProvider(Session session);
+    
+    public ISampleLoader createSampleLoader(Session session);
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ISampleLoader.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ISampleLoader.java
new file mode 100644
index 00000000000..bf65aba6af4
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ISampleLoader.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010 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.openbis.plugin.phosphonetx.server.business;
+
+import java.util.List;
+
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
+
+/**
+ * Loader for sample having a parent.
+ *
+ * @author Franz-Josef Elmer
+ */
+public interface ISampleLoader
+{
+    public List<Sample> listSamplesWithParentsByTypeAndSpace(String sampleTypeCode, String spaceCode);
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/SampleLoader.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/SampleLoader.java
new file mode 100644
index 00000000000..1ce33e6907c
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/SampleLoader.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2010 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.openbis.plugin.phosphonetx.server.business;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import ch.systemsx.cisd.common.collections.IValidator;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.samplelister.ISampleLister;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListOrSearchSampleCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
+import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SampleRelationShipSkeleton;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SampleSkeleton;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class SampleLoader implements ISampleLoader
+{
+    private final Session session;
+    private final IDAOFactory daoFactory;
+    private final ICommonBusinessObjectFactory businessObjectFactory;
+
+    public SampleLoader(Session session, IDAOFactory daoFactory,
+            ICommonBusinessObjectFactory businessObjectFactory)
+    {
+        this.session = session;
+        this.daoFactory = daoFactory;
+        this.businessObjectFactory = businessObjectFactory;
+        
+    }
+    
+    public List<Sample> listSamplesWithParentsByTypeAndSpace(String sampleTypeCode, String spaceCode)
+    {
+        ISampleLister sampleLister = businessObjectFactory.createSampleLister(session);
+        Set<Long> filteredSampleIDs = getSampleIDs(sampleLister, sampleTypeCode, spaceCode);
+        ListOrSearchSampleCriteria criteria = new ListOrSearchSampleCriteria(filteredSampleIDs);
+        criteria.setEnrichDependentSamplesWithProperties(true);
+        List<Sample> samples = sampleLister.list(criteria);
+        ArrayList<Sample> samplesWithParent = new ArrayList<Sample>();
+        for (Sample sample : samples)
+        {
+            if (sample.getParents().size() == 1)
+            {
+                samplesWithParent.add(sample);
+            }
+        }
+        return samplesWithParent;
+    }
+
+    private Set<Long> getSampleIDs(ISampleLister sampleLister, String sampleTypeCode,
+            String spaceCode)
+    {
+        SampleTypePE sampleTypePE =
+                daoFactory.getSampleTypeDAO().tryFindSampleTypeByCode(sampleTypeCode);
+        final Long sampleTypeID = sampleTypePE.getId();
+        GroupPE space =
+                daoFactory.getGroupDAO().tryFindGroupByCodeAndDatabaseInstance(spaceCode,
+                        daoFactory.getHomeDatabaseInstance());
+        final Long spaceID = space.getId();
+        List<SampleSkeleton> sampleSkeletons =
+                sampleLister.listSampleBy(new IValidator<SampleSkeleton>()
+                    {
+                        public boolean isValid(SampleSkeleton sampleSkeleton)
+                        {
+                            return spaceID.equals(sampleSkeleton.getSpaceID())
+                                    && sampleTypeID.equals(sampleSkeleton.getTypeID());
+                        }
+                    });
+        final Set<Long> sampleIDs = new HashSet<Long>();
+        for (SampleSkeleton sampleSkeleton : sampleSkeletons)
+        {
+            sampleIDs.add(sampleSkeleton.getId());
+        }
+        final long relationshipTypeID =
+                sampleLister
+                        .getRelationshipTypeID(BasicConstant.PARENT_CHILD_INTERNAL_RELATIONSHIP);
+        List<SampleRelationShipSkeleton> relationshipSkeletons =
+                sampleLister.listSampleRelationShipsBy(new IValidator<SampleRelationShipSkeleton>()
+                    {
+                        public boolean isValid(SampleRelationShipSkeleton skeleton)
+                        {
+                            return skeleton.getRelationShipTypeID() == relationshipTypeID
+                                    && sampleIDs.contains(skeleton.getChildSampleID());
+                        }
+                    });
+        Set<Long> filteredSampleIDs = new HashSet<Long>();
+        Set<Long> parentIDs = new HashSet<Long>();
+        for (SampleRelationShipSkeleton sampleRelationShipSkeleton : relationshipSkeletons)
+        {
+            filteredSampleIDs.add(sampleRelationShipSkeleton.getChildSampleID());
+            parentIDs.add(sampleRelationShipSkeleton.getParentSampleID());
+        }
+        return filteredSampleIDs;
+    }
+    
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/DataSet.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/DataSet.java
new file mode 100644
index 00000000000..c913ba615bd
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/DataSet.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2010 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.openbis.plugin.phosphonetx.shared.api.v1.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Date Set bean with properties, children and parents.
+ *
+ * @author Franz-Josef Elmer
+ */
+public class DataSet implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+    
+    private long id;
+    
+    private String code;
+    
+    private String type;
+    
+    private Date registrationDate;
+    
+    private Map<PropertyKey, Serializable> properties;
+    
+    private Set<DataSet> parents = new HashSet<DataSet>();
+    
+    private Set<DataSet> children = new HashSet<DataSet>();
+
+    public final long getId()
+    {
+        return id;
+    }
+
+    public final void setId(long id)
+    {
+        this.id = id;
+    }
+
+    public final String getCode()
+    {
+        return code;
+    }
+
+    public final void setCode(String code)
+    {
+        this.code = code;
+    }
+
+    public final String getType()
+    {
+        return type;
+    }
+
+    public final void setType(String type)
+    {
+        this.type = type;
+    }
+
+    public final Date getRegistrationDate()
+    {
+        return registrationDate;
+    }
+
+    public final void setRegistrationDate(Date registrationDate)
+    {
+        this.registrationDate = registrationDate;
+    }
+
+    public final Map<PropertyKey, Serializable> getProperties()
+    {
+        return properties;
+    }
+
+    public final void setProperties(Map<PropertyKey, Serializable> properties)
+    {
+        this.properties = properties;
+    }
+
+    public final Set<DataSet> getParents()
+    {
+        return parents;
+    }
+
+    public final Set<DataSet> getChildren()
+    {
+        return children;
+    }
+
+    public void addChild(DataSet child)
+    {
+        getChildren().add(child);
+        child.getParents().add(this);
+    }
+
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/Experiment.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/Experiment.java
index a0d5e366092..be5e7ede90a 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/Experiment.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/Experiment.java
@@ -21,7 +21,7 @@ import java.util.Date;
 import java.util.Map;
 
 /**
- * 
+ * Experiment bean with properties.
  *
  * @author Franz-Josef Elmer
  */
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/MsInjectionDataInfo.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/MsInjectionDataInfo.java
index 8bf75e5b448..12fb03700b8 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/MsInjectionDataInfo.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/api/v1/dto/MsInjectionDataInfo.java
@@ -19,14 +19,17 @@ package ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.api.v1.dto;
 import java.io.Serializable;
 import java.util.Date;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Informations about an MS_INJECTION sample and its related biological sample. There are attributes
  * for 
  * <ul><li>MS_INJECTION sample: ID, properties, code and registration date
  * <li>Biological sample: identifier and properties
- * <li>Identifier of biological experiment (i.e. experiment of biological sample) if present
+ * <li>Biological experiment (i.e. experiment of biological sample) if present: identifier and properties
  * <li>Registration dates of the most recently registered data sets for each data set type 
+ * <li>All data sets including the derived ones of the MS_INJECTION sample with their type, 
+ * registration date, and properties.
  * </ul>
  *
  * @author Franz-Josef Elmer
@@ -49,8 +52,12 @@ public class MsInjectionDataInfo implements Serializable
     
     private String biologicalExperimentIdentifier;
     
+    private Experiment biologicalExperiment;
+    
     private Map<PropertyKey, Serializable> biologicalSampleProperties;
     
+    private Set<DataSet> dataSets;
+    
     private Map<String, Date> latestDataSetRegistrationDates;
 
     public long getMsInjectionSampleID()
@@ -124,6 +131,16 @@ public class MsInjectionDataInfo implements Serializable
         this.biologicalExperimentIdentifier = biologicalExperimentIdentifier;
     }
 
+    public final Experiment getBiologicalExperiment()
+    {
+        return biologicalExperiment;
+    }
+
+    public final void setBiologicalExperiment(Experiment biologicalExperiment)
+    {
+        this.biologicalExperiment = biologicalExperiment;
+    }
+
     public Map<PropertyKey, Serializable> getBiologicalSampleProperties()
     {
         return biologicalSampleProperties;
@@ -134,6 +151,16 @@ public class MsInjectionDataInfo implements Serializable
         this.biologicalSampleProperties = biologicalSampleProperties;
     }
 
+    public final Set<DataSet> getDataSets()
+    {
+        return dataSets;
+    }
+
+    public final void setDataSets(Set<DataSet> dataSets)
+    {
+        this.dataSets = dataSets;
+    }
+
     public Map<String, Date> getLatestDataSetRegistrationDates()
     {
         return latestDataSetRegistrationDates;
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/ParentSampleValidator.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/ParentSampleValidator.java
new file mode 100644
index 00000000000..7a1e1e77f1a
--- /dev/null
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/ParentSampleValidator.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2010 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.openbis.plugin.phosphonetx.shared.authorization.validator;
+
+import ch.systemsx.cisd.openbis.generic.shared.authorization.validator.IValidator;
+import ch.systemsx.cisd.openbis.generic.shared.authorization.validator.SpaceValidator;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Space;
+import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class ParentSampleValidator implements IValidator<Sample>
+{
+    private IValidator<Space> validator = new SpaceValidator();
+
+    public boolean isValid(PersonPE person, Sample sample)
+    {
+        Sample parent = sample.getGeneratedFrom();
+        if (parent != null)
+        {
+            Space space = parent.getSpace();
+            if (space == null || validator.isValid(person, space))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/RawDataSampleValidator.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/RawDataSampleValidator.java
index 5e51d404422..320c992f5aa 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/RawDataSampleValidator.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/RawDataSampleValidator.java
@@ -1,8 +1,6 @@
 package ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.authorization.validator;
 
-import ch.systemsx.cisd.openbis.generic.shared.authorization.validator.SpaceValidator;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.validator.IValidator;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Space;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.MsInjectionSample;
@@ -14,19 +12,10 @@ import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.MsInjectionSample;
  */
 public final class RawDataSampleValidator implements IValidator<MsInjectionSample>
 {
-    private IValidator<Space> validator = new SpaceValidator();
+    private IValidator<Sample> validator = new ParentSampleValidator();
 
     public boolean isValid(PersonPE person, MsInjectionSample sample)
     {
-        Sample parent = sample.getSample().getGeneratedFrom();
-        if (parent != null)
-        {
-            Space space = parent.getSpace();
-            if (space == null || validator.isValid(person, space))
-            {
-                return true;
-            }
-        }
-        return false;
+        return validator.isValid(person, sample.getSample());
     }
 }
\ No newline at end of file
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/MsInjectionSample.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/MsInjectionSample.java
index 45ef8105d4a..b13d6d86198 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/MsInjectionSample.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/dto/MsInjectionSample.java
@@ -18,6 +18,7 @@ package ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto;
 
 import java.util.Date;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
@@ -32,10 +33,25 @@ public class MsInjectionSample
 {
     private final Sample sample;
     private final Map<String, ExternalData> latestDataSets = new LinkedHashMap<String, ExternalData>();
+    private final List<ExternalData> dataSets;
 
-    public MsInjectionSample(Sample sample)
+    public MsInjectionSample(Sample sample, List<ExternalData> dataSets)
     {
         this.sample = sample;
+        this.dataSets = dataSets;
+        add(dataSets);
+    }
+
+    private void add(List<ExternalData> datasets)
+    {
+        if (datasets != null)
+        {
+            for (ExternalData dataSet : datasets)
+            {
+                addLatestDataSet(dataSet);
+                add(dataSet.getChildren());
+            }
+        }
     }
 
     public Sample getSample()
@@ -43,12 +59,17 @@ public class MsInjectionSample
         return sample;
     }
 
+    public final List<ExternalData> getDataSets()
+    {
+        return dataSets;
+    }
+
     public Map<String, ExternalData> getLatestDataSets()
     {
         return latestDataSets;
     }
 
-    public void addLatestDataSet(ExternalData dataSet)
+    private void addLatestDataSet(ExternalData dataSet)
     {
         String dataSetTypeCode = dataSet.getDataSetType().getCode();
         Date registrationDate = dataSet.getRegistrationDate();
diff --git a/rtd_phosphonetx/source/java/phosphonetx-applicationContext.xml b/rtd_phosphonetx/source/java/phosphonetx-applicationContext.xml
index 5bec6ae66f6..85eaadd8a99 100644
--- a/rtd_phosphonetx/source/java/phosphonetx-applicationContext.xml
+++ b/rtd_phosphonetx/source/java/phosphonetx-applicationContext.xml
@@ -38,6 +38,7 @@
           class="ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business.BusinessObjectFactory">
         <constructor-arg ref="dao-factory"/>
         <constructor-arg ref="phosphonetx-dao-factory"/>
+        <constructor-arg ref="common-business-object-factory" />
     </bean>      
     
     <!-- 
@@ -66,6 +67,7 @@
         </constructor-arg>
         <constructor-arg ref="dao-factory"/>
         <constructor-arg ref="common-business-object-factory" />
+        <constructor-arg ref="phosphonetx-bo-factory" />
     </bean>
     
     <bean id="proteomics-data-service-web" 
@@ -73,6 +75,7 @@
         <constructor-arg ref="session-manager" />
         <constructor-arg ref="dao-factory"/>
         <constructor-arg ref="common-business-object-factory" />
+        <constructor-arg ref="phosphonetx-bo-factory" />
     </bean>
 
     
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/RawDataSampleProviderTest.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/RawDataSampleProviderTest.java
index 61c92451561..bc741c6a138 100644
--- a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/RawDataSampleProviderTest.java
+++ b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/RawDataSampleProviderTest.java
@@ -21,6 +21,7 @@ import static ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.basic.dto.RawDa
 import static ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.basic.dto.RawDataSampleGridIDs.REGISTRATION_DATE;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 
@@ -33,6 +34,7 @@ import ch.systemsx.cisd.openbis.generic.shared.AbstractServerTestCase;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.GenericValueEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyType;
@@ -220,7 +222,7 @@ public class RawDataSampleProviderTest extends AbstractServerTestCase
                     List<MsInjectionSample> list = new ArrayList<MsInjectionSample>();
                     for (Sample sample : samples)
                     {
-                        list.add(new MsInjectionSample(sample));
+                        list.add(new MsInjectionSample(sample, Arrays.<ExternalData>asList()));
                     }
                     will(returnValue(list));
                 }
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceInternalTest.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceInternalTest.java
index a6fc63ba091..56c4dae0fc6 100644
--- a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceInternalTest.java
+++ b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceInternalTest.java
@@ -31,10 +31,7 @@ import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
 import org.jmock.Expectations;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -47,13 +44,14 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStoreServiceKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseInstance;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListOrSearchSampleCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Space;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServicePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityPropertyPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentTypePE;
@@ -62,9 +60,10 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.RoleAssignmentPE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business.IBusinessObjectFactory;
+import ch.systemsx.cisd.openbis.plugin.phosphonetx.server.business.ISampleLoader;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.IProteomicsDataServiceInternal;
 import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.MsInjectionSample;
 
@@ -80,20 +79,37 @@ public class ProteomicsDataServiceInternalTest extends AbstractServerTestCase
 
     private IProteomicsDataServiceInternal service;
 
-    private ICommonBusinessObjectFactory boFactory;
+    private ICommonBusinessObjectFactory commonBoFactory;
 
     private ExperimentTypePE experimentType;
 
+    private IBusinessObjectFactory boFactory;
+
+    private ISampleLoader sampleLoader;
+
     @Override
     @BeforeMethod
     public final void setUp()
     {
         super.setUp();
-        boFactory = context.mock(ICommonBusinessObjectFactory.class);
-        service = new ProteomicsDataServiceInternal(sessionManager, daoFactory, boFactory);
+        commonBoFactory = context.mock(ICommonBusinessObjectFactory.class);
+        boFactory = context.mock(IBusinessObjectFactory.class);
+        sampleLoader = context.mock(ISampleLoader.class);
+        service = new ProteomicsDataServiceInternal(sessionManager, daoFactory, commonBoFactory, boFactory);
         experimentType = new ExperimentTypePE();
         experimentType.setCode(SEARCH_EXPERIMENT_TYPE);
         experimentType.setDatabaseInstance(CommonTestUtils.createHomeDatabaseInstance());
+        PersonPE person = new PersonPE();
+        RoleAssignmentPE roleAssignment = new RoleAssignmentPE();
+        GroupPE group = new GroupPE();
+        group.setCode("Space-0");
+        DatabaseInstancePE databaseInstance = new DatabaseInstancePE();
+        databaseInstance.setCode("db");
+        databaseInstance.setUuid("UUID-db");
+        group.setDatabaseInstance(databaseInstance);
+        roleAssignment.setGroup(group);
+        person.setRoleAssignments(Collections.singleton(roleAssignment));
+        SESSION.setPerson(person);
     }
 
     @Test
@@ -117,8 +133,8 @@ public class ProteomicsDataServiceInternalTest extends AbstractServerTestCase
         final long[] ids = new long[]
             { 2 };
         HashMap<String, String> parameterBindings = new HashMap<String, String>();
-        parameterBindings.put("ds-2", "s-2");
-        prepareProcessDataSets(SESSION, parameterBindings, "ds-2");
+        parameterBindings.put("ds-21-child", "s-2");
+        prepareProcessDataSets(SESSION, parameterBindings, "ds-21-child");
 
         service.processRawData(SESSION_TOKEN, COPY_PROCESSING_KEY, ids, "dt-0");
 
@@ -274,7 +290,7 @@ public class ProteomicsDataServiceInternalTest extends AbstractServerTestCase
                     DataStorePE s2 = store("s2", service(COPY_PROCESSING_KEY, PROCESSING));
                     will(returnValue(Arrays.asList(s1, s2)));
 
-                    one(boFactory).createExternalDataTable(session);
+                    one(commonBoFactory).createExternalDataTable(session);
                     will(returnValue(externalDataTable));
 
                     one(externalDataTable).processDatasets(COPY_PROCESSING_KEY, "s2",
@@ -287,8 +303,8 @@ public class ProteomicsDataServiceInternalTest extends AbstractServerTestCase
     private void prepareListRawDataSamples(final Long... sampleIDs)
     {
         final List<Sample> samples = new ArrayList<Sample>();
-        final List<ExternalData> dataSets = new ArrayList<ExternalData>();
-        final LinkedHashSet<TechId> experimentIds = new LinkedHashSet<TechId>();
+        final LinkedHashSet<Long> experimentIds = new LinkedHashSet<Long>();
+        final List<ExperimentPE> bioExperiments = new ArrayList<ExperimentPE>();
         for (Long id : sampleIDs)
         {
             Sample sample = new Sample();
@@ -300,67 +316,74 @@ public class ProteomicsDataServiceInternalTest extends AbstractServerTestCase
             sample.setExperiment(experiment);
             Sample parent = new Sample();
             parent.setId(id * 100);
+            Space space = new Space();
+            space.setCode("Space-" + id % 2);
+            DatabaseInstance instance = new DatabaseInstance();
+            instance.setCode("db");
+            instance.setUuid("UUID-db");
+            space.setInstance(instance);
+            parent.setSpace(space);
+            Experiment parentExperiment = new Experiment();
+            parentExperiment.setId(id * 20);
+            parent.setExperiment(parentExperiment);
             sample.setGeneratedFrom(parent);
             samples.add(sample);
-            ExternalData dataSet = new ExternalData();
-            dataSet.setId(id * 1000);
-            dataSet.setCode("ds-" + id);
-            dataSet.setDataSetType(new DataSetType("dt-" + id % 2));
-            dataSet.setSample(sample);
-            dataSets.add(dataSet);
-            experimentIds.add(new TechId(id * 10));
+            if (id % 2 == 0)
+            {
+                ExperimentPE bioExperiment = createExperiment("e-type", "exp-" + id, space.getCode());
+                bioExperiment.setId(parentExperiment.getId());
+                bioExperiments.add(bioExperiment);
+                experimentIds.add(bioExperiment.getId());
+            }
         }
         context.checking(new Expectations()
             {
                 {
-                    one(boFactory).createSampleLister(SESSION);
-                    will(returnValue(sampleLister));
-
-                    one(sampleTypeDAO).tryFindSampleTypeByCode(RAW_DATA_SAMPLE_TYPE);
-                    final SampleTypePE sampleType = new SampleTypePE();
-                    sampleType.setCode(RAW_DATA_SAMPLE_TYPE);
-                    sampleType.setId(20100104l);
-                    sampleType.setListable(Boolean.TRUE);
-                    sampleType.setAutoGeneratedCode(Boolean.FALSE);
-                    sampleType.setSubcodeUnique(Boolean.FALSE);
-                    sampleType.setGeneratedFromHierarchyDepth(0);
-                    sampleType.setContainerHierarchyDepth(0);
-                    sampleType.setSubcodeUnique(false);
-                    will(returnValue(sampleType));
-
-                    one(sampleLister).list(with(new BaseMatcher<ListOrSearchSampleCriteria>()
-                        {
-                            public boolean matches(Object item)
-                            {
-                                if (item instanceof ListOrSearchSampleCriteria)
-                                {
-                                    ListOrSearchSampleCriteria criteria =
-                                            (ListOrSearchSampleCriteria) item;
-                                    assertEquals(SPACE_CODE, criteria.getSpaceCode());
-                                    assertEquals(true, criteria.isIncludeSpace());
-                                    SampleType type = criteria.getSampleType();
-                                    assertEquals(RAW_DATA_SAMPLE_TYPE, type.getCode());
-                                    assertEquals(sampleType.getId(), type.getId());
-                                    return true;
-                                }
-                                return false;
-                            }
-
-                            public void describeTo(Description description)
-                            {
-                                description.appendValue(sampleType);
-                            }
-                        }));
+                    one(boFactory).createSampleLoader(SESSION);
+                    will(returnValue(sampleLoader));
+                    
+                    one(sampleLoader).listSamplesWithParentsByTypeAndSpace(RAW_DATA_SAMPLE_TYPE, SPACE_CODE);
                     will(returnValue(samples));
-
-                    one(boFactory).createDatasetLister(SESSION);
+                    
+                    one(experimentDAO).listExperimentsWithProperties(experimentIds);
+                    will(returnValue(bioExperiments));
+                    
+                    List<Sample> filteredSamples = new ArrayList<Sample>();
+                    Map<Sample, List<ExternalData>> dataSetsBySamples = new HashMap<Sample, List<ExternalData>>();
+                    for (Sample sample : samples)
+                    {
+                        if ("Space-0".equals(sample.getGeneratedFrom().getSpace().getCode()))
+                        {
+                            Long id = sample.getId();
+                            ExternalData ds1 = new ExternalData();
+                            ds1.setId(id * 1000);
+                            ds1.setCode("ds-" + id);
+                            ds1.setRegistrationDate(new Date(ds1.getId()));
+                            ds1.setDataSetType(new DataSetType("dt-" + id % 2));
+                            ds1.setSample(sample);
+                            ExternalData ds2 = new ExternalData();
+                            ds2.setId((id + 1) * 1001);
+                            ds2.setCode("ds-" + id + 1);
+                            ds2.setRegistrationDate(new Date(ds2.getId()));
+                            ds2.setDataSetType(new DataSetType("dt-" + id % 2));
+                            ds2.setSample(sample);
+                            ExternalData ds2Child = new ExternalData();
+                            ds2Child.setId(ds2.getId() + 1);
+                            ds2Child.setCode(ds2.getCode() + "-child");
+                            ds2Child.setRegistrationDate(new Date(ds2Child.getId()));
+                            ds2Child.setDataSetType(new DataSetType("dt-" + id % 2));
+                            ds2.setChildren(Arrays.asList(ds2Child));
+                            dataSetsBySamples.put(sample, Arrays.asList(ds1, ds2));
+                            filteredSamples.add(sample);
+                        }
+                    }
+                    
+                    one(commonBoFactory).createDatasetLister(SESSION);
                     will(returnValue(datasetLister));
-
-                    one(datasetLister).listByExperimentTechIds(experimentIds);
-                    will(returnValue(dataSets));
-
-                    one(datasetLister).listParentIds(Collections.<Long> emptySet());
-                    will(returnValue(Collections.<Long, Set<Long>> emptyMap()));
+                        
+                    one(datasetLister).listAllDataSetsFor(filteredSamples);
+                    will(returnValue(dataSetsBySamples));
+                    
                 }
             });
     }
@@ -390,7 +413,7 @@ public class ProteomicsDataServiceInternalTest extends AbstractServerTestCase
         }
         return experiment;
     }
-
+    
     private DataStorePE store(String code, DataStoreServicePE... services)
     {
         DataStorePE store = new DataStorePE();
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceTest.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceTest.java
index df92f957408..ce66ea14cc9 100644
--- a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceTest.java
+++ b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/ProteomicsDataServiceTest.java
@@ -157,12 +157,6 @@ public class ProteomicsDataServiceTest extends AbstractServerTestCase
             {
                 {
                     one(internalService).listRawDataSamples(session2.getSessionToken());
-                    MsInjectionSample msInjectionSample = new MsInjectionSample(sample);
-                    msInjectionSample.addLatestDataSet(createDataSet(RAW_DATA, 10));
-                    msInjectionSample.addLatestDataSet(createDataSet(MZXML_DATA, 20));
-                    msInjectionSample.addLatestDataSet(createDataSet(MZXML_DATA, 15));
-                    msInjectionSample.addLatestDataSet(createDataSet(RAW_DATA, 30));
-                    will(returnValue(Arrays.asList(msInjectionSample)));
                 }
             });
 
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/AbstractLoaderTestCase.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/AbstractLoaderTestCase.java
new file mode 100644
index 00000000000..5c10b7d8d75
--- /dev/null
+++ b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/AbstractLoaderTestCase.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010 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.openbis.plugin.phosphonetx.server.business;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.transaction.TransactionConfiguration;
+
+import ch.systemsx.cisd.authentication.Principal;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.AbstractDAOWithoutContextTest;
+import ch.systemsx.cisd.openbis.generic.shared.CommonTestUtils;
+import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+@ContextConfiguration(locations = "classpath:phosphonetx-applicationContext.xml")
+//In 'commonContext.xml', our transaction manager is called 'transaction-manager' (by default
+//Spring looks for 'transactionManager').
+@TransactionConfiguration(transactionManager = "transaction-manager")
+public abstract class AbstractLoaderTestCase extends AbstractDAOWithoutContextTest
+{
+    private static final Principal PRINCIPAL = new Principal(CommonTestUtils.USER_ID, "john",
+            "doe", "j@d");
+
+    private static final String SESSION_TOKEN = "session-token";
+
+    protected static final Session SESSION = new Session(CommonTestUtils.USER_ID, SESSION_TOKEN,
+            PRINCIPAL, "remote-host", 1);
+    
+    protected ICommonBusinessObjectFactory boFactory;
+
+    @Autowired
+    public final void setBoFactory(final ICommonBusinessObjectFactory boFactory)
+    {
+        this.boFactory = boFactory;
+    }
+}
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/DataSetManagerTest.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/DataSetManagerTest.java
deleted file mode 100644
index 146f35188bd..00000000000
--- a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/DataSetManagerTest.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright 2010 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.openbis.plugin.phosphonetx.server.business;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Map.Entry;
-
-import org.jmock.Expectations;
-import org.jmock.Mockery;
-import org.testng.AssertJUnit;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.openbis.generic.server.business.bo.datasetlister.IDatasetLister;
-import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
-import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.dto.MsInjectionSample;
-
-/**
- * 
- *
- * @author Franz-Josef Elmer
- */
-public class DataSetManagerTest extends AssertJUnit
-{
-    private static final long EXP1_ID = 11;
-    private static final long SAMPLE1A_ID = 111;
-    private static final long SAMPLE1B_ID = 112;
-    private static final long EXP2_ID = 22;
-    private static final long SAMPLE2A_ID = 221;
-    private static final Sample SAMPLE1A = sample(EXP1_ID, SAMPLE1A_ID);
-    private static final Sample SAMPLE1B = sample(EXP1_ID, SAMPLE1B_ID);
-    private static final Sample SAMPLE2A = sample(EXP2_ID, SAMPLE2A_ID);
-    
-    private static Sample sample(long experimentID, long sampleID)
-    {
-        Sample sample = new Sample();
-        sample.setId(sampleID);
-        Experiment experiment = new Experiment();
-        experiment.setId(experimentID);
-        sample.setExperiment(experiment);
-        return sample;
-    }
-    
-    private Mockery context;
-    private IDatasetLister dataSetLister;
-    private DataSetManager manager;
-    
-    @BeforeMethod
-    public void setUp()
-    {
-        context = new Mockery();
-        dataSetLister = context.mock(IDatasetLister.class);
-        manager = new DataSetManager();
-        manager.addSample(SAMPLE1A);
-        manager.addSample(SAMPLE1B);
-        manager.addSample(SAMPLE2A);
-    }
-    
-    @AfterMethod
-    public void tearDown()
-    {
-        // To 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 testNoDataSets()
-    {
-        prepareListByExperimentIds(Collections.<ExternalData>emptyList());
-        prepareListParentIds(Collections.<Integer, Integer>emptyMap());
-        
-        manager.gatherDataSets(dataSetLister);
-        
-        List<MsInjectionSample> samples = manager.getSamples();
-        for (MsInjectionSample msInjectionSample : samples)
-        {
-            assertEquals(0, msInjectionSample.getLatestDataSets().size());
-        }
-        assertEquals(3, samples.size());
-        context.assertIsSatisfied();
-    }
-    
-    @Test
-    public void testHappyCase()
-    {
-        Map<Integer, Integer> parentsMap = new HashMap<Integer, Integer>();
-        List<ExternalData> dataSets = new ArrayList<ExternalData>();
-        dataSets.add(dataSet(1, "A", SAMPLE1A));
-        dataSets.add(dataSet(2, "A", SAMPLE1A));
-        dataSets.add(dataSet(3, "A", SAMPLE1B));
-        dataSets.add(dataSet(4, "A", SAMPLE2A));
-        dataSets.add(dataSet(5, "B", null));
-        parentsMap.put(5, 1);
-        dataSets.add(dataSet(6, "B", null));
-        parentsMap.put(6, 3);
-        dataSets.add(dataSet(7, "B", null));
-        parentsMap.put(7, 3);
-        dataSets.add(dataSet(8, "B", null));
-        parentsMap.put(8, 4);
-        dataSets.add(dataSet(9, "C", null));
-        parentsMap.put(9, 6);
-        dataSets.add(dataSet(10, "C", null));
-        parentsMap.put(10, 8);
-        prepareListByExperimentIds(dataSets);
-        prepareListParentIds(parentsMap);
-        
-        manager.gatherDataSets(dataSetLister);
-        
-        List<MsInjectionSample> samples = manager.getSamples();
-        Map<Long, MsInjectionSample> map = new HashMap<Long, MsInjectionSample>();
-        for (MsInjectionSample msInjectionSample : samples)
-        {
-            map.put(msInjectionSample.getSample().getId(), msInjectionSample);
-        }
-        assertEquals(3, samples.size());
-        assertLatestDataSets(map.get(SAMPLE1A_ID), "A:2", "B:5");
-        assertLatestDataSets(map.get(SAMPLE1B_ID), "A:3", "B:7", "C:9");
-        assertLatestDataSets(map.get(SAMPLE2A_ID), "A:4", "B:8", "C:10");
-        context.assertIsSatisfied();
-    }
-    
-    
-    @Test
-    public void testWrongParent()
-    {
-        Map<Integer, Integer> parentsMap = new HashMap<Integer, Integer>();
-        List<ExternalData> dataSets = new ArrayList<ExternalData>();
-        dataSets.add(dataSet(1, "A", SAMPLE1A));
-        dataSets.add(dataSet(2, "B", null));
-        parentsMap.put(2, 42);
-        prepareListByExperimentIds(dataSets);
-        prepareListParentIds(parentsMap);
-
-        try
-        {
-            manager.gatherDataSets(dataSetLister);
-            fail("UserFailureException expected");
-        } catch (UserFailureException ex)
-        {
-            assertEquals("Following data sets have wrong parents: ds-2", ex.getMessage());
-        }
-        
-        context.assertIsSatisfied();
-    }
-    
-    private void assertLatestDataSets(MsInjectionSample sample, String... typesAndIDs)
-    {
-        Map<String, ExternalData> latestDataSets = sample.getLatestDataSets();
-        for (String typeAndID : typesAndIDs)
-        {
-            String[] array = typeAndID.split(":");
-            assertEquals(array[1], latestDataSets.get(array[0]).getId().toString());
-        }
-        assertEquals(typesAndIDs.length, latestDataSets.size());
-    }
-    
-    private void prepareListByExperimentIds(final List<ExternalData> dataSets)
-    {
-        context.checking(new Expectations()
-            {
-                {
-                    one(dataSetLister).listByExperimentTechIds(
-                            new HashSet<TechId>(Arrays.asList(new TechId(EXP1_ID), new TechId(
-                                    EXP2_ID))));
-                    will(returnValue(dataSets));
-                }
-            });
-    }
-    
-    private void prepareListParentIds(final Map<Integer, Integer> parentIds)
-    {
-        context.checking(new Expectations()
-            {
-                {
-                    Set<Entry<Integer, Integer>> entrySet = parentIds.entrySet();
-                    Map<Long, Set<Long>> result = new HashMap<Long, Set<Long>>();
-                    Set<Long> keys = new HashSet<Long>();
-                    for (Entry<Integer, Integer> entry : entrySet)
-                    {
-                        long key = entry.getKey().longValue();
-                        keys.add(key);
-                        result.put(key, Collections.singleton(entry.getValue().longValue()));
-                    }
-                    one(dataSetLister).listParentIds(keys);
-                    will(returnValue(result));
-                }
-            });
-    }
-
-    private ExternalData dataSet(long id, String type, Sample sampleOrNull)
-    {
-        ExternalData dataSet = new ExternalData();
-        dataSet.setId(id);
-        dataSet.setCode("ds-" + id);
-        dataSet.setRegistrationDate(new Date(100 * id));
-        dataSet.setDataSetType(new DataSetType(type));
-        dataSet.setSample(sampleOrNull);
-        return dataSet;
-    }
-}
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ExperimentLoaderTest.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ExperimentLoaderTest.java
new file mode 100644
index 00000000000..9ffd83a789e
--- /dev/null
+++ b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/ExperimentLoaderTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010 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.openbis.plugin.phosphonetx.server.business;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.ISampleDAO;
+import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
+import ch.systemsx.cisd.openbis.generic.shared.translator.SampleTranslator;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+@Test(groups = "db")
+public class ExperimentLoaderTest extends AbstractLoaderTestCase
+{
+    @Test
+    public void test()
+    {
+        ExperimentLoader loader = new ExperimentLoader(daoFactory);
+        List<Sample> samples = loadSamples(980l, 981l, 982l, 983l, 984l);
+        
+        loader.enrichWithExperiments(samples);
+        
+        StringBuilder builder = new StringBuilder();
+        for (Sample sample : samples)
+        {
+            builder.append(sample.getId()).append(' ').append(sample.getCode());
+            Experiment experiment = sample.getExperiment();
+            if (experiment != null)
+            {
+                builder.append(": ").append(experiment.getCode()).append(' ');
+                builder.append(getSortedProperties(experiment));
+            }
+            builder.append('\n');
+        }
+        assertEquals("980 3V-126\n" 
+                   + "981 DP\n"
+                   + "982 3VCP1: EXP1 [DESCRIPTION: A simple experiment, GENDER: MALE]\n"
+                   + "983 3VCP2\n"
+                   + "984 3VCP3: EXP1 [DESCRIPTION: A simple experiment, GENDER: MALE]\n",
+                builder.toString());
+    }
+    
+    private List<Sample> loadSamples(Long... ids)
+    {
+        ISampleDAO sampleDAO = daoFactory.getSampleDAO();
+        List<Sample> list = new ArrayList<Sample>();
+        for (Long id : ids)
+        {
+            list.add(SampleTranslator.translate(sampleDAO.tryGetByTechId(new TechId(id)), ""));
+   
+        }
+        return list;
+    }
+}
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/SampleLoaderTest.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/SampleLoaderTest.java
new file mode 100644
index 00000000000..c806da68d06
--- /dev/null
+++ b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/business/SampleLoaderTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010 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.openbis.plugin.phosphonetx.server.business;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+@Test(groups = "db")
+public class SampleLoaderTest extends AbstractLoaderTestCase
+{
+
+    @Test
+    public void test()
+    {
+        SampleLoader loader = new SampleLoader(SESSION, daoFactory, boFactory);
+        List<Sample> samples = loader.listSamplesWithParentsByTypeAndSpace("CELL_PLATE", "CISD");
+        Collections.sort(samples, new Comparator<Sample>()
+            {
+                public int compare(Sample s1, Sample s2)
+                {
+                    return s1.getCode().compareTo(s2.getCode());
+                }
+            });
+        StringBuilder builder = new StringBuilder();
+        for (Sample sample : samples)
+        {
+            Sample parent = sample.getGeneratedFrom();
+            builder.append(sample.getCode()).append(" ").append(getSortedProperties(sample));
+            builder.append(" <- ").append(parent.getCode()).append(" ");
+            builder.append(getSortedProperties(parent)).append('\n');
+        }
+        assertEquals("3VCP1 [] <- 3V-123 [OFFSET: 42]\n"
+                   + "3VCP2 [] <- 3V-123 [OFFSET: 42]\n"
+                   + "3VCP4 [] <- 3V-125 [OFFSET: 49]\n"
+                   + "3VCP5 [] <- 3V-125 [OFFSET: 49]\n"
+                   + "3VCP6 [] <- 3V-125 [OFFSET: 49]\n"
+                   + "3VCP7 [COMMENT: test comment, ORGANISM: RAT, SIZE: 4711] <- 3V-125 [OFFSET: 49]\n"
+                   + "3VCP8 [] <- 3V-125 [OFFSET: 49]\n" 
+                   + "CP1-A1 [] <- DP1-A [OFFSET: 42]\n"
+                   + "CP1-A2 [] <- DP1-A [OFFSET: 42]\n" 
+                   + "CP1-B1 [] <- DP1-B [OFFSET: 42]\n"
+                   + "CP2-A1 [] <- DP2-A [OFFSET: 42]\n", builder.toString());
+    }
+
+}
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/RawDataSampleValidatorTest.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/RawDataSampleValidatorTest.java
index 09b4f96dac0..708d3acbe70 100644
--- a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/RawDataSampleValidatorTest.java
+++ b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/shared/authorization/validator/RawDataSampleValidatorTest.java
@@ -23,6 +23,7 @@ import org.testng.AssertJUnit;
 import org.testng.annotations.Test;
 
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseInstance;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Space;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
@@ -79,7 +80,7 @@ public class RawDataSampleValidatorTest extends AssertJUnit
             }
             sample.setGeneratedFrom(parent);
         }
-        return new MsInjectionSample(sample);
+        return new MsInjectionSample(sample, Arrays.<ExternalData>asList());
     }
 
     private static DatabaseInstance createDatabaseInstance(String code)
-- 
GitLab