diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
index 938f91c4122553dfebfa5040540cea6490fe9a87..6239fa321ad137b99dbe89669358f88a34c1e77f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
@@ -71,7 +71,6 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.dynamic_property.calc
 import ch.systemsx.cisd.openbis.generic.server.business.bo.materiallister.IMaterialLister;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.samplelister.ISampleLister;
 import ch.systemsx.cisd.openbis.generic.server.business.search.DataSetSearchManager;
-import ch.systemsx.cisd.openbis.generic.server.business.search.SampleSearchManager;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataStoreDAO;
@@ -266,7 +265,7 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
         return businessObjectFactory;
     }
 
-    private static UserFailureException createUserFailureException(final DataAccessException ex)
+    static UserFailureException createUserFailureException(final DataAccessException ex)
     {
         return new UserFailureException(ex.getMostSpecificCause().getMessage(), ex);
     }
@@ -492,15 +491,9 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
     public List<Sample> searchForSamples(String sessionToken, DetailedSearchCriteria criteria)
     {
         final Session session = getSession(sessionToken);
-        try
-        {
-            final ISampleLister sampleLister = businessObjectFactory.createSampleLister(session);
-            final IHibernateSearchDAO searchDAO = getDAOFactory().getHibernateSearchDAO();
-            return new SampleSearchManager(searchDAO, sampleLister).searchForSamples(criteria);
-        } catch (final DataAccessException ex)
-        {
-            throw createUserFailureException(ex);
-        }
+        SearchHelper searchHelper =
+                new SearchHelper(session, businessObjectFactory, getDAOFactory());
+        return searchHelper.searchForSamples(criteria);
     }
 
     public final List<ExternalData> listSampleExternalData(final String sessionToken,
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
index 3caa53ff5be80301626cb54b82f284eda0e366aa..af1f540cc45529c76fd7e0db0b8c6f8152f7007c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
@@ -31,6 +31,7 @@ import ch.systemsx.cisd.common.collections.CollectionUtils;
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.spring.IInvocationLoggerContext;
+import ch.systemsx.cisd.openbis.generic.server.api.v1.SearchCriteriaToDetailedSearchCriteriaTranslator;
 import ch.systemsx.cisd.openbis.generic.server.business.IDataStoreServiceFactory;
 import ch.systemsx.cisd.openbis.generic.server.business.IPropertiesBatchManager;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
@@ -54,6 +55,8 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.ISampleTypeDAO;
 import ch.systemsx.cisd.openbis.generic.server.plugin.IDataSetTypeSlaveServerPlugin;
 import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
 import ch.systemsx.cisd.openbis.generic.shared.IServer;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchableEntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ArchiverDataSetCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSet;
@@ -63,6 +66,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStoreServiceKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseInstance;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatastoreServiceDescription;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DeletedDataSet;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentType;
@@ -1106,16 +1110,14 @@ public class ETLService extends AbstractCommonServer<IETLService> implements IET
                 samplesUpdated, samplesCreated, dataSetsCreated, dataSetsUpdated);
     }
 
-    private List<Space> createSpaces(Session session,
-            AtomicEntityOperationDetails operationDetails)
+    private List<Space> createSpaces(Session session, AtomicEntityOperationDetails operationDetails)
     {
         ArrayList<SpacePE> spacePEsCreated = new ArrayList<SpacePE>();
         List<NewSpace> newSpaces = operationDetails.getSpaceRegistrations();
         for (NewSpace newSpace : newSpaces)
         {
             SpacePE spacePE =
-                    registerSpaceInternal(session, newSpace,
-                            operationDetails.tryUserIdOrNull());
+                    registerSpaceInternal(session, newSpace, operationDetails.tryUserIdOrNull());
             spacePEsCreated.add(spacePE);
         }
         return GroupTranslator.translate(spacePEsCreated);
@@ -1161,8 +1163,7 @@ public class ETLService extends AbstractCommonServer<IETLService> implements IET
         for (NewProject newProject : newProjects)
         {
             ProjectPE projectPE =
-                    registerProjectInternal(session, newProject,
-                            operationDetails.tryUserIdOrNull());
+                    registerProjectInternal(session, newProject, operationDetails.tryUserIdOrNull());
             projectPEsCreated.add(projectPE);
         }
         return ProjectTranslator.translate(projectPEsCreated);
@@ -1193,8 +1194,7 @@ public class ETLService extends AbstractCommonServer<IETLService> implements IET
         for (NewSample newSample : newSamples)
         {
             SamplePE samplePE =
-                    registerSampleInternal(session, newSample,
-                            operationDetails.tryUserIdOrNull());
+                    registerSampleInternal(session, newSample, operationDetails.tryUserIdOrNull());
             samplePEsCreated.add(samplePE);
         }
         return SampleTranslator.translate(samplePEsCreated, session.getBaseIndexURL());
@@ -1354,4 +1354,15 @@ public class ETLService extends AbstractCommonServer<IETLService> implements IET
         return externalDataBO;
     }
 
+    public List<Sample> searchForSamples(String sessionToken, SearchCriteria searchCriteria)
+    {
+        Session session = getSession(sessionToken);
+        DetailedSearchCriteria detailedSearchCriteria =
+                SearchCriteriaToDetailedSearchCriteriaTranslator.convert(
+                        SearchableEntityKind.SAMPLE, searchCriteria);
+        SearchHelper searchHelper =
+                new SearchHelper(session, businessObjectFactory, getDAOFactory());
+        return searchHelper.searchForSamples(detailedSearchCriteria);
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java
index 31c61ec419a3ce2c1cc14121b31f2d2efbfe2de7..7c503106f226a8312146ad5b556b167e40676691 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java
@@ -24,6 +24,7 @@ import ch.systemsx.cisd.authentication.ISessionManager;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.spring.IInvocationLoggerContext;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerLogger;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ArchiverDataSetCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetArchivingStatus;
@@ -342,8 +343,8 @@ public class ETLServiceLogger extends AbstractServerLogger implements IETLServic
     public void updateShareIdAndSize(String sessionToken, String dataSetCode, String shareId,
             long size) throws UserFailureException
     {
-        logTracking(sessionToken, "updateShareIdAndSize", "DATA_SET_CODE(%s) SHARE_ID(%s) SIZE(%s)",
-                dataSetCode, shareId, size);
+        logTracking(sessionToken, "updateShareIdAndSize",
+                "DATA_SET_CODE(%s) SHARE_ID(%s) SIZE(%s)", dataSetCode, shareId, size);
     }
 
     public void updateDataSetStatuses(String sessionToken, List<String> dataSetCodes,
@@ -475,4 +476,10 @@ public class ETLServiceLogger extends AbstractServerLogger implements IETLServic
         return null;
     }
 
+    public List<Sample> searchForSamples(String sessionToken, SearchCriteria searchCriteria)
+    {
+        logAccess(sessionToken, "searchForSamples", "%s", searchCriteria);
+        return null;
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/SearchHelper.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/SearchHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..8df2ff513f209b97606c55c67b1f1d6db909620a
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/SearchHelper.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2011 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.generic.server;
+
+import java.util.List;
+
+import org.springframework.dao.DataAccessException;
+
+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.business.search.SampleSearchManager;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IHibernateSearchDAO;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
+import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
+
+/**
+ * A class with helper methods for implementing search. This is the shared code for that searches
+ * originating from CommonServer and ETLService.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+class SearchHelper
+{
+    private final Session session;
+
+    private final ICommonBusinessObjectFactory businessObjectFactory;
+
+    private final IDAOFactory daoFactory;
+
+    public SearchHelper(Session session, ICommonBusinessObjectFactory businessObjectFactory,
+            IDAOFactory daoFactory)
+    {
+        this.session = session;
+        this.businessObjectFactory = businessObjectFactory;
+        this.daoFactory = daoFactory;
+    }
+
+    public List<Sample> searchForSamples(DetailedSearchCriteria criteria)
+    {
+        try
+        {
+            final ISampleLister sampleLister = businessObjectFactory.createSampleLister(session);
+            final IHibernateSearchDAO searchDAO = daoFactory.getHibernateSearchDAO();
+            return new SampleSearchManager(searchDAO, sampleLister).searchForSamples(criteria);
+        } catch (final DataAccessException ex)
+        {
+            throw CommonServer.createUserFailureException(ex);
+        }
+    }
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslator.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslator.java
index 133742196d06d0e7f310fde93dde65e2203ee3bb..3c043796aeeb946420303c98b51d3b0a13ac2bc9 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslator.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslator.java
@@ -47,7 +47,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SearchCriteriaConnectio
  * 
  * @author Chandrasekhar Ramakrishnan
  */
-class SearchCriteriaToDetailedSearchCriteriaTranslator
+public class SearchCriteriaToDetailedSearchCriteriaTranslator
 {
     private final static String INVALID_SEARCH_ATTRIBUTE_TEMPLATE =
             "%s is not a valid search attribute for %s";
@@ -288,7 +288,7 @@ class SearchCriteriaToDetailedSearchCriteriaTranslator
                         ((AttributeMatchClause) matchClause).getAttribute();
                 IAttributeSearchFieldKind searchFieldKind =
                         attributeTranslator
-                                        .convertMatchClauseAttributeToAttributeSearchFieldKind(attribute);
+                                .convertMatchClauseAttributeToAttributeSearchFieldKind(attribute);
                 searchField = DetailedSearchField.createAttributeField(searchFieldKind);
                 break;
             default:
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java
index 07d95e038ab952277e5c5ec7fc496065ed8bd65c..f955a5d60f3ad38506dc0505b87d6c60ad51c6cd 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java
@@ -23,6 +23,7 @@ import java.util.List;
 import org.springframework.transaction.annotation.Transactional;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.ISessionProvider;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.AuthorizationGuard;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.ReturnValueFilter;
@@ -111,7 +112,8 @@ public interface IETLLIMSService extends IServer, ISessionProvider
      * @param experimentIdentifier an identifier which uniquely identifies the experiment.
      */
     @Transactional(readOnly = true)
-    @RolesAllowed({RoleWithHierarchy.SPACE_OBSERVER, RoleWithHierarchy.SPACE_ETL_SERVER})
+    @RolesAllowed(
+        { RoleWithHierarchy.SPACE_OBSERVER, RoleWithHierarchy.SPACE_ETL_SERVER })
     public Experiment tryToGetExperiment(
             String sessionToken,
             @AuthorizationGuard(guardClass = ExistingSpaceIdentifierPredicate.class) ExperimentIdentifier experimentIdentifier)
@@ -183,7 +185,8 @@ public interface IETLLIMSService extends IServer, ISessionProvider
      * @return a sorted list of {@link ExternalData}.
      */
     @Transactional(readOnly = true)
-    @RolesAllowed({RoleWithHierarchy.SPACE_OBSERVER, RoleWithHierarchy.SPACE_ETL_SERVER})
+    @RolesAllowed(
+        { RoleWithHierarchy.SPACE_OBSERVER, RoleWithHierarchy.SPACE_ETL_SERVER })
     public List<ExternalData> listDataSetsByExperimentID(
             final String sessionToken,
             @AuthorizationGuard(guardClass = ExperimentTechIdPredicate.class) final TechId experimentID)
@@ -349,7 +352,7 @@ public interface IETLLIMSService extends IServer, ISessionProvider
     @Transactional(readOnly = true)
     @RolesAllowed(RoleWithHierarchy.INSTANCE_ADMIN)
     public void checkInstanceAdminAuthorization(String sessionToken) throws UserFailureException;
-    
+
     /**
      * Checks that the user of specified session has SPACE_POWER_USER access rights.
      */
@@ -428,8 +431,8 @@ public interface IETLLIMSService extends IServer, ISessionProvider
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER)
-    public List<DataSetShareId> listShareIds(final String sessionToken,
-            String dataStore) throws UserFailureException;
+    public List<DataSetShareId> listShareIds(final String sessionToken, String dataStore)
+            throws UserFailureException;
 
     /**
      * Lists data sets belonging to chosen data store.
@@ -438,7 +441,7 @@ public interface IETLLIMSService extends IServer, ISessionProvider
     @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER)
     public List<SimpleDataSetInformationDTO> listDataSets(final String sessionToken,
             String dataStore) throws UserFailureException;
-    
+
     /**
      * List data sets deleted after specified date.
      */
@@ -460,7 +463,8 @@ public interface IETLLIMSService extends IServer, ISessionProvider
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER)
-    public List<ExternalData> listDataSets(String sessionToken, String dataStoreCode, TrackingDataSetCriteria criteria);
+    public List<ExternalData> listDataSets(String sessionToken, String dataStoreCode,
+            TrackingDataSetCriteria criteria);
 
     /**
      * List all experiments for a given project identifier.
@@ -480,7 +484,7 @@ public interface IETLLIMSService extends IServer, ISessionProvider
         { RoleWithHierarchy.SPACE_OBSERVER })
     @ReturnValueFilter(validatorClass = ProjectValidator.class)
     public List<Project> listProjects(String sessionToken);
-    
+
     /**
      * Adds specified properties of given data set. Properties defined before will not be updated.
      */
@@ -677,4 +681,15 @@ public interface IETLLIMSService extends IServer, ISessionProvider
     public Project tryGetProject(
             String sessionToken,
             @AuthorizationGuard(guardClass = ExistingSpaceIdentifierPredicate.class) ProjectIdentifier projectIdentifier);
+
+    /**
+     * Search for samples matching the provided criteria.
+     * 
+     * @param sessionToken The user authentication token. Must not be <code>null</code>.
+     * @param searchCriteria The criteria for samples.
+     * @return A collection of samples matching the search criteria.
+     */
+    @Transactional(readOnly = true)
+    @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER)
+    public List<Sample> searchForSamples(String sessionToken, SearchCriteria searchCriteria);
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/DataSet.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/DataSet.java
index 8e0631296b29d41fd910241cb75cc63c5ef55c16..42d4b44ec4ce22bfabc2e5e94d7cbc442a2a1c1b 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/DataSet.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/DataSet.java
@@ -281,7 +281,26 @@ public final class DataSet implements Serializable
         builder.append(getExperimentIdentifier());
         builder.append(getSampleIdentifierOrNull());
         builder.append(getDataSetTypeCode());
-        builder.append(getProperties());
+
+        // Append properties alphabetically for consistency
+        HashMap<String, String> props = getProperties();
+        ArrayList<String> sortedKeys = new ArrayList<String>(props.size());
+        sortedKeys.addAll(props.keySet());
+        Collections.sort(sortedKeys);
+        StringBuilder propString = new StringBuilder();
+        propString.append("{");
+        for (String key : sortedKeys)
+        {
+            propString.append(key);
+            propString.append("=");
+            propString.append(props.get(key));
+            propString.append(", ");
+        }
+        // Get rid of the trailing ,
+        propString.deleteCharAt(propString.length() - 1);
+        propString.deleteCharAt(propString.length() - 1);
+        propString.append("}");
+        builder.append(propString);
         if (retrievedConnections.contains(Connections.PARENTS))
         {
             builder.append(getParentCodes());
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java
index 62e2d313715d917ddaabd33084cca89ede988cf4..3ea32e1518a9226db55cf9883e5ece79e19bf809 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java
@@ -39,16 +39,22 @@ import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.business.IDataStoreServiceFactory;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IHibernateSearchDAO;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerTestCase;
 import ch.systemsx.cisd.openbis.generic.shared.CommonTestUtils;
 import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
 import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseAttribute;
 import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant;
 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.DatastoreServiceDescription;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListOrSearchSampleCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAttachment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewExperiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewProject;
@@ -62,6 +68,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SourceType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.DataSetBuilder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.DataStoreBuilder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.ExperimentBuilder;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.SampleBuilder;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationDetails;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationResult;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
@@ -88,6 +95,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SampleUpdatesDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.DatabaseInstanceIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
+import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 
 /**
  * @author Franz-Josef Elmer
@@ -117,6 +125,8 @@ public class ETLServiceTest extends AbstractServerTestCase
 
     private IDataStoreService dataStoreService;
 
+    private IHibernateSearchDAO hibernateSearchDao;
+
     @Override
     @BeforeMethod
     public final void setUp()
@@ -125,6 +135,7 @@ public class ETLServiceTest extends AbstractServerTestCase
         boFactory = context.mock(ICommonBusinessObjectFactory.class);
         dssfactory = context.mock(IDataStoreServiceFactory.class);
         dataStoreService = context.mock(IDataStoreService.class);
+        hibernateSearchDao = context.mock(IHibernateSearchDAO.class);
     }
 
     @Test
@@ -1007,6 +1018,48 @@ public class ETLServiceTest extends AbstractServerTestCase
         context.assertIsSatisfied();
     }
 
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSearchForSamples()
+    {
+        prepareGetSession();
+        context.checking(new Expectations()
+            {
+                {
+                    one(boFactory).createSampleLister(SESSION);
+                    will(returnValue(sampleLister));
+
+                    one(daoFactory).getHibernateSearchDAO();
+                    will(returnValue(hibernateSearchDao));
+
+                    one(hibernateSearchDao).searchForEntityIds(
+                            with(aNonNull(DetailedSearchCriteria.class)),
+                            with(equal(EntityKind.SAMPLE)), with(aNonNull(List.class)));
+                    will(returnValue(Arrays.asList(new Long(1), new Long(2))));
+
+                    one(sampleLister).list(with(aNonNull(ListOrSearchSampleCriteria.class)));
+                    SampleBuilder sample1 = new SampleBuilder().id(1);
+                    SampleBuilder sample2 = new SampleBuilder().id(2);
+                    will(returnValue(Arrays.asList(sample1, sample2)));
+
+                }
+            });
+
+        List<Sample> sample =
+                createService().searchForSamples(SESSION_TOKEN, createSearchCriteriaForSample());
+
+        assertEquals(2, sample.size());
+        context.assertIsSatisfied();
+    }
+
+    private SearchCriteria createSearchCriteriaForSample()
+    {
+        SearchCriteria sc = new SearchCriteria();
+        sc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, "a code"));
+        sc.addMatchClause(MatchClause.createPropertyMatch("MY_PROPERTY2", "a property value"));
+        return sc;
+    }
+
     private void prepareRegisterDataSet(final SampleIdentifier sampleIdentifier,
             final ExperimentPE experiment, final SourceType sourceType,
             final NewExternalData externalData)
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java.expected b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java.expected
index 07d95e038ab952277e5c5ec7fc496065ed8bd65c..f955a5d60f3ad38506dc0505b87d6c60ad51c6cd 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java.expected
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java.expected
@@ -23,6 +23,7 @@ import java.util.List;
 import org.springframework.transaction.annotation.Transactional;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.ISessionProvider;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.AuthorizationGuard;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.ReturnValueFilter;
@@ -111,7 +112,8 @@ public interface IETLLIMSService extends IServer, ISessionProvider
      * @param experimentIdentifier an identifier which uniquely identifies the experiment.
      */
     @Transactional(readOnly = true)
-    @RolesAllowed({RoleWithHierarchy.SPACE_OBSERVER, RoleWithHierarchy.SPACE_ETL_SERVER})
+    @RolesAllowed(
+        { RoleWithHierarchy.SPACE_OBSERVER, RoleWithHierarchy.SPACE_ETL_SERVER })
     public Experiment tryToGetExperiment(
             String sessionToken,
             @AuthorizationGuard(guardClass = ExistingSpaceIdentifierPredicate.class) ExperimentIdentifier experimentIdentifier)
@@ -183,7 +185,8 @@ public interface IETLLIMSService extends IServer, ISessionProvider
      * @return a sorted list of {@link ExternalData}.
      */
     @Transactional(readOnly = true)
-    @RolesAllowed({RoleWithHierarchy.SPACE_OBSERVER, RoleWithHierarchy.SPACE_ETL_SERVER})
+    @RolesAllowed(
+        { RoleWithHierarchy.SPACE_OBSERVER, RoleWithHierarchy.SPACE_ETL_SERVER })
     public List<ExternalData> listDataSetsByExperimentID(
             final String sessionToken,
             @AuthorizationGuard(guardClass = ExperimentTechIdPredicate.class) final TechId experimentID)
@@ -349,7 +352,7 @@ public interface IETLLIMSService extends IServer, ISessionProvider
     @Transactional(readOnly = true)
     @RolesAllowed(RoleWithHierarchy.INSTANCE_ADMIN)
     public void checkInstanceAdminAuthorization(String sessionToken) throws UserFailureException;
-    
+
     /**
      * Checks that the user of specified session has SPACE_POWER_USER access rights.
      */
@@ -428,8 +431,8 @@ public interface IETLLIMSService extends IServer, ISessionProvider
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER)
-    public List<DataSetShareId> listShareIds(final String sessionToken,
-            String dataStore) throws UserFailureException;
+    public List<DataSetShareId> listShareIds(final String sessionToken, String dataStore)
+            throws UserFailureException;
 
     /**
      * Lists data sets belonging to chosen data store.
@@ -438,7 +441,7 @@ public interface IETLLIMSService extends IServer, ISessionProvider
     @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER)
     public List<SimpleDataSetInformationDTO> listDataSets(final String sessionToken,
             String dataStore) throws UserFailureException;
-    
+
     /**
      * List data sets deleted after specified date.
      */
@@ -460,7 +463,8 @@ public interface IETLLIMSService extends IServer, ISessionProvider
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER)
-    public List<ExternalData> listDataSets(String sessionToken, String dataStoreCode, TrackingDataSetCriteria criteria);
+    public List<ExternalData> listDataSets(String sessionToken, String dataStoreCode,
+            TrackingDataSetCriteria criteria);
 
     /**
      * List all experiments for a given project identifier.
@@ -480,7 +484,7 @@ public interface IETLLIMSService extends IServer, ISessionProvider
         { RoleWithHierarchy.SPACE_OBSERVER })
     @ReturnValueFilter(validatorClass = ProjectValidator.class)
     public List<Project> listProjects(String sessionToken);
-    
+
     /**
      * Adds specified properties of given data set. Properties defined before will not be updated.
      */
@@ -677,4 +681,15 @@ public interface IETLLIMSService extends IServer, ISessionProvider
     public Project tryGetProject(
             String sessionToken,
             @AuthorizationGuard(guardClass = ExistingSpaceIdentifierPredicate.class) ProjectIdentifier projectIdentifier);
+
+    /**
+     * Search for samples matching the provided criteria.
+     * 
+     * @param sessionToken The user authentication token. Must not be <code>null</code>.
+     * @param searchCriteria The criteria for samples.
+     * @return A collection of samples matching the search criteria.
+     */
+    @Transactional(readOnly = true)
+    @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER)
+    public List<Sample> searchForSamples(String sessionToken, SearchCriteria searchCriteria);
 }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
index 4d2f07aaf9b1f99566678ac76b3e4a792ebe70b8..50e089de90274b85e6252a507aa7906b5077e43c 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
@@ -470,7 +470,7 @@ public class GeneralInformationServiceTest extends SystemTestCase
         assertEquals(4, result.size());
         assertEquals(
                 "[DataSet[20081105092158673-1,/CISD/NEMO/EXP1,/CISD/3VCP1,HCS_IMAGE,{COMMENT=no comment},[]], "
-                        + "DataSet[20081105092159111-1,/CISD/NEMO/EXP-TEST-1,/CISD/CP-TEST-1,HCS_IMAGE,{ANY_MATERIAL=null, GENDER=null, COMMENT=no comment, BACTERIUM=null},[]], "
+                        + "DataSet[20081105092159111-1,/CISD/NEMO/EXP-TEST-1,/CISD/CP-TEST-1,HCS_IMAGE,{ANY_MATERIAL=null, BACTERIUM=null, COMMENT=no comment, GENDER=null},[]], "
                         + "DataSet[20081105092159222-2,/CISD/NOE/EXP-TEST-2,/CISD/CP-TEST-2,HCS_IMAGE,{COMMENT=no comment},[]], "
                         + "DataSet[20081105092159333-3,/CISD/NEMO/EXP-TEST-2,/CISD/CP-TEST-3,HCS_IMAGE,{COMMENT=no comment},[]]]",
                 result.toString());