diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/SampleSearchLocatorResolver.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/SampleSearchLocatorResolver.java
index 1f8ecfbc1e7db940fe84364fa4b07f8b04e0eaa6..ec9ac201b387137a1a271bae7a520c33483ffe8a 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/SampleSearchLocatorResolver.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/SampleSearchLocatorResolver.java
@@ -16,9 +16,6 @@
 
 package ch.systemsx.cisd.openbis.generic.client.web.client.application.locator;
 
-import java.util.ArrayList;
-import java.util.Map;
-
 import com.extjs.gxt.ui.client.widget.MessageBox;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 
@@ -39,111 +36,30 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayC
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetWithEntityTypes;
 import ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriterion;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchField;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleAttributeSearchFieldKind;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SearchCriteriaConnection;
 
-class SampleSearchLocatorResolver
+/**
+ * A resolver that takes search critera, executes a sample search, and displays the results.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class SampleSearchLocatorResolver
 {
     private final IViewContext<ICommonClientServiceAsync> viewContext;
 
-    private final ViewLocator locator;
-
-    SampleSearchLocatorResolver(IViewContext<ICommonClientServiceAsync> viewContext,
-            ViewLocator locator)
+    public SampleSearchLocatorResolver(IViewContext<ICommonClientServiceAsync> viewContext)
     {
         this.viewContext = viewContext;
-        this.locator = locator;
     }
 
-    void openInitialEntitySearch() throws UserFailureException
+    public void openEntitySearch(DetailedSearchCriteria searchCriteria) throws UserFailureException
     {
-        openEntitySearch();
-    }
-
-    private void openEntitySearch()
-    {
-        ListSampleDisplayCriteria displayCriteria = getListSampleDisplayCriteria();
-
-        viewContext.getCommonService().listSamples(displayCriteria,
-                new OpenEntitySearchTabCallback(displayCriteria));
-    }
-
-    /**
-     * Convert the locator parameters into a ListSampleDisplayCriteria -- a LSDC is used to pass the
-     * search parameters to the server.
-     */
-    private ListSampleDisplayCriteria getListSampleDisplayCriteria()
-    {
-        // Loop over the parameters and create a detailed search criteria for each parameter
-        // -- a parameter key could refer to an attribute (valid options known at compile time)
-        // -- or a property (valid options must be retrieved from server)
-        Map<String, String> parameters = locator.getParameters();
-        ArrayList<DetailedSearchCriterion> criterionList = new ArrayList<DetailedSearchCriterion>();
-
-        DetailedSearchCriteria searchCriteria = new DetailedSearchCriteria();
-        // Default to match all
-        searchCriteria.setConnection(SearchLocatorResolver.DEFAULT_MATCH_CONNECTION);
-
-        for (String key : parameters.keySet())
-        {
-            String value = parameters.get(key);
-            // The match key is handled separately
-            if (key.equals(SearchLocatorResolver.MATCH_KEY))
-            {
-                if (value.equalsIgnoreCase(SearchLocatorResolver.MATCH_ANY_VALUE))
-                {
-                    searchCriteria.setConnection(SearchCriteriaConnection.MATCH_ANY);
-                }
-            } else if (key.equals("gwt.codesvr"))
-            {
-                // ignore this gwt keyword
-            } else
-            {
-                DetailedSearchCriterion searchCriterion = getSearchCriterionForKeyValue(key, value);
-                criterionList.add(searchCriterion);
-            }
-        }
-
-        // Default the search criteria if none is provided
-        if (criterionList.isEmpty())
-        {
-            DetailedSearchCriterion searchCriterion =
-                    new DetailedSearchCriterion(DetailedSearchField
-                            .createAttributeField(SampleAttributeSearchFieldKind.CODE),
-                            SearchLocatorResolver.DEFAULT_SEARCH_STRING);
-            criterionList.add(searchCriterion);
-        }
-
-        searchCriteria.setCriteria(criterionList);
-
         // Create a display criteria object for the search string
         ListSampleDisplayCriteria displayCriteria = ListSampleDisplayCriteria.createForSearch();
         displayCriteria.updateSearchCriteria(searchCriteria);
-        return displayCriteria;
-    }
-
-    /**
-     * Convert the key/value to a search criterion. The kind of field depends on whether the key
-     * refers to an attribute or property.
-     */
-    private DetailedSearchCriterion getSearchCriterionForKeyValue(String key, String value)
-    {
-        DetailedSearchField field;
 
-        try
-        {
-            SampleAttributeSearchFieldKind attributeKind =
-                    SampleAttributeSearchFieldKind.valueOf(key.toUpperCase());
-            field = DetailedSearchField.createAttributeField(attributeKind);
-        } catch (IllegalArgumentException ex)
-        {
-            // this is not an attribute
-            field = DetailedSearchField.createPropertyField(key.toUpperCase());
-        }
-        return new DetailedSearchCriterion(field, value);
+        viewContext.getCommonService().listSamples(displayCriteria,
+                new OpenEntitySearchTabCallback(displayCriteria));
     }
 
     private class OpenEntitySearchTabCallback implements
@@ -218,8 +134,6 @@ class SampleSearchLocatorResolver
 
         public ITabItem create()
         {
-            // CRDEBUG
-            System.err.println(displayCriteria.getSearchCriteria().toString());
             IDisposableComponent browser =
                     SampleSearchHitGrid.createWithInitialDisplayCriteria(viewContext,
                             displayCriteria);
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/SearchLocatorResolver.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/SearchLocatorResolver.java
index 76d5fd06ffec65756100eb36a0f584056fdb566d..f41970d26ef392847d532542d2d781c7a2bd7951 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/SearchLocatorResolver.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/SearchLocatorResolver.java
@@ -1,10 +1,18 @@
 package ch.systemsx.cisd.openbis.generic.client.web.client.application.locator;
 
+import java.util.ArrayList;
+import java.util.Map;
+
 import ch.systemsx.cisd.openbis.generic.client.web.client.ICommonClientServiceAsync;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext;
 import ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.shared.basic.AttributeSearchFieldKindProvider;
 import ch.systemsx.cisd.openbis.generic.shared.basic.PermlinkUtilities;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriterion;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchField;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IAttributeSearchFieldKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SearchCriteriaConnection;
 
 /**
@@ -29,6 +37,8 @@ public class SearchLocatorResolver extends AbstractViewLocatorResolver
     protected static final SearchCriteriaConnection DEFAULT_MATCH_CONNECTION =
             SearchCriteriaConnection.MATCH_ALL;
 
+    protected static final boolean DEFAULT_USE_WILDCARDS = true;
+
     public SearchLocatorResolver(IViewContext<ICommonClientServiceAsync> viewContext)
     {
         super(SEARCH_ACTION);
@@ -37,26 +47,31 @@ public class SearchLocatorResolver extends AbstractViewLocatorResolver
 
     public void resolve(ViewLocator locator) throws UserFailureException
     {
+        // Extract the search criteria from the ViewLocator and dispatch to a resolver that can
+        // handle the entity type.
         String searchEntityKindValueOrNull = locator.tryGetEntity();
         if (null == searchEntityKindValueOrNull)
             return;
 
         // Find the specific resolver based on entity type
         EntityKind entityKind = getEntityKind(searchEntityKindValueOrNull);
+
+        DetailedSearchCriteria searchCriteria =
+                new ViewLocatorToDetailedSearchCriteriaConverter(locator, entityKind)
+                        .getDetailedSearchCriteria();
         if (EntityKind.SAMPLE == entityKind)
         {
-            SampleSearchLocatorResolver resolver =
-                    new SampleSearchLocatorResolver(viewContext, locator);
-            resolver.openInitialEntitySearch();
+            SampleSearchLocatorResolver resolver = new SampleSearchLocatorResolver(viewContext);
+            resolver.openEntitySearch(searchCriteria);
         } else
         {
-            throw new UserFailureException("URLs for searching openBIS only support "
-                    + EntityKind.SAMPLE.getDescription() + " searches. "
-                    + entityKind.getDescription() + "s are not supported.");
+            throw new UserFailureException(
+                    "URLs for searching openBIS only support SAMPLE and DATA_SET searches. Entity "
+                            + entityKind + " is not supported.");
         }
     }
 
-    private EntityKind getEntityKind(String entityKindValueOrNull)
+    protected EntityKind getEntityKind(String entityKindValueOrNull)
     {
         try
         {
@@ -67,4 +82,90 @@ public class SearchLocatorResolver extends AbstractViewLocatorResolver
                     + PermlinkUtilities.ENTITY_KIND_PARAMETER_KEY + "' URL parameter value.");
         }
     }
+
+    protected static class ViewLocatorToDetailedSearchCriteriaConverter
+    {
+        private final ViewLocator locator;
+
+        private final EntityKind entityKind;
+
+        protected ViewLocatorToDetailedSearchCriteriaConverter(ViewLocator locator,
+                EntityKind entityKind)
+        {
+            this.locator = locator;
+            this.entityKind = entityKind;
+        }
+
+        protected DetailedSearchCriteria getDetailedSearchCriteria()
+        {
+            // Loop over the parameters and create a detailed search criteria for each parameter
+            // -- a parameter key could refer to an attribute (valid options known at compile time)
+            // -- or a property (valid options must be retrieved from server)
+            Map<String, String> parameters = locator.getParameters();
+            ArrayList<DetailedSearchCriterion> criterionList =
+                    new ArrayList<DetailedSearchCriterion>();
+
+            DetailedSearchCriteria searchCriteria = new DetailedSearchCriteria();
+            // Default to match all
+            searchCriteria.setConnection(SearchLocatorResolver.DEFAULT_MATCH_CONNECTION);
+
+            // Default to use wildcards
+            searchCriteria.setUseWildcardSearchMode(SearchLocatorResolver.DEFAULT_USE_WILDCARDS);
+
+            for (String key : parameters.keySet())
+            {
+                String value = parameters.get(key);
+                // The match key is handled separately
+                if (key.equals(SearchLocatorResolver.MATCH_KEY))
+                {
+                    if (value.equalsIgnoreCase(SearchLocatorResolver.MATCH_ANY_VALUE))
+                    {
+                        searchCriteria.setConnection(SearchCriteriaConnection.MATCH_ANY);
+                    }
+                } else
+                {
+                    DetailedSearchCriterion searchCriterion =
+                            getSearchCriterionForKeyValueAndEntityKind(key, value);
+                    criterionList.add(searchCriterion);
+                }
+            }
+
+            // Default the search criteria if none is provided
+            if (criterionList.isEmpty())
+            {
+                DetailedSearchCriterion searchCriterion =
+                        new DetailedSearchCriterion(DetailedSearchField
+                                .createAttributeField(AttributeSearchFieldKindProvider
+                                        .getAttributeFieldKind(entityKind, "CODE")),
+                                SearchLocatorResolver.DEFAULT_SEARCH_STRING);
+                criterionList.add(searchCriterion);
+            }
+
+            searchCriteria.setCriteria(criterionList);
+            return searchCriteria;
+        }
+
+        /**
+         * Convert the key/value to a search criterion. The kind of field depends on whether the key
+         * refers to an attribute or property.
+         */
+        protected DetailedSearchCriterion getSearchCriterionForKeyValueAndEntityKind(String key,
+                String value)
+        {
+            DetailedSearchField field;
+
+            try
+            {
+                IAttributeSearchFieldKind searchFieldKind =
+                        AttributeSearchFieldKindProvider.getAttributeFieldKind(entityKind, key
+                                .toUpperCase());
+                field = DetailedSearchField.createAttributeField(searchFieldKind);
+            } catch (IllegalArgumentException ex)
+            {
+                // this is not an attribute
+                field = DetailedSearchField.createPropertyField(key.toUpperCase());
+            }
+            return new DetailedSearchCriterion(field, value);
+        }
+    }
 }
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/ViewLocator.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/ViewLocator.java
index 2abfa2539e2348a1a716dcbedbb118cfcd62ef64..7f40ec5aa3572b809696f97fca2650e1ef446763 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/ViewLocator.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/ViewLocator.java
@@ -46,6 +46,8 @@ public class ViewLocator
 
     public static final String PERMLINK_ACTION = "VIEW";
 
+    private static final String GWT_PARAMETER = "gwt.codesvr";
+
     // Instance Variables
     private String actionOrNull;
 
@@ -122,7 +124,10 @@ public class ViewLocator
             final String[] paramPair = params[i].split(KEY_VALUE_SEPARATOR);
             assert paramPair.length == 2 : "Incorrectly formatted URL parameters";
 
-            if (ACTION_PARAMETER.equalsIgnoreCase(paramPair[0]))
+            if (GWT_PARAMETER.equals(paramPair[0]))
+            {
+                // skip GWT parameters -- only relevant during testing
+            } else if (ACTION_PARAMETER.equalsIgnoreCase(paramPair[0]))
             {
                 actionOrNull = paramPair[1];
             } else if (ENTITY_PARAMETER.equalsIgnoreCase(paramPair[0]))
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/sample/SampleSearchHitGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/sample/SampleSearchHitGrid.java
index 7a06dfc681643391e61deb4e8652b2382e3aeff3..5a8fc77da7d35591bae3fb19cc76d41227a77c8f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/sample/SampleSearchHitGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/sample/SampleSearchHitGrid.java
@@ -76,8 +76,6 @@ public class SampleSearchHitGrid extends SampleBrowserGrid implements IDetailedS
             final IViewContext<ICommonClientServiceAsync> viewContext,
             ListSampleDisplayCriteria displayCriteria)
     {
-        assert displayCriteria.getSearchCriteria().getCriteria().size() == 1;
-
         // Use the caller-provided display criteria
         ISampleCriteriaProvider criteriaProvider =
                 new SampleCriteriaProvider(viewContext, displayCriteria);