diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractClient.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ff7c2a37e73b07ad3bdc5fef60bedf7ccf6281b
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractClient.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.client.api.cli;
+
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.remoting.RemoteAccessException;
+import org.springframework.remoting.RemoteConnectFailureException;
+
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.common.exceptions.InvalidSessionException;
+import ch.systemsx.cisd.common.exceptions.MasqueradingException;
+import ch.systemsx.cisd.common.exceptions.SystemExitException;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.common.utilities.IExitHandler;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class AbstractClient
+{
+
+    protected static void disableLogging()
+    {
+        System.setProperty("org.apache.commons.logging.Log",
+                "org.apache.commons.logging.impl.NoOpLog");
+    }
+
+    protected static void enableDebugLogging()
+    {
+        // Log protocol information -- for debugging only
+        System.setProperty("org.apache.commons.logging.Log",
+                "org.apache.commons.logging.impl.SimpleLog");
+        System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
+        System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire.header",
+                "debug");
+        System.setProperty(
+                "org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "debug");
+    }
+
+    protected final IExitHandler exitHandler;
+
+    protected final ICommandFactory commandFactory;
+
+    /**
+     *
+     *
+     */
+    public AbstractClient(IExitHandler exitHandler, ICommandFactory commandFactory)
+    {
+        this.exitHandler = exitHandler;
+        this.commandFactory = commandFactory;
+    }
+
+    protected void runWithArgs(String[] args)
+    {
+        ICommand command = getCommandOrDie(args);
+    
+        int resultCode = 0;
+        try
+        {
+            // Strip the name of the command and pass the rest of the arguments to the command
+            String[] cmdArgs = new String[args.length - 1];
+            Arrays.asList(args).subList(1, args.length).toArray(cmdArgs);
+            resultCode = command.execute(cmdArgs);
+        } catch (final InvalidSessionException ex)
+        {
+            System.err
+                    .println("Your session is no longer valid. Please login again. [server said: '"
+                            + ex.getMessage() + "']");
+            resultCode = 1;
+        } catch (final UserFailureException ex)
+        {
+            System.err.println();
+            System.err.println(ex.getMessage());
+            resultCode = 1;
+        } catch (final EnvironmentFailureException ex)
+        {
+            System.err.println();
+            System.err.println(ex.getMessage() + " (environment failure)");
+            resultCode = 1;
+        } catch (final RemoteConnectFailureException ex)
+        {
+            System.err.println();
+            System.err.println("Remote server cannot be reached (environment failure)");
+            resultCode = 1;
+        } catch (final RemoteAccessException ex)
+        {
+            System.err.println();
+            final Throwable cause = ex.getCause();
+            if (cause != null)
+            {
+                if (cause instanceof UnknownHostException)
+                {
+                    System.err.println(String.format(
+                            "Given host '%s' can not be reached  (environment failure)",
+                            cause.getMessage()));
+                } else if (cause instanceof IllegalArgumentException)
+                {
+                    System.err.println(cause.getMessage());
+                } else if (cause instanceof SSLHandshakeException)
+                {
+                    final String property = "javax.net.ssl.trustStore";
+                    System.err.println(String.format(
+                            "Validation of SSL certificate failed [%s=%s] (configuration failure)",
+                            property, StringUtils.defaultString(System.getProperty(property))));
+                } else
+                {
+                    ex.printStackTrace();
+                }
+            } else
+            {
+                ex.printStackTrace();
+            }
+            resultCode = 1;
+        } catch (final SystemExitException e)
+        {
+            resultCode = 1;
+        } catch (MasqueradingException e)
+        {
+            System.err.println(e);
+            resultCode = 1;
+        } catch (IllegalArgumentException e)
+        {
+            System.err.println(e.getMessage());
+            resultCode = 1;
+        } catch (final Exception e)
+        {
+            System.err.println();
+            e.printStackTrace();
+            resultCode = 1;
+        }
+    
+        exitHandler.exit(resultCode);
+    }
+
+    private ICommand getCommandOrDie(String[] args)
+    {
+        // No arguments supplied -- print help
+        if (args.length < 1)
+        {
+            ICommand help = commandFactory.getHelpCommand();
+            help.printUsage(System.err);
+            exitHandler.exit(1);
+    
+            // Never gets here
+            return null;
+        }
+    
+        String commandName = args[0];
+        ICommand command = commandFactory.tryCommandForName(commandName);
+        if (null == command)
+        {
+            ICommand help = commandFactory.getHelpCommand();
+            help.printUsage(System.err);
+            exitHandler.exit(1);
+    
+            // Never gets here
+            return null;
+        }
+        return command;
+    }
+
+}
\ No newline at end of file
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractCommand.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractCommand.java
index 38558fac4cb99068d8870484634684e914b5fd07..9e5335c74ee7923053127ce6fc6f47c81fdeb5a8 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractCommand.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractCommand.java
@@ -16,55 +16,25 @@
 
 package ch.systemsx.cisd.openbis.dss.client.api.cli;
 
-import java.io.PrintStream;
-
 import ch.systemsx.cisd.args4j.CmdLineParser;
-import ch.systemsx.cisd.args4j.ExampleMode;
-import ch.systemsx.cisd.openbis.dss.client.api.v1.DssComponentFactory;
-import ch.systemsx.cisd.openbis.dss.client.api.v1.IDssComponent;
 
 /**
- * Superclass for dss command-line client commands.
- * <p>
- * Provides services used by most subclasses. In order for the AbstractCommand to work, subclasses
- * must do the following:
- * <ul>
- * <li>Set the parser ivar in their constructor
- * </ul>
- * 
  * @author Chandrasekhar Ramakrishnan
  */
-abstract class AbstractCommand implements ICommand
+public class AbstractCommand<T extends GlobalArguments>
 {
-    // The parser for command-line arguments; set by subclasses in the constructor.
-    protected CmdLineParser parser;
+    protected final T arguments;
 
-    /**
-     * Print usage information about the command.
-     */
-    public void printUsage(PrintStream out)
-    {
-        out.println(getUsagePrefixString() + " [options] <data set code> <path>");
-        parser.printUsage(out);
-        out.println("  Example : " + getCommandCallString() + " "
-                + parser.printExample(ExampleMode.ALL) + " <data set code> <path>");
-    }
+    protected final CmdLineParser parser;
 
     /**
-     * How is this command invoked from the command line? This is the program call string + command
-     * name
+     *
+     *
      */
-    protected String getCommandCallString()
+    public AbstractCommand(T arguments)
     {
-        return getProgramCallString() + " " + getName();
-    }
-
-    /**
-     * Used for displaying help.
-     */
-    protected String getUsagePrefixString()
-    {
-        return "usage: " + getCommandCallString();
+        this.arguments = arguments;
+        parser = new CmdLineParser(arguments);
     }
 
     /**
@@ -75,19 +45,4 @@ abstract class AbstractCommand implements ICommand
         return "dss_client.sh";
     }
 
-    /**
-     * Creates the DSS Component object and logs into the server.
-     */
-    protected IDssComponent login(GlobalArguments arguments)
-    {
-        IDssComponent component =
-                DssComponentFactory.tryCreate(arguments.getUsername(), arguments.getPassword(),
-                        arguments.getServerBaseUrl());
-        if (null == component)
-        {
-            System.out.println("Username / password is invalid");
-        }
-        return component;
-    }
-
-}
+}
\ No newline at end of file
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractCommandFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractCommandFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..f69a2a4ea757e1a95e85240a3f9c93fcc13f9333
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractCommandFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.client.api.cli;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+abstract public class AbstractCommandFactory implements ICommandFactory
+{
+
+    /**
+     *
+     *
+     */
+    public AbstractCommandFactory()
+    {
+        super();
+    }
+
+    public ICommand tryCommandForName(String name)
+    {
+        // special handling of "-h" and "--help"
+        if ("-h".equals(name) || "--help".equals(name))
+        {
+            return getHelpCommand();
+        }
+
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractDataSetExecutor.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractDataSetExecutor.java
index b887564f5a325a1f13f27f6b4c7c0fc92b301c0c..7740bcd939cc92a6a534916a8fd98e746887192c 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractDataSetExecutor.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractDataSetExecutor.java
@@ -21,19 +21,16 @@ import ch.systemsx.cisd.openbis.dss.client.api.v1.IDssComponent;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO;
 
 /**
- * 
- *
  * @author Franz-Josef Elmer
  */
 abstract class AbstractDataSetExecutor<A extends DataSetArguments> extends AbstractExecutor<A>
 {
 
-    AbstractDataSetExecutor(A arguments, AbstractCommand command)
+    AbstractDataSetExecutor(A arguments, AbstractDssCommand<A> command)
     {
         super(arguments, command);
     }
 
-
     @Override
     protected final int doExecute(IDssComponent component)
     {
@@ -42,12 +39,12 @@ abstract class AbstractDataSetExecutor<A extends DataSetArguments> extends Abstr
         handle(fileInfos, dataSet);
         return 0;
     }
-    
+
     private FileInfoDssDTO[] getFileInfos(IDataSetDss dataSet)
     {
         return dataSet.listFiles(arguments.getRequestedPath(), arguments.isRecursive());
     }
 
     protected abstract void handle(FileInfoDssDTO[] fileInfos, IDataSetDss dataSet);
-    
+
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractDssCommand.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractDssCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c64cca32fd06694936c59dfd51aea8e5ad0f6be
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractDssCommand.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.client.api.cli;
+
+import java.io.PrintStream;
+
+import ch.systemsx.cisd.args4j.ExampleMode;
+import ch.systemsx.cisd.openbis.dss.client.api.v1.DssComponentFactory;
+import ch.systemsx.cisd.openbis.dss.client.api.v1.IDssComponent;
+
+/**
+ * Superclass for dss command-line client commands.
+ * <p>
+ * Provides services used by most subclasses. In order for the AbstractCommand to work, subclasses
+ * must do the following:
+ * <ul>
+ * <li>Set the parser ivar in their constructor
+ * </ul>
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+abstract class AbstractDssCommand<T extends GlobalArguments> extends AbstractCommand<T> implements
+        ICommand
+{
+    /**
+     * @param arguments
+     */
+    public AbstractDssCommand(T arguments)
+    {
+        super(arguments);
+    }
+
+    /**
+     * Print usage information about the command.
+     */
+    public void printUsage(PrintStream out)
+    {
+        out.println(getUsagePrefixString() + " [options] " + getRequiredArgumentsString());
+        parser.printUsage(out);
+        out.println("  Example : " + getCommandCallString() + " "
+                + parser.printExample(ExampleMode.ALL) + " " + getRequiredArgumentsString());
+    }
+
+    protected String getRequiredArgumentsString()
+    {
+        return "<data set code> <path>";
+    }
+
+    /**
+     * How is this command invoked from the command line? This is the program call string + command
+     * name
+     */
+    protected String getCommandCallString()
+    {
+        return getProgramCallString() + " " + getName();
+    }
+
+    /**
+     * Used for displaying help.
+     */
+    protected String getUsagePrefixString()
+    {
+        return "usage: " + getCommandCallString();
+    }
+
+    /**
+     * Creates the DSS Component object and logs into the server.
+     */
+    protected IDssComponent login(GlobalArguments args)
+    {
+        IDssComponent component =
+                DssComponentFactory.tryCreate(arguments.getUsername(), arguments.getPassword(),
+                        arguments.getServerBaseUrl());
+        if (null == component)
+        {
+            System.out.println("Username / password is invalid");
+        }
+        return component;
+    }
+
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractExecutor.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractExecutor.java
index 445c85fa3ecf8dfa7d4f86b24f170ba65ce5737b..c88fcd6a0bb318dc3f361897971ae632abf83bce 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractExecutor.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/AbstractExecutor.java
@@ -20,23 +20,23 @@ import ch.systemsx.cisd.args4j.CmdLineParser;
 import ch.systemsx.cisd.openbis.dss.client.api.v1.IDssComponent;
 
 /**
- * 
- *
  * @author Franz-Josef Elmer
  */
 abstract class AbstractExecutor<A extends GlobalArguments>
 {
     protected final A arguments;
+
     private final CmdLineParser parser;
-    private final AbstractCommand command;
 
-    AbstractExecutor(A arguments, AbstractCommand command)
+    private final AbstractDssCommand<A> command;
+
+    AbstractExecutor(A arguments, AbstractDssCommand<A> command)
     {
         this.arguments = arguments;
         this.command = command;
         parser = new CmdLineParser(arguments);
     }
-    
+
     final int execute(String[] args)
     {
         parser.parseArgument(args);
@@ -74,6 +74,6 @@ abstract class AbstractExecutor<A extends GlobalArguments>
         }
 
     }
-    
+
     protected abstract int doExecute(IDssComponent component);
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandFactory.java
index 350226fdd8ad36f41a3853d37d4808153a371a7f..bb4bbe2abe5a5fc1d204cf37b39feb91231eb48a 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandFactory.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandFactory.java
@@ -22,22 +22,20 @@ import java.util.List;
 /**
  * @author Chandrasekhar Ramakrishnan
  */
-class CommandFactory
+class CommandFactory extends AbstractCommandFactory
 {
     private static enum Command
     {
         LS, GET, HELP, PUT
     }
 
-    /**
-     * Find the command that matches the name.
-     */
-    ICommand tryCommandForName(String name)
+    @Override
+    public ICommand tryCommandForName(String name)
     {
-        // special handling of "-h" and "--help"
-        if ("-h".equals(name) || "--help".equals(name))
+        ICommand superCommandOrNull = super.tryCommandForName(name);
+        if (null != superCommandOrNull)
         {
-            return new CommandHelp(this);
+            return superCommandOrNull;
         }
 
         Command command;
@@ -46,7 +44,6 @@ class CommandFactory
             command = Command.valueOf(name.toUpperCase());
         } catch (IllegalArgumentException e)
         {
-
             return null;
         }
 
@@ -60,7 +57,7 @@ class CommandFactory
                 result = new CommandGet();
                 break;
             case HELP:
-                result = new CommandHelp(this);
+                result = getHelpCommand();
                 break;
             case PUT:
                 result = new CommandPut();
@@ -73,7 +70,12 @@ class CommandFactory
         return result;
     }
 
-    List<String> getKnownCommands()
+    public ICommand getHelpCommand()
+    {
+        return new CommandHelp(this);
+    }
+
+    public List<String> getKnownCommands()
     {
         String[] commands =
             { "ls", "get", "put" };
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandGet.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandGet.java
index bce8d56fe134ae435050a8514e6edc24e3511a2f..20a3a2b57c40e90fd4aca4fa9f16a6b0b01e2ab0 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandGet.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandGet.java
@@ -18,7 +18,6 @@ package ch.systemsx.cisd.openbis.dss.client.api.cli;
 
 import java.io.File;
 
-import ch.systemsx.cisd.args4j.CmdLineParser;
 import ch.systemsx.cisd.args4j.Option;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
@@ -31,9 +30,9 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO;
  * 
  * @author Chandrasekhar Ramakrishnan
  */
-class CommandGet extends AbstractCommand
+class CommandGet extends AbstractDssCommand<CommandGet.CommandGetArguments>
 {
-    private static class CommandGetArguments extends DataSetArguments
+    static class CommandGetArguments extends DataSetArguments
     {
         @Option(name = "l", longName = "link", usage = "Try to return a link to the data set in the DSS if possible, download the entire data set otherwise.")
         private boolean link = false;
@@ -81,7 +80,8 @@ class CommandGet extends AbstractCommand
 
     private static class CommandGetExecutor extends AbstractDataSetExecutor<CommandGetArguments>
     {
-        CommandGetExecutor(CommandGetArguments arguments, AbstractCommand command)
+        CommandGetExecutor(CommandGetArguments arguments,
+                AbstractDssCommand<CommandGetArguments> command)
         {
             super(arguments, command);
         }
@@ -144,12 +144,9 @@ class CommandGet extends AbstractCommand
         }
     }
 
-    private final CommandGetArguments arguments;
-
     CommandGet()
     {
-        arguments = new CommandGetArguments();
-        parser = new CmdLineParser(arguments);
+        super(new CommandGetArguments());
     }
 
     public int execute(String[] args) throws UserFailureException, EnvironmentFailureException
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandHelp.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandHelp.java
index 1abb4582e7c61a9fa1362b697e0d6af6059b8dba..51b1e51608c5ca78fd3ac993a96fb7bad5bb3bed 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandHelp.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandHelp.java
@@ -19,7 +19,6 @@ package ch.systemsx.cisd.openbis.dss.client.api.cli;
 import java.io.PrintStream;
 import java.util.List;
 
-import ch.systemsx.cisd.args4j.CmdLineParser;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 
@@ -28,21 +27,18 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException;
  * 
  * @author Chandrasekhar Ramakrishnan
  */
-class CommandHelp extends AbstractCommand
+class CommandHelp extends AbstractDssCommand<CommandHelp.CommandHelpArguments>
 {
-    private static class CommandHelpArguments extends GlobalArguments
+    static class CommandHelpArguments extends GlobalArguments
     {
 
     }
 
-    private final CommandHelpArguments arguments;
+    private final ICommandFactory commandFactory;
 
-    private final CommandFactory commandFactory;
-
-    CommandHelp(CommandFactory factory)
+    CommandHelp(ICommandFactory factory)
     {
-        arguments = new CommandHelpArguments();
-        parser = new CmdLineParser(arguments);
+        super(new CommandHelpArguments());
         this.commandFactory = factory;
     }
 
@@ -74,7 +70,8 @@ class CommandHelp extends AbstractCommand
     @Override
     public void printUsage(PrintStream out)
     {
-        out.println("usage: " + getProgramCallString() + " COMMAND [options...] <command arguments>");
+        out.println("usage: " + getProgramCallString()
+                + " COMMAND [options...] <command arguments>");
         List<String> commands = commandFactory.getKnownCommands();
         out.println("\nCommands:");
         for (String cmd : commands)
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandLs.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandLs.java
index c299fee86edae8539c3c766189235033fef9054a..e5f6b42afb0d70fff1c148f566fa822630e39c36 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandLs.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandLs.java
@@ -16,7 +16,6 @@
 
 package ch.systemsx.cisd.openbis.dss.client.api.cli;
 
-import ch.systemsx.cisd.args4j.CmdLineParser;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.dss.client.api.v1.IDataSetDss;
@@ -27,11 +26,11 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO;
  * 
  * @author Chandrasekhar Ramakrishnan
  */
-class CommandLs extends AbstractCommand
+class CommandLs extends AbstractDssCommand<DataSetArguments>
 {
     private static class CommandLsExecutor extends AbstractDataSetExecutor<DataSetArguments>
     {
-        CommandLsExecutor(DataSetArguments arguments, AbstractCommand command)
+        CommandLsExecutor(DataSetArguments arguments, AbstractDssCommand<DataSetArguments> command)
         {
             super(arguments, command);
         }
@@ -57,12 +56,9 @@ class CommandLs extends AbstractCommand
 
     }
 
-    private final DataSetArguments arguments;
-
     CommandLs()
     {
-        arguments = new DataSetArguments();
-        parser = new CmdLineParser(arguments);
+        super(new DataSetArguments());
     }
 
     public int execute(String[] args) throws UserFailureException, EnvironmentFailureException
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandPut.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandPut.java
index b31577595272126a7af1b9a1e474c51b5542941e..bac42ad4ac644c6b7f794fce76ed1c1ef02a6bdc 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandPut.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandPut.java
@@ -22,7 +22,6 @@ import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.HashMap;
 
-import ch.systemsx.cisd.args4j.CmdLineParser;
 import ch.systemsx.cisd.args4j.ExampleMode;
 import ch.systemsx.cisd.args4j.Option;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
@@ -40,9 +39,9 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetO
  * 
  * @author Chandrasekhar Ramakrishnan
  */
-class CommandPut extends AbstractCommand
+class CommandPut extends AbstractDssCommand<CommandPut.CommandPutArguments>
 {
-    private static class CommandPutArguments extends GlobalArguments
+    static class CommandPutArguments extends GlobalArguments
     {
         @Option(name = "t", longName = "type", usage = "Set the data set type")
         private String dataSetType;
@@ -128,7 +127,8 @@ class CommandPut extends AbstractCommand
     private static class CommandPutExecutor extends AbstractExecutor<CommandPutArguments>
     {
 
-        CommandPutExecutor(CommandPutArguments arguments, AbstractCommand command)
+        CommandPutExecutor(CommandPutArguments arguments,
+                AbstractDssCommand<CommandPutArguments> command)
         {
             super(arguments, command);
         }
@@ -213,12 +213,9 @@ class CommandPut extends AbstractCommand
         }
     }
 
-    private final CommandPutArguments arguments;
-
     CommandPut()
     {
-        arguments = new CommandPutArguments();
-        parser = new CmdLineParser(arguments);
+        super(new CommandPutArguments());
     }
 
     public int execute(String[] args) throws UserFailureException, EnvironmentFailureException
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/DssClient.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/DssClient.java
index 59fa88b1d21b36f1976c25a952faafa2fc333707..4cf7a6187ecba83de2048814dfc59d2565c57447 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/DssClient.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/DssClient.java
@@ -16,21 +16,9 @@
 
 package ch.systemsx.cisd.openbis.dss.client.api.cli;
 
-import java.net.UnknownHostException;
-import java.util.Arrays;
 
-import javax.net.ssl.SSLHandshakeException;
 
-import org.apache.commons.lang.StringUtils;
-import org.springframework.remoting.RemoteAccessException;
-import org.springframework.remoting.RemoteConnectFailureException;
 
-import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
-import ch.systemsx.cisd.common.exceptions.InvalidSessionException;
-import ch.systemsx.cisd.common.exceptions.MasqueradingException;
-import ch.systemsx.cisd.common.exceptions.SystemExitException;
-import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.common.utilities.IExitHandler;
 import ch.systemsx.cisd.common.utilities.SystemExit;
 
 /**
@@ -43,7 +31,7 @@ import ch.systemsx.cisd.common.utilities.SystemExit;
  * 
  * @author Chandrasekhar Ramakrishnan
  */
-public class DssClient
+public class DssClient extends AbstractClient
 {
     private final static boolean ENABLE_LOGGING = false;
 
@@ -56,141 +44,9 @@ public class DssClient
             disableLogging();
     }
 
-    private static void disableLogging()
-    {
-        System.setProperty("org.apache.commons.logging.Log",
-                "org.apache.commons.logging.impl.NoOpLog");
-    }
-
-    private static void enableDebugLogging()
-    {
-        // Log protocol information -- for debugging only
-        System.setProperty("org.apache.commons.logging.Log",
-                "org.apache.commons.logging.impl.SimpleLog");
-        System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
-        System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire.header",
-                "debug");
-        System.setProperty(
-                "org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "debug");
-    }
-
-    private CommandFactory commandFactory;
-
-    private final IExitHandler exitHandler;
-
     private DssClient()
     {
-        this.exitHandler = SystemExit.SYSTEM_EXIT;
-        this.commandFactory = new CommandFactory();
-    }
-
-    private void runWithArgs(String[] args)
-    {
-        ICommand command = getCommandOrDie(args);
-
-        int resultCode = 0;
-        try
-        {
-            // Strip the name of the command and pass the rest of the arguments to the command
-            String[] cmdArgs = new String[args.length - 1];
-            Arrays.asList(args).subList(1, args.length).toArray(cmdArgs);
-            resultCode = command.execute(cmdArgs);
-        } catch (final InvalidSessionException ex)
-        {
-            System.err
-                    .println("Your session is no longer valid. Please login again. [server said: '"
-                            + ex.getMessage() + "']");
-            resultCode = 1;
-        } catch (final UserFailureException ex)
-        {
-            System.err.println();
-            System.err.println(ex.getMessage());
-            resultCode = 1;
-        } catch (final EnvironmentFailureException ex)
-        {
-            System.err.println();
-            System.err.println(ex.getMessage() + " (environment failure)");
-            resultCode = 1;
-        } catch (final RemoteConnectFailureException ex)
-        {
-            System.err.println();
-            System.err.println("Remote server cannot be reached (environment failure)");
-            resultCode = 1;
-        } catch (final RemoteAccessException ex)
-        {
-            System.err.println();
-            final Throwable cause = ex.getCause();
-            if (cause != null)
-            {
-                if (cause instanceof UnknownHostException)
-                {
-                    System.err.println(String.format(
-                            "Given host '%s' can not be reached  (environment failure)", cause
-                                    .getMessage()));
-                } else if (cause instanceof IllegalArgumentException)
-                {
-                    System.err.println(cause.getMessage());
-                } else if (cause instanceof SSLHandshakeException)
-                {
-                    final String property = "javax.net.ssl.trustStore";
-                    System.err.println(String.format(
-                            "Validation of SSL certificate failed [%s=%s] (configuration failure)",
-                            property, StringUtils.defaultString(System.getProperty(property))));
-                } else
-                {
-                    ex.printStackTrace();
-                }
-            } else
-            {
-                ex.printStackTrace();
-            }
-            resultCode = 1;
-        } catch (final SystemExitException e)
-        {
-            resultCode = 1;
-        } catch (MasqueradingException e)
-        {
-            System.err.println(e);
-            resultCode = 1;
-        } catch (IllegalArgumentException e)
-        {
-            System.err.println(e.getMessage());
-            resultCode = 1;
-        } catch (final Exception e)
-        {
-            System.err.println();
-            e.printStackTrace();
-            resultCode = 1;
-        }
-
-        exitHandler.exit(resultCode);
-    }
-
-    private ICommand getCommandOrDie(String[] args)
-    {
-        // No arguments supplied -- print help
-        if (args.length < 1)
-        {
-            CommandHelp help = new CommandHelp(commandFactory);
-            help.printUsage(System.err);
-            exitHandler.exit(1);
-
-            // Never gets here
-            return null;
-        }
-
-        String commandName = args[0];
-        ICommand command = commandFactory.tryCommandForName(commandName);
-        if (null == command)
-        {
-            CommandHelp help = new CommandHelp(commandFactory);
-            help.printUsage(System.err);
-            exitHandler.exit(1);
-
-            // Never gets here
-            return null;
-        }
-        return command;
+        super(SystemExit.SYSTEM_EXIT, new CommandFactory());
     }
 
     public static void main(String[] args)
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/ICommandFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/ICommandFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..66031de4546a9e082636544435a0db8b5183c284
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/cli/ICommandFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.client.api.cli;
+
+import java.util.List;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public interface ICommandFactory
+{
+
+    /**
+     * Find the command that matches the name.
+     */
+    public ICommand tryCommandForName(String name);
+
+    /**
+     * List all the commands supported by this command factory.
+     */
+    public List<String> getKnownCommands();
+
+    /**
+     * Get the help command.
+     */
+    public ICommand getHelpCommand();
+}
\ No newline at end of file
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandGetTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandGetTest.java
index a0ab573773b423813bcec56de4330fb14914cdaf..de9c382e9a02a2823dc21976a8c4243ef24425cd 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandGetTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandGetTest.java
@@ -37,7 +37,7 @@ public class CommandGetTest extends AssertJUnit
     private final class MockCommandGet extends CommandGet
     {
         @Override
-        protected IDssComponent login(GlobalArguments arguments)
+        protected IDssComponent login(GlobalArguments args)
         {
             return dssComponent;
         }
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandLsTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandLsTest.java
index 3e1b17384317817788f6d66304a723d89b904691..0d0f6f26bac3e73681d08683aab437341a7d920c 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandLsTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandLsTest.java
@@ -28,8 +28,6 @@ import ch.systemsx.cisd.openbis.dss.client.api.v1.IDssComponent;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO;
 
 /**
- * 
- *
  * @author Franz-Josef Elmer
  */
 public class CommandLsTest extends AssertJUnit
@@ -37,14 +35,16 @@ public class CommandLsTest extends AssertJUnit
     private final class MockCommandLs extends CommandLs
     {
         @Override
-        protected IDssComponent login(GlobalArguments arguments)
+        protected IDssComponent login(GlobalArguments args)
         {
             return dssComponent;
         }
     }
-    
+
     private Mockery context;
+
     private IDssComponent dssComponent;
+
     private IDataSetDss dataSet;
 
     @BeforeMethod
@@ -71,18 +71,18 @@ public class CommandLsTest extends AssertJUnit
                 {
                     one(dssComponent).getDataSet("ds1");
                     will(returnValue(dataSet));
-                    
+
                     one(dataSet).listFiles("root-dir", false);
                     will(returnValue(new FileInfoDssDTO[] {}));
-                    
+
                     one(dssComponent).logout();
                 }
             });
         ICommand command = new MockCommandLs();
-        
+
         int exitCode = command.execute(new String[]
             { "-s", "url", "-u", "user", "-p", "pswd", "ds1", "root-dir" });
-        
+
         assertEquals(0, exitCode);
         context.assertIsSatisfied();
     }
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandPutTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandPutTest.java
index ed4c389ab5fb99f0cf841c46a21a75a2a3cf5940..af420f787ecd6d6c59fe40563ccdf9369b94614f 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandPutTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/cli/CommandPutTest.java
@@ -37,8 +37,6 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetO
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetOwnerType;
 
 /**
- * 
- *
  * @author Franz-Josef Elmer
  */
 public class CommandPutTest extends AbstractFileSystemTestCase
@@ -46,15 +44,18 @@ public class CommandPutTest extends AbstractFileSystemTestCase
     private final class MockCommandPut extends CommandPut
     {
         @Override
-        protected IDssComponent login(GlobalArguments arguments)
+        protected IDssComponent login(GlobalArguments args)
         {
             return dssComponent;
         }
     }
-    
+
     private Mockery context;
+
     private IDssComponent dssComponent;
+
     private IDataSetDss dataSet;
+
     private File dataSetExample;
 
     @BeforeMethod
@@ -101,7 +102,7 @@ public class CommandPutTest extends AbstractFileSystemTestCase
                             }
                         }), with(equal(dataSetExample)));
                     will(returnValue(dataSet));
-                    
+
                     one(dataSet).getCode();
                     will(returnValue("ds1"));
 
@@ -109,12 +110,12 @@ public class CommandPutTest extends AbstractFileSystemTestCase
                 }
             });
         ICommand command = new MockCommandPut();
-        
+
         int exitCode =
                 command.execute(new String[]
                     { "-s", "url", "-u", "user", "-p", "pswd", "-t", "my_type", "EXPERIMENT",
                             "/s/p/e", dataSetExample.getPath() });
-        
+
         assertEquals(0, exitCode);
         context.assertIsSatisfied();
     }