diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AbstractSwingGUI.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AbstractSwingGUI.java
index 6c4eb40adba0f4c6b334434bd76fa069a3167cb8..27144c051f9c9ebcd406c4d7373d0ce7246545c5 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AbstractSwingGUI.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AbstractSwingGUI.java
@@ -236,9 +236,8 @@ public abstract class AbstractSwingGUI
                     @Override
                     public void run()
                     {
-                        JOptionPane.showMessageDialog(parentFrame,
-                                WordUtils.wrap(message, MESSAGE_WRAP_MAX_CHAR), title,
-                                JOptionPane.ERROR_MESSAGE);
+                        UiUtilities.showMessageAndException(parentFrame, throwable, 
+                                WordUtils.wrap(message, MESSAGE_WRAP_MAX_CHAR), title);
                     }
                 });
         }
@@ -269,90 +268,4 @@ public abstract class AbstractSwingGUI
         }
     }
 
-    // -------- errors reporting -----------------
-
-    protected static void showErrorsAndWarningsIfAny(JFrame frame, String firstMessageOrNull,
-            List<String> warningMessages, List<Throwable> exceptions)
-    {
-        String message = (firstMessageOrNull == null ? "" : firstMessageOrNull + "\n");
-        message += joinMessages(warningMessages, exceptions);
-        if (exceptions.size() > 0)
-        {
-            showErrorMessage(frame, message);
-        } else if (warningMessages.size() > 0)
-        {
-            showWarningMessage(frame, message);
-        }
-    }
-
-    private static void showErrorMessage(JFrame frame, String message)
-    {
-        showMessageDialog(frame, message, "Error", JOptionPane.ERROR_MESSAGE);
-    }
-
-    private static void showWarningMessage(JFrame frame, String message)
-    {
-        showMessageDialog(frame, message, "Warning", JOptionPane.WARNING_MESSAGE);
-    }
-
-    private static void showMessageDialog(JFrame frame, String message, String title,
-            int messageType)
-    {
-        JOptionPane.showMessageDialog(frame, message, title, messageType);
-    }
-
-    private static String joinMessages(List<String> warningMessages, List<Throwable> exceptions)
-    {
-        StringBuffer sb = new StringBuffer();
-        addErrorMessages(exceptions, sb);
-        addWarningMessages(warningMessages, sb);
-        return sb.toString();
-    }
-
-    private static void addErrorMessages(List<Throwable> exceptions, StringBuffer sb)
-    {
-        if (exceptions.size() > 0)
-        {
-            if (exceptions.size() > 1)
-            {
-                sb.append("Following errors occured: \n");
-            }
-            for (Throwable exception : exceptions)
-            {
-                sb.append(getErrorMessage(exception));
-                sb.append("\n");
-            }
-        }
-    }
-
-    private static void addWarningMessages(List<String> warningMessages, StringBuffer sb)
-    {
-        if (warningMessages.size() > 0)
-        {
-            sb.append("Following warnings occured (you can most probably ignore them): \n");
-            String lastWarningMessage = "";
-            for (String warningMessage : warningMessages)
-            {
-                if (lastWarningMessage.equals(warningMessage) == false)
-                {
-                    sb.append(warningMessage);
-                    sb.append("\n");
-                    lastWarningMessage = warningMessage;
-                }
-            }
-        }
-    }
-
-    private static String getErrorMessage(Throwable throwable)
-    {
-        final String message;
-        if (throwable instanceof HighLevelException)
-        {
-            message = throwable.getMessage();
-        } else
-        {
-            message = "ERROR: " + throwable;
-        }
-        return message;
-    }
 }
\ No newline at end of file
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AbstractTreeEntityPickerDialog.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AbstractTreeEntityPickerDialog.java
index 25de90dc8cf8bca521f571fa1b3cc1839c11ede6..7e1e9d1d6b523b494a7e70c03022480f3d02d610 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AbstractTreeEntityPickerDialog.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AbstractTreeEntityPickerDialog.java
@@ -26,7 +26,6 @@ import java.util.List;
 
 import javax.swing.JDialog;
 import javax.swing.JFrame;
-import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
@@ -49,6 +48,7 @@ import ch.systemsx.cisd.openbis.dss.client.api.gui.model.UploadClientSortingUtil
 import ch.systemsx.cisd.openbis.dss.client.api.gui.tree.FilterableMutableTreeNode;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetOwnerType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 
 /**
  * Abstract super class of all entity picker dialogs based on {@link JTree}.
@@ -77,6 +77,8 @@ public abstract class AbstractTreeEntityPickerDialog extends AbstractEntityPicke
         this.entityKind = entityKind;
         tree = new JTree();
         tree.setModel(new DefaultTreeModel(null));
+        tree.setRootVisible(false);
+        tree.setShowsRootHandles(true);
         tree.addTreeWillExpandListener(this);
         tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
         addTreeSelectionListener();
@@ -93,7 +95,7 @@ public abstract class AbstractTreeEntityPickerDialog extends AbstractEntityPicke
         final JScrollPane scrollPane = new JScrollPane(tree);
 
         List<Object> objectsAsList = new ArrayList<Object>();
-        objectsAsList.add("Filter experiments: ");
+        objectsAsList.add("Filter experiments and samples: ");
         objectsAsList.add(northPanel);
         objectsAsList.add("Select " + entityKind.toString().toLowerCase() + ":");
 
@@ -159,66 +161,104 @@ public abstract class AbstractTreeEntityPickerDialog extends AbstractEntityPicke
             }
         }
         String label = entityKind.toString();
-        JOptionPane.showMessageDialog(this, "No correct " + label.toLowerCase() + " found, " + selected + " is not correct!",
-                "No correct " + label.toLowerCase() + " found, " + selected + " is not correct!", JOptionPane.WARNING_MESSAGE);
+        JOptionPane.showMessageDialog(this, "Please, select a " + label.toLowerCase() + ".",
+                "No " + label.toLowerCase() + " selected!", JOptionPane.WARNING_MESSAGE);
         optionPane.setValue(optionPane.getInitialValue());
     }
 
     @Override
     public void treeWillExpand(final TreeExpansionEvent event) throws ExpandVetoException
     {
-        final DefaultMutableTreeNode node =
-                (DefaultMutableTreeNode) event.getPath().getLastPathComponent();
+        final FilterableMutableTreeNode node =
+                (FilterableMutableTreeNode) event.getPath().getLastPathComponent();
         // if top level, then finish
         if (((TreeNode) node).getParent() == null)
         {
             return;
         }
         Object userObject = node.getUserObject();
-        if (userObject instanceof Identifier)
+        if (UiUtilities.ROOT_EXPERIMENTS.equals(userObject))
+        {
+            expandExperiments(node);
+        } else if (UiUtilities.ROOT_SAMPLES.equals(userObject))
+        {
+            expandSamples(node);
+        } else if (userObject instanceof Identifier)
         {
             expandNode(node, (Identifier) userObject);
         }
     }
+    
+    private void expandExperiments(final FilterableMutableTreeNode node)
+    {
+        AsyncNodeAction<List<Experiment>> action =
+                new AsyncNodeAction<List<Experiment>>(tree, node, scheduler)
+                    {
+                        @Override
+                        public void handleData(List<Experiment> experiments)
+                        {
+                            node.removeAllChildren();
+                            UploadClientSortingUtils.sortExperimentsByIdentifier(experiments);
+                            for (Experiment experiment : experiments)
+                            {
+                                FilterableMutableTreeNode category =
+                                        new FilterableMutableTreeNode(Identifier.create(experiment));
+                                category.add(UiUtilities.createWaitingNode());
+                                node.add(category);
+                            }
+                        }
+                    };
+        clientModel.listExperiments(action);
+    }
+    
+    private void expandSamples(final FilterableMutableTreeNode node)
+    {
+        AsyncNodeAction<List<Sample>> action =
+                new AsyncNodeAction<List<Sample>>(tree, node, scheduler)
+                    {
+                        @Override
+                        public void handleData(List<Sample> samples)
+                        {
+                            node.removeAllChildren();
+                            UploadClientSortingUtils.sortSamplesByIdentifier(samples);
+                            for (Sample sample : samples)
+                            {
+                                FilterableMutableTreeNode category =
+                                        new FilterableMutableTreeNode(Identifier.create(sample));
+                                category.add(UiUtilities.createWaitingNode());
+                                node.add(category);
+                            }
+                        }
+                    };
+        clientModel.listSamplesWithNoExperiments(action);
+    }
 
-    protected abstract void expandNode(final DefaultMutableTreeNode node, Identifier identifier);
+    protected abstract void expandNode(final FilterableMutableTreeNode node, Identifier identifier);
 
     @Override
     public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException
     {
-        DefaultMutableTreeNode node =
-                (DefaultMutableTreeNode) event.getPath().getLastPathComponent();
+        FilterableMutableTreeNode node =
+                (FilterableMutableTreeNode) event.getPath().getLastPathComponent();
 
         if (node.isRoot())
         {
             throw new ExpandVetoException(event);
         }
         node.removeAllChildren();
-        node.add(new DefaultMutableTreeNode(UiUtilities.WAITING_NODE_LABEL));
-    }
-
-    protected void createNodes(FilterableMutableTreeNode top, List<Experiment> experiments)
-    {
-        UploadClientSortingUtils.sortExperimentsByIdentifier(experiments);
-        for (Experiment experiment : experiments)
-        {
-            DefaultMutableTreeNode category =
-                    new DefaultMutableTreeNode(Identifier.create(experiment), true);
-            category.add(new DefaultMutableTreeNode(UiUtilities.WAITING_NODE_LABEL));
-            top.add(category);
-        }
+        node.add(UiUtilities.createWaitingNode());
     }
 
     @Override
     protected void setDialogData()
     {
-        FilterableMutableTreeNode top = new FilterableMutableTreeNode("Experiments");
-
-        final List<String> projectIdentifiers = clientModel.getProjectIdentifiers();
-        List<Experiment> experiments =
-                clientModel.getOpenBISService().listExperimentsHavingSamplesForProjects(
-                        projectIdentifiers);
-        createNodes(top, experiments);
+        FilterableMutableTreeNode top = new FilterableMutableTreeNode(null);
+        FilterableMutableTreeNode experimentsTree = new FilterableMutableTreeNode(UiUtilities.ROOT_EXPERIMENTS);
+        experimentsTree.add(UiUtilities.createWaitingNode());
+        top.add(experimentsTree);
+        FilterableMutableTreeNode samplesTree = new FilterableMutableTreeNode(UiUtilities.ROOT_SAMPLES);
+        samplesTree.add(UiUtilities.createWaitingNode());
+        top.add(samplesTree);
 
         DefaultTreeModel treeModel = (DefaultTreeModel) tree.getModel();
         treeModel.setRoot(top);
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AsyncNodeAction.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AsyncNodeAction.java
index 1c613630ccc618c1b1f6c92be965bc038f16025f..60027e7e13a0f8306773b5b7d4e8d8fb8845b494 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AsyncNodeAction.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/AsyncNodeAction.java
@@ -16,11 +16,20 @@
 
 package ch.systemsx.cisd.openbis.dss.client.api.gui;
 
+import java.awt.BorderLayout;
 import java.awt.EventQueue;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
 import java.util.Timer;
 import java.util.TimerTask;
 
+import javax.swing.JButton;
+import javax.swing.JLabel;
 import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
 import javax.swing.JTree;
 import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.DefaultTreeModel;
@@ -61,9 +70,9 @@ public abstract class AsyncNodeAction<T> implements IAsyncAction<T>
     }
 
     @Override
-    public void handleException(Throwable throwable)
+    public void handleException(final Throwable throwable)
     {
-        JOptionPane.showMessageDialog(tree, throwable.toString());
+        UiUtilities.showException(tree, throwable);
     }
 
     public abstract void handleData(T data);
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPickerDialog.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPickerDialog.java
index b1718c5f45aba66c6a75cbf9ca03a265f45cf613..52128a080073aedcc7afa6449d89ddd948ae8561 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPickerDialog.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPickerDialog.java
@@ -19,14 +19,11 @@ package ch.systemsx.cisd.openbis.dss.client.api.gui;
 import java.util.List;
 
 import javax.swing.JFrame;
-import javax.swing.event.TreeExpansionEvent;
-import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.ExpandVetoException;
-import javax.swing.tree.TreeNode;
 
 import ch.systemsx.cisd.openbis.dss.client.api.gui.model.DataSetUploadClientModel;
 import ch.systemsx.cisd.openbis.dss.client.api.gui.model.Identifier;
 import ch.systemsx.cisd.openbis.dss.client.api.gui.model.SamplesDataSets;
+import ch.systemsx.cisd.openbis.dss.client.api.gui.tree.FilterableMutableTreeNode;
 import ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetOwnerType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
@@ -48,26 +45,7 @@ public class DataSetPickerDialog extends AbstractTreeEntityPickerDialog
     }
 
     @Override
-    public void treeWillExpand(final TreeExpansionEvent event) throws ExpandVetoException
-    {
-        final DefaultMutableTreeNode node =
-                (DefaultMutableTreeNode) event.getPath().getLastPathComponent();
-        // if top level, then finish
-        if (((TreeNode) node).getParent() == null)
-        {
-            return;
-        }
-        Object userObject = node.getUserObject();
-        if (userObject instanceof Identifier == false)
-        {
-            return;
-        }
-        Identifier identifier = (Identifier) userObject;
-        expandNode(node, identifier);
-    }
-
-    @Override
-    protected void expandNode(final DefaultMutableTreeNode node, Identifier identifier)
+    protected void expandNode(final FilterableMutableTreeNode node, Identifier identifier)
     {
         if (identifier.getOwnerType() != DataSetOwnerType.DATA_SET)
         {
@@ -82,22 +60,21 @@ public class DataSetPickerDialog extends AbstractTreeEntityPickerDialog
                                 node.removeAllChildren();
                                 if (dataSets.size() > 0)
                                 {
-                                    DefaultMutableTreeNode dataSetsNode =
-                                            new DefaultMutableTreeNode("Data Sets");
+                                    FilterableMutableTreeNode dataSetsNode =
+                                            new FilterableMutableTreeNode(UiUtilities.DATA_SETS);
                                     node.add(dataSetsNode);
                                     for (DataSet dataSet : dataSets)
                                     {
-                                        dataSetsNode.add(new DefaultMutableTreeNode(Identifier
+                                        dataSetsNode.add(new FilterableMutableTreeNode(Identifier
                                                 .create(dataSet)));
                                     }
                                 }
 
                                 for (Sample s : samplesDataSets.getSamples())
                                 {
-                                    DefaultMutableTreeNode sampleNode =
-                                            new DefaultMutableTreeNode(Identifier.create(s));
-                                    sampleNode.add(new DefaultMutableTreeNode(
-                                            UiUtilities.WAITING_NODE_LABEL));
+                                    FilterableMutableTreeNode sampleNode =
+                                            new FilterableMutableTreeNode(Identifier.create(s));
+                                    sampleNode.add(UiUtilities.createWaitingNode());
                                     node.add(sampleNode);
                                 }
                             }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetUploadClient.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetUploadClient.java
index ad234da86a5a5d7031ac41918f598b3488907267..eb12cd64169b341049c9076f04c15571120d02b9 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetUploadClient.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetUploadClient.java
@@ -154,13 +154,7 @@ public class DataSetUploadClient extends AbstractSwingGUI
                         prefs.flush();
                     } catch (Exception ex)
                     {
-                        final JFrame frame = new JFrame(TITLE);
-                        String message = ex.getMessage();
-                        if (null == message || message.length() == 0)
-                        {
-                            message = ex.toString();
-                        }
-                        JOptionPane.showMessageDialog(frame, message, "Error", JOptionPane.ERROR_MESSAGE);
+                        UiUtilities.showException(null, ex);
                     }
 
                 }
@@ -187,14 +181,7 @@ public class DataSetUploadClient extends AbstractSwingGUI
             newMe.show();
         } catch (RuntimeException ex)
         {
-            final JFrame frame = new JFrame(TITLE);
-            frame.setVisible(true);
-            String message = ex.getMessage();
-            if (null == message || message.length() == 0)
-            {
-                message = ex.toString();
-            }
-            JOptionPane.showMessageDialog(frame, message, "Error", JOptionPane.ERROR_MESSAGE);
+            UiUtilities.showException(null, ex);
             System.exit(1);
         }
     }
@@ -210,14 +197,7 @@ public class DataSetUploadClient extends AbstractSwingGUI
             newMe.show();
         } catch (RuntimeException ex)
         {
-            final JFrame frame = new JFrame(TITLE);
-            frame.setVisible(true);
-            String message = ex.getMessage();
-            if (null == message || message.length() == 0)
-            {
-                message = ex.toString();
-            }
-            JOptionPane.showMessageDialog(frame, message, "Error", JOptionPane.ERROR_MESSAGE);
+            UiUtilities.showException(null, ex);
             System.exit(1);
         }
     }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/SamplePickerDialog.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/SamplePickerDialog.java
index 0f73a69bf2a045fa43c421fde576490a67d94a41..ade5d7ac7a95d389505fb627323a20137fcd8e8c 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/SamplePickerDialog.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/SamplePickerDialog.java
@@ -19,10 +19,10 @@ package ch.systemsx.cisd.openbis.dss.client.api.gui;
 import java.util.List;
 
 import javax.swing.JFrame;
-import javax.swing.tree.DefaultMutableTreeNode;
 
 import ch.systemsx.cisd.openbis.dss.client.api.gui.model.DataSetUploadClientModel;
 import ch.systemsx.cisd.openbis.dss.client.api.gui.model.Identifier;
+import ch.systemsx.cisd.openbis.dss.client.api.gui.tree.FilterableMutableTreeNode;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetOwnerType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 
@@ -44,7 +44,7 @@ public class SamplePickerDialog extends AbstractTreeEntityPickerDialog
     }
 
     @Override
-    protected void expandNode(final DefaultMutableTreeNode node, Identifier identifier)
+    protected void expandNode(final FilterableMutableTreeNode node, Identifier identifier)
     {
         AsyncNodeAction<List<Sample>> action =
                 new AsyncNodeAction<List<Sample>>(tree, node, scheduler)
@@ -55,10 +55,9 @@ public class SamplePickerDialog extends AbstractTreeEntityPickerDialog
                             node.removeAllChildren();
                             for (Sample s : samples)
                             {
-                                DefaultMutableTreeNode sampleNode =
-                                        new DefaultMutableTreeNode(Identifier.create(s));
-                                sampleNode.add(new DefaultMutableTreeNode(
-                                        UiUtilities.WAITING_NODE_LABEL));
+                                FilterableMutableTreeNode sampleNode =
+                                        new FilterableMutableTreeNode(Identifier.create(s));
+                                sampleNode.add(UiUtilities.createWaitingNode());
                                 node.add(sampleNode);
                             }
                         }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/UiUtilities.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/UiUtilities.java
index d4696e15d1aec55423cc6d9d8f1b9109856219c8..03cfa90eab2fcbb509d36692c89b22fe8a1de4c5 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/UiUtilities.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/UiUtilities.java
@@ -16,11 +16,26 @@
 
 package ch.systemsx.cisd.openbis.dss.client.api.gui;
 
+import java.awt.BorderLayout;
 import java.awt.Color;
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.regex.Pattern;
 
+import javax.swing.JButton;
 import javax.swing.JComponent;
 import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.SwingUtilities;
 
+import ch.systemsx.cisd.openbis.dss.client.api.gui.model.Identifier;
+import ch.systemsx.cisd.openbis.dss.client.api.gui.tree.FilterableMutableTreeNode;
+import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetOwnerType;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation.ValidationError;
 
 /**
@@ -50,5 +65,74 @@ public class UiUtilities
         }
     }
 
-    static final String WAITING_NODE_LABEL = "Loading data ...";
+    static final String ROOT_SAMPLES = "Samples";
+    static final String ROOT_EXPERIMENTS = "Experiments";
+    static final String DATA_SETS = "Data Sets";
+    private static final String WAITING_NODE_LABEL = "Loading data ...";
+    
+    private static final String[] SPECIAL_NODES = {ROOT_EXPERIMENTS, ROOT_SAMPLES, DATA_SETS, WAITING_NODE_LABEL};
+    
+    public static boolean isMatchingNode(Object node, Pattern pattern)
+    {
+        for (String specialNode : SPECIAL_NODES)
+        {
+            if (equals(specialNode, node))
+            {
+                return true;
+            }
+        }
+        if (node instanceof FilterableMutableTreeNode)
+        {
+            Object userObject = ((FilterableMutableTreeNode) node).getUserObject();
+            if (userObject instanceof Identifier)
+            {
+                if (((Identifier) userObject).getOwnerType() == DataSetOwnerType.DATA_SET)
+                {
+                    return true;
+                }
+            }
+        }
+        return pattern.matcher(node.toString()).find();
+    }
+    
+    private static boolean equals(String label, Object object)
+    {
+        return object.toString().equals(label);
+    }
+    
+    public static FilterableMutableTreeNode createWaitingNode()
+    {
+        return new FilterableMutableTreeNode(UiUtilities.WAITING_NODE_LABEL);
+    }
+    
+    public static void showException(Component parentComponent, final Throwable throwable)
+    {
+        showMessageAndException(parentComponent, throwable, throwable.toString(), "Error");
+    }
+
+    public static void showMessageAndException(Component parentComponent, final Throwable throwable, String message, String title)
+    {
+        final JPanel panel = new JPanel(new BorderLayout(20, 5));
+        panel.add(new JLabel(message), BorderLayout.WEST);
+        JButton button = new JButton("Show Details");
+        button.addActionListener(new ActionListener()
+            {
+                @Override
+                public void actionPerformed(ActionEvent e)
+                {
+                    panel.removeAll();
+                    JTextArea textArea = new JTextArea(20, 20);
+                    textArea.setEditable(false);
+                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+                    throwable.printStackTrace(new PrintStream(byteArrayOutputStream, true));
+                    textArea.setText(byteArrayOutputStream.toString());
+                    panel.add(textArea, BorderLayout.CENTER);
+                    panel.validate();
+                    SwingUtilities.getWindowAncestor(panel).pack();
+                }
+            });
+        panel.add(button, BorderLayout.EAST);
+        JOptionPane.showMessageDialog(parentComponent, panel, title, JOptionPane.ERROR_MESSAGE);
+    }
+
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/model/DataSetUploadClientModel.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/model/DataSetUploadClientModel.java
index dfcdd86a641cf9bc45772bb03224c816257a2a99..f02564db000ffc35d046038510a3fd2abfa37d69 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/model/DataSetUploadClientModel.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/model/DataSetUploadClientModel.java
@@ -23,10 +23,13 @@ import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.search.SampleSearchCriteria;
 import ch.systemsx.cisd.base.namedthread.NamingThreadPoolExecutor;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.io.TransmissionSpeedCalculator;
@@ -34,6 +37,7 @@ import ch.systemsx.cisd.common.utilities.ITimeProvider;
 import ch.systemsx.cisd.openbis.dss.client.api.gui.model.DataSetUploadClientModel.NewDataSetInfo.Status;
 import ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet;
 import ch.systemsx.cisd.openbis.dss.client.api.v1.IOpenbisServiceFacade;
+import ch.systemsx.cisd.openbis.dss.client.api.v3.IOpenbisServiceFacadeV3;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTOBuilder;
@@ -42,6 +46,7 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation.ValidationE
 import ch.systemsx.cisd.openbis.generic.shared.ResourceNames;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSetTypeFilter;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.EntityRegistrationDetails;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.NewVocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
@@ -69,6 +74,8 @@ public class DataSetUploadClientModel
 
     private final IOpenbisServiceFacade openBISService;
 
+    private final IOpenbisServiceFacadeV3 serviceFacadeV3;
+    
     private final ITimeProvider timeProvider;
 
     private List<DataSetType> dataSetTypes;
@@ -104,6 +111,7 @@ public class DataSetUploadClientModel
     public DataSetUploadClientModel(DssCommunicationState commState, ITimeProvider timeProvider, IUserNotifier userNotifier)
     {
         this.openBISService = commState.getOpenBISService();
+        serviceFacadeV3 = commState.getServiceFacadeV3();
         this.timeProvider = timeProvider;
         this.userNotifier = userNotifier;
 
@@ -327,41 +335,108 @@ public class DataSetUploadClientModel
     {
         return !openBISService.getSamples(Arrays.asList(identifier)).isEmpty();
     }
-
-    public void listSamples(final Identifier identifier,
-            final IAsyncAction<List<Sample>> action)
+    
+    public void listExperiments(IAsyncAction<List<Experiment>> action)
     {
-        execute(new Runnable()
+        doAsync(new Callable<List<Experiment>>()
             {
                 @Override
-                public void run()
+                public List<Experiment> call() throws Exception
                 {
-                    try
+                    return getOpenBISService().listExperimentsHavingSamplesForProjects(
+                            projectIdentifiers);
+                }
+            }, action);
+    }
+    
+    public void listSamplesWithNoExperiments(IAsyncAction<List<Sample>> action)
+    {
+        doAsync(new Callable<List<Sample>>()
+            {
+                @Override
+                public List<Sample> call() throws Exception
+                {
+                    SampleSearchCriteria criteria = new SampleSearchCriteria();
+                    criteria.withoutExperiment();
+                    criteria.withType().withListable().setListable(true);
+                    List<Sample> result = new ArrayList<>();
+                    for (ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample sample : 
+                            serviceFacadeV3.searchSamples(criteria, new SampleFetchOptions()).getObjects())
                     {
-                        List<Sample> samples = new ArrayList<Sample>();
-                        String permId = identifier.getPermId();
-                        switch (identifier.getOwnerType())
-                        {
-                            case EXPERIMENT:
-                                loadListableSamples(samples, permId);
-                                break;
-                            case SAMPLE:
-                                loadSamplesLinkedToAnExperiment(samples, permId);
-                                break;
-                            default:
-                        }
-                        UploadClientSortingUtils.sortSamplesByIdentifier(samples);
-                        action.performAction(samples);
-                    } catch (Throwable throwable)
+                        Sample.SampleInitializer initializer = new Sample.SampleInitializer();
+                        initializer.setIdentifier(sample.getIdentifier().getIdentifier());
+                        initializer.setPermId(sample.getPermId().getPermId());
+                        initializer.setId(-1l); // dummy value for validation
+                        initializer.setCode(sample.getCode());
+                        initializer.setSampleTypeCode("dummy"); // dummy value for validation
+                        initializer.setSampleTypeId(-1l); // dummy value for validation
+                        EntityRegistrationDetails.EntityRegistrationDetailsInitializer detailsInitializer 
+                                = new EntityRegistrationDetails.EntityRegistrationDetailsInitializer();
+                        initializer.setRegistrationDetails(new EntityRegistrationDetails(detailsInitializer));
+                        result.add(new Sample(initializer));
+                    }
+                    return result;
+                }
+            }, action);
+    }
+
+    public void listSamples(final Identifier identifier, final IAsyncAction<List<Sample>> action)
+    {
+        doAsync(new Callable<List<Sample>>()
+            {
+                @Override
+                public List<Sample> call() throws Exception
+                {
+                    List<Sample> samples = new ArrayList<Sample>();
+                    String permId = identifier.getPermId();
+                    switch (identifier.getOwnerType())
                     {
-                        action.handleException(throwable);
+                        case EXPERIMENT:
+                            loadListableSamples(samples, permId);
+                            break;
+                        case SAMPLE:
+                            loadSamplesLinkedToAnExperiment(samples, permId);
+                            break;
+                        default:
                     }
+                    UploadClientSortingUtils.sortSamplesByIdentifier(samples);
+                    return samples;
                 }
-            });
+            }, action);
     }
 
     public void listSamplesDataSets(final Identifier identifier, final IAsyncAction<SamplesDataSets> action)
     {
+        doAsync(new Callable<SamplesDataSets>()
+            {
+                @Override
+                public SamplesDataSets call() throws Exception
+                {
+                    List<Sample> samples = new ArrayList<Sample>();
+                    List<DataSet> dataSets = new ArrayList<DataSet>();
+                    String permId = identifier.getPermId();
+                    switch (identifier.getOwnerType())
+                    {
+                        case EXPERIMENT:
+                            loadListableSamples(samples, permId);
+                            dataSets.addAll(openBISService.listDataSetsForExperiment(permId));
+                            break;
+                        case SAMPLE:
+                            loadSamplesLinkedToAnExperiment(samples, permId);
+                            dataSets.addAll(openBISService.listDataSetsForSample(permId));
+                            break;
+                        default:
+                    }
+                    UploadClientSortingUtils.sortSamplesByIdentifier(samples);
+                    UploadClientSortingUtils.sortDataSetsByCode(dataSets);
+                    return new SamplesDataSets(samples, dataSets);
+                }
+            }, action);
+    }
+    
+    private <T> void doAsync(final Callable<T> action, final IAsyncAction<T> subsequentAction)
+    {
+
         execute(new Runnable()
             {
                 @Override
@@ -369,27 +444,10 @@ public class DataSetUploadClientModel
                 {
                     try
                     {
-                        List<Sample> samples = new ArrayList<Sample>();
-                        List<DataSet> dataSets = new ArrayList<DataSet>();
-                        String permId = identifier.getPermId();
-                        switch (identifier.getOwnerType())
-                        {
-                            case EXPERIMENT:
-                                loadListableSamples(samples, permId);
-                                dataSets.addAll(openBISService.listDataSetsForExperiment(permId));
-                                break;
-                            case SAMPLE:
-                                loadSamplesLinkedToAnExperiment(samples, permId);
-                                dataSets.addAll(openBISService.listDataSetsForSample(permId));
-                                break;
-                            default:
-                        }
-                        UploadClientSortingUtils.sortSamplesByIdentifier(samples);
-                        UploadClientSortingUtils.sortDataSetsByCode(dataSets);
-                        action.performAction(new SamplesDataSets(samples, dataSets));
+                        subsequentAction.performAction(action.call());
                     } catch (Throwable throwable)
                     {
-                        action.handleException(throwable);
+                        subsequentAction.handleException(throwable);
                     }
                 }
             });
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/model/DssCommunicationState.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/model/DssCommunicationState.java
index b7eb5db0614996eabc33daa58c5c52eb5e79c19a..b57f568928125885e8c1a8f2276e8d74f20e9e5d 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/model/DssCommunicationState.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/model/DssCommunicationState.java
@@ -23,11 +23,15 @@ import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.dss.client.api.v1.IOpenbisServiceFacade;
 import ch.systemsx.cisd.openbis.dss.client.api.v1.OpenbisServiceFacadeFactory;
+import ch.systemsx.cisd.openbis.dss.client.api.v3.IOpenbisServiceFacadeV3;
+import ch.systemsx.cisd.openbis.dss.client.api.v3.OpenbisServiceFacadeV3;
 
 public class DssCommunicationState
 {
     private final IOpenbisServiceFacade openBISService;
 
+    private final IOpenbisServiceFacadeV3 serviceFacadeV3;
+    
     private final boolean logoutOnClose;
 
     private static final long CONNECTION_TIMEOUT_MILLIS = 60 * DateUtils.MILLIS_PER_SECOND;
@@ -48,28 +52,27 @@ public class DssCommunicationState
         {
             case 2:
                 String sessionToken = args[1];
-                openBISService =
-                        OpenbisServiceFacadeFactory.tryCreate(sessionToken, openBisUrl,
-                                CONNECTION_TIMEOUT_MILLIS);
-                if (null == openBISService)
+                serviceFacadeV3 = OpenbisServiceFacadeV3.tryCreate(sessionToken, openBisUrl, CONNECTION_TIMEOUT_MILLIS);
+                if (null == serviceFacadeV3)
                 {
                     throw new ConfigurationFailureException(
                             "The openBIS File Upload Client was improperly configured -- the session token is not valid. Please talk to the openBIS administrator.");
                 }
+                openBISService =  OpenbisServiceFacadeFactory.tryCreate(sessionToken, openBisUrl, CONNECTION_TIMEOUT_MILLIS);
                 // Don't logout -- the user wants to keep his/her session token alive.
                 logoutOnClose = false;
                 break;
             default:
                 String userName = args[1];
                 String passwd = args[2];
-                openBISService =
-                        OpenbisServiceFacadeFactory.tryCreate(userName, passwd, openBisUrl,
-                                CONNECTION_TIMEOUT_MILLIS);
-                if (null == openBISService)
+                serviceFacadeV3 = OpenbisServiceFacadeV3.tryCreate(userName, passwd, openBisUrl, CONNECTION_TIMEOUT_MILLIS);
+                if (null == serviceFacadeV3)
                 {
                     throw new ConfigurationFailureException(
                             "The user name / password combination is incorrect.");
                 }
+                String token = serviceFacadeV3.getSessionToken();
+                openBISService =  OpenbisServiceFacadeFactory.tryCreate(token, openBisUrl, CONNECTION_TIMEOUT_MILLIS);
                 // Do logout on close
                 logoutOnClose = true;
         }
@@ -80,6 +83,11 @@ public class DssCommunicationState
         return openBISService;
     }
 
+    public IOpenbisServiceFacadeV3 getServiceFacadeV3()
+    {
+        return serviceFacadeV3;
+    }
+
     public boolean isLogoutOnClose()
     {
         return logoutOnClose;
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/tree/FilterableMutableTreeNode.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/tree/FilterableMutableTreeNode.java
index 5d4219b75102996901a94a6f336fa1809493ee4e..d75b50216882d03a721390ec4da699ae3605edb5 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/tree/FilterableMutableTreeNode.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/tree/FilterableMutableTreeNode.java
@@ -24,6 +24,8 @@ import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.MutableTreeNode;
 import javax.swing.tree.TreeNode;
 
+import ch.systemsx.cisd.openbis.dss.client.api.gui.UiUtilities;
+
 /**
  * @author Pawel Glyzewski
  */
@@ -65,7 +67,7 @@ public class FilterableMutableTreeNode extends DefaultMutableTreeNode
             while (enumeration.hasMoreElements())
             {
                 final Object tmp = enumeration.nextElement();
-                if (pattern.matcher(tmp.toString()).find())
+                if (UiUtilities.isMatchingNode(tmp, pattern))
                 {
                     next = tmp;
                     break;
@@ -95,6 +97,7 @@ public class FilterableMutableTreeNode extends DefaultMutableTreeNode
     private synchronized void setPattern(Pattern pattern)
     {
         this.pattern = pattern;
+        filter(pattern);
     }
 
     private synchronized ArrayList<Object> getFiltered()
@@ -120,11 +123,13 @@ public class FilterableMutableTreeNode extends DefaultMutableTreeNode
             pattern = Pattern.compile(".*");
         }
         setPattern(pattern);
-
-        filter(pattern);
+        for (int i = 0, n = getChildCount(); i < n; i++)
+        {
+            ((FilterableMutableTreeNode) getChildAt(i)).setPattern(pattern);
+        }
     }
 
-    public void filter(@SuppressWarnings("hiding") Pattern pattern)
+    private void filter(@SuppressWarnings("hiding") Pattern pattern)
     {
         @SuppressWarnings("hiding")
         ArrayList<Object> filtered = new ArrayList<Object>();
@@ -133,14 +138,14 @@ public class FilterableMutableTreeNode extends DefaultMutableTreeNode
         while (enumeration.hasMoreElements())
         {
             Object o = enumeration.nextElement();
-            if (pattern.matcher(o.toString()).find())
+            if (UiUtilities.isMatchingNode(o, pattern))
             {
                 filtered.add(o);
             }
         }
         setFiltered(filtered);
     }
-
+    
     @Override
     public TreeNode getChildAt(int childIndex)
     {
@@ -190,11 +195,25 @@ public class FilterableMutableTreeNode extends DefaultMutableTreeNode
     {
         return new FilteredEnumerationWrapper(super.children(), getPattern());
     }
-
+    
     @Override
     public void add(MutableTreeNode o)
     {
         super.add(o);
+        if (o instanceof FilterableMutableTreeNode)
+        {
+            FilterableMutableTreeNode filterableMutableTreeNode = (FilterableMutableTreeNode) o;
+            filterableMutableTreeNode.setPattern(getPattern());
+        }
+        filter(getPattern());
+    }
+
+    @Override
+    public void removeAllChildren()
+    {
+        super.removeAllChildren();
         filter(getPattern());
     }
+    
+    
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v3/IOpenbisServiceFacadeV3.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v3/IOpenbisServiceFacadeV3.java
new file mode 100644
index 0000000000000000000000000000000000000000..394d0df88ffeaee455105e08cac8e2810f19a043
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v3/IOpenbisServiceFacadeV3.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 ETH Zuerich, SIS
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.client.api.v3;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.search.SampleSearchCriteria;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public interface IOpenbisServiceFacadeV3
+{
+    public String getSessionToken();
+    
+    public SearchResult<Sample> searchSamples(SampleSearchCriteria searchCriteria, SampleFetchOptions fetchOptions);
+
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v3/OpenbisServiceFacadeV3.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v3/OpenbisServiceFacadeV3.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b25d21161a0652d6b8e500ea5274f498b376481
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v3/OpenbisServiceFacadeV3.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2017 ETH Zuerich, SIS
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.client.api.v3;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.search.SampleSearchCriteria;
+import ch.systemsx.cisd.common.api.retry.RetryCaller;
+import ch.systemsx.cisd.common.api.retry.RetryProxyFactory;
+import ch.systemsx.cisd.openbis.common.api.client.ServiceFinder;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+public class OpenbisServiceFacadeV3 implements IOpenbisServiceFacadeV3
+{
+    public static IOpenbisServiceFacadeV3 tryCreate(final String username, final String password,
+            final String openbisUrl, final long timeoutInMillis)
+    {
+        RetryCaller<IOpenbisServiceFacadeV3, RuntimeException> caller =
+                new RetryCaller<IOpenbisServiceFacadeV3, RuntimeException>()
+                    {
+                        @Override
+                        protected IOpenbisServiceFacadeV3 call()
+                        {
+                            IApplicationServerApi service = createService(openbisUrl, timeoutInMillis);
+                            String sessionToken = service.login(username, password);
+                            OpenbisServiceFacadeV3 facade = new OpenbisServiceFacadeV3(sessionToken, service);
+                            return RetryProxyFactory.createProxy(facade);
+                        }
+                    };
+        return caller.callWithRetry();
+    }
+
+    public static IOpenbisServiceFacadeV3 tryCreate(final String sessionToken,
+            final String openbisUrl, final long timeoutInMillis)
+    {
+        RetryCaller<IOpenbisServiceFacadeV3, RuntimeException> caller =
+                new RetryCaller<IOpenbisServiceFacadeV3, RuntimeException>()
+        {
+            @Override
+            protected IOpenbisServiceFacadeV3 call()
+            {
+                IApplicationServerApi service = createService(openbisUrl, timeoutInMillis);
+                OpenbisServiceFacadeV3 facade = new OpenbisServiceFacadeV3(sessionToken, service);
+                return RetryProxyFactory.createProxy(facade);
+            }
+        };
+        return caller.callWithRetry();
+    }
+    
+    private static IApplicationServerApi createService(String openbisUrl, long timeoutInMillis)
+    {
+        ServiceFinder serviceFinder = new ServiceFinder("openbis", IApplicationServerApi.SERVICE_URL);
+        return serviceFinder.createService(IApplicationServerApi.class, openbisUrl, timeoutInMillis);
+    }
+    
+    private String sessionToken;
+    private IApplicationServerApi service;
+    
+    private OpenbisServiceFacadeV3(String sessionToken, IApplicationServerApi service)
+    {
+        this.sessionToken = sessionToken;
+        this.service = service;
+    }
+
+    @Override
+    public String getSessionToken()
+    {
+        return sessionToken;
+    }
+
+    @Override
+    public SearchResult<Sample> searchSamples(SampleSearchCriteria searchCriteria, SampleFetchOptions fetchOptions)
+    {
+        return service.searchSamples(sessionToken, searchCriteria, fetchOptions);
+    }
+}