From 05faa55c58ae7ed33e22707d7d3637bc847ab25e Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Mon, 20 Jun 2011 10:54:17 +0000
Subject: [PATCH] LMS-2284 done including unit tests

SVN: 21743
---
 .../dss/generic/server/ConfigParameters.java  |  98 +++++------
 .../dss/generic/server/DataStoreServer.java   | 104 ++++++------
 .../server/IServletPropertiesManager.java     |  46 ++++++
 .../tasks/AbstractPluginTaskFactory.java      |  30 +++-
 .../plugins/tasks/PluginTaskProviders.java    |  23 ++-
 .../tasks/ProcessingPluginTaskFactory.java    |   8 +-
 .../tasks/ReportingPluginTaskFactory.java     |   8 +-
 .../shared/dto/PluginServletConfig.java       |  65 ++++++++
 .../generic/server/ConfigParametersTest.java  | 155 ++++++++++++++++++
 .../tasks/PluginTaskParametersTest.java       |  86 ++++++++--
 .../generic/shared/utils/PluginUtilTest.java  |   2 +-
 11 files changed, 478 insertions(+), 147 deletions(-)
 create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IServletPropertiesManager.java
 create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/dto/PluginServletConfig.java
 create mode 100644 datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParametersTest.java

diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParameters.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParameters.java
index 3077eb0d03b..63f06d1e1a4 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParameters.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParameters.java
@@ -18,7 +18,9 @@ package ch.systemsx.cisd.openbis.dss.generic.server;
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 
 import org.apache.log4j.Logger;
@@ -29,13 +31,14 @@ import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.utilities.PropertyParametersUtil;
 import ch.systemsx.cisd.common.utilities.PropertyParametersUtil.SectionProperties;
 import ch.systemsx.cisd.common.utilities.PropertyUtils;
+import ch.systemsx.cisd.openbis.dss.generic.shared.dto.PluginServletConfig;
 
 /**
  * Configuration parameters for the Data Set Download Server.
  * 
  * @author Franz-Josef Elmer
  */
-public final class ConfigParameters
+public final class ConfigParameters implements IServletPropertiesManager
 {
 
     private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
@@ -61,12 +64,12 @@ public final class ConfigParameters
 
     static final String AUTH_CACHE_EXPIRATION_TIME = "authorization-cache-expiration-time";
 
-    private static final int DEFAULT_AUTH_CACHE_EXPIRATION_TIME_MINS = 5;
+    static final int DEFAULT_AUTH_CACHE_EXPIRATION_TIME_MINS = 5;
 
     static final String AUTH_CACHE_CLEANUP_TIMER_PERIOD =
             "authorization-cache-cleanup-timer-period";
 
-    private static final int DEFAULT_AUTH_CACHE_CLEANUP_TIMER_PERIOD_MINS = 3 * 60;
+    static final int DEFAULT_AUTH_CACHE_CLEANUP_TIMER_PERIOD_MINS = 3 * 60;
 
     public static final String KEYSTORE_PATH_KEY = KEYSTORE + "path";
 
@@ -88,7 +91,7 @@ public final class ConfigParameters
      */
     static final String WEBSTART_JAR_PATH = "webstart-jar-path";
 
-    private static final String WEBSTART_JAR_PATH_DEFAULT = "lib";
+    static final String WEBSTART_JAR_PATH_DEFAULT = "lib";
 
     // PropertyParametersUtil
 
@@ -118,52 +121,12 @@ public final class ConfigParameters
 
     private final int authCacheCleanupTimerPeriodMins;
 
-    private final List<PluginServlet> pluginServlets;
+    private final Map<String, PluginServletConfig> pluginServlets;
 
     private final Properties properties;
 
     private final String webstartJarPath;
 
-    static final class PluginServlet
-    {
-        private final String servletClass;
-
-        private final String servletPath;
-
-        private final Properties servletProperties;
-
-        public PluginServlet(String servletClass, String servletPath, Properties servletProperties)
-        {
-            this.servletClass = servletClass;
-            this.servletPath = servletPath;
-            this.servletProperties = servletProperties;
-        }
-
-        public String getServletClass()
-        {
-            return servletClass;
-        }
-
-        /** URL path at which the servlet will be deployed */
-        public String getServletPath()
-        {
-            return servletPath;
-        }
-
-        /** Any additional properties specified in the properties file */
-        public Properties getServletProperties()
-        {
-            return servletProperties;
-        }
-
-        @Override
-        public String toString()
-        {
-            return "class = " + servletClass + ", path = " + servletPath;
-        }
-
-    }
-
     /**
      * Creates an instance based on the specified properties.
      * 
@@ -197,7 +160,11 @@ public final class ConfigParameters
         authCacheCleanupTimerPeriodMins =
                 PropertyUtils.getInt(properties, AUTH_CACHE_CLEANUP_TIMER_PERIOD,
                         DEFAULT_AUTH_CACHE_CLEANUP_TIMER_PERIOD_MINS);
-        pluginServlets = extractPluginServletsProperties(properties);
+        pluginServlets = new LinkedHashMap<String, PluginServletConfig>();
+        SectionProperties[] pluginServicesProperties =
+            PropertyParametersUtil.extractSectionProperties(properties,
+                    PLUGIN_SERVICES_LIST_KEY, false);
+        addServletsProperties("", pluginServicesProperties);
 
         webstartJarPath =
                 PropertyUtils.getProperty(properties, WEBSTART_JAR_PATH, WEBSTART_JAR_PATH_DEFAULT);
@@ -215,22 +182,31 @@ public final class ConfigParameters
         }
     }
 
-    private static List<PluginServlet> extractPluginServletsProperties(Properties properties)
+    public void addServletsProperties(String keyPrefix, SectionProperties[] servletsProperties)
     {
-        List<PluginServlet> servlets = new ArrayList<PluginServlet>();
-        SectionProperties[] pluginServicesProperties =
-                PropertyParametersUtil.extractSectionProperties(properties,
-                        PLUGIN_SERVICES_LIST_KEY, false);
-        for (SectionProperties sectionProperties : pluginServicesProperties)
+        for (SectionProperties sectionProperties : servletsProperties)
         {
             Properties servletProps = sectionProperties.getProperties();
-            String servletClass =
-                    PropertyUtils.getMandatoryProperty(servletProps, PLUGIN_SERVICE_CLASS_KEY);
-            String servletPath =
-                    PropertyUtils.getMandatoryProperty(servletProps, PLUGIN_SERVICE_PATH_KEY);
-            servlets.add(new PluginServlet(servletClass, servletPath, servletProps));
+            String key = keyPrefix + sectionProperties.getKey();
+            addServletProperties(key, servletProps);
+        }
+    }
+
+    public void addServletProperties(String propertiesName, Properties servletProperties)
+    {
+        String servletClass =
+                PropertyUtils.getMandatoryProperty(servletProperties, PLUGIN_SERVICE_CLASS_KEY);
+        String servletPath =
+                PropertyUtils.getMandatoryProperty(servletProperties, PLUGIN_SERVICE_PATH_KEY);
+        PluginServletConfig servletConfig =
+                new PluginServletConfig(servletClass, servletPath, servletProperties);
+        if (pluginServlets.containsKey(servletPath))
+        {
+            throw new ConfigurationFailureException("Servlet configuration [" + propertiesName
+                    + "]: There has already been a servlet configured for the path '" + servletPath
+                    + "'.");
         }
-        return servlets;
+        pluginServlets.put(servletPath, servletConfig);
     }
 
     private final static int getMandatoryIntegerProperty(final Properties properties,
@@ -292,9 +268,9 @@ public final class ConfigParameters
         return keystoreKeyPassword;
     }
 
-    public final List<PluginServlet> getPluginServlets()
+    public final List<PluginServletConfig> getPluginServlets()
     {
-        return pluginServlets;
+        return new ArrayList<PluginServletConfig>(pluginServlets.values());
     }
 
     public boolean isUseSSL()
@@ -344,7 +320,7 @@ public final class ConfigParameters
                     "Authorization cache cleanup timer period (minutes): %s",
                     authCacheCleanupTimerPeriodMins));
             operationLog.info(String.format("Keystore path: '%s'.", keystorePath));
-            for (PluginServlet pluginServlet : pluginServlets)
+            for (PluginServletConfig pluginServlet : getPluginServlets())
             {
                 operationLog.info("Plugin servlet: " + pluginServlet);
             }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java
index b980895c4dc..b0de8bf6603 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java
@@ -62,13 +62,13 @@ import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.logging.LogInitializer;
 import ch.systemsx.cisd.common.utilities.ExtendedProperties;
 import ch.systemsx.cisd.common.utilities.IInitializable;
-import ch.systemsx.cisd.openbis.dss.generic.server.ConfigParameters.PluginServlet;
 import ch.systemsx.cisd.openbis.dss.generic.server.api.v1.DssServiceRpcGeneric;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.DssSessionAuthorizationHolder;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.IDssServiceRpcGenericInternal;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.DataStoreApiUrlUtilities;
+import ch.systemsx.cisd.openbis.dss.generic.shared.dto.PluginServletConfig;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.DssPropertyParametersUtil;
 import ch.systemsx.cisd.openbis.generic.shared.IServer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.DatasetImageOverviewUtilities;
@@ -131,17 +131,20 @@ public class DataStoreServer
 
     private static final String UPLOAD_GUI_SERVING_SERVLET_PATH = "/dss_upload_gui";
 
+    private static ConfigParameters configParameters;
+
     public static final void start()
     {
         assert server == null : "Server already started";
-        final ConfigParameters configParameters = getConfigParameters();
+        ConfigParameters configParams = getConfigParameters();
         final IEncapsulatedOpenBISService openBISService = ServiceProvider.getOpenBISService();
         final ApplicationContext applicationContext =
                 new ApplicationContext(openBISService, ServiceProvider.getShareIdManager(),
-                        ServiceProvider.getHierarchicalContentProvider(), configParameters);
-        DssSessionAuthorizationHolder.setAuthorizer(new DatasetSessionAuthorizer(configParameters
-                .getAuthCacheExpirationTimeMins(), configParameters
+                        ServiceProvider.getHierarchicalContentProvider(), configParams);
+        DssSessionAuthorizationHolder.setAuthorizer(new DatasetSessionAuthorizer(configParams
+                .getAuthCacheExpirationTimeMins(), configParams
                 .getAuthCacheCleanupTimerPeriodMins()));
+        configParams.log();
         server = createServer(applicationContext);
         try
         {
@@ -184,32 +187,32 @@ public class DataStoreServer
 
     private final static Server createServer(final ApplicationContext applicationContext)
     {
-        final ConfigParameters configParameters = applicationContext.getConfigParameters();
-        final int port = configParameters.getPort();
+        final ConfigParameters configParams = applicationContext.getConfigParameters();
+        final int port = configParams.getPort();
         final Server thisServer = new Server();
-        initializeServer(configParameters, port, thisServer);
-        initializeContext(applicationContext, configParameters, thisServer);
+        initializeServer(configParams, port, thisServer);
+        initializeContext(applicationContext, configParams, thisServer);
         return thisServer;
     }
 
-    private static void initializeServer(final ConfigParameters configParameters, final int port,
+    private static void initializeServer(final ConfigParameters configParams, final int port,
             final Server thisServer)
     {
-        final Connector socketConnector = createSocketConnector(configParameters);
+        final Connector socketConnector = createSocketConnector(configParams);
         socketConnector.setPort(port);
         socketConnector.setMaxIdleTime(30000);
         thisServer.addConnector(socketConnector);
     }
 
     private static void initializeContext(final ApplicationContext applicationContext,
-            final ConfigParameters configParameters, final Server thisServer)
+            final ConfigParameters configParams, final Server thisServer)
     {
         // Create a handler collection for grouping together the handlers
         ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
         thisServer.setHandler(contextHandlers);
 
         // Register the handler that returns the webstart jars
-        registerDssUploadClientHandler(thisServer, contextHandlers, configParameters);
+        registerDssUploadClientHandler(thisServer, contextHandlers, configParams);
 
         ServletContextHandler servletContextHandler =
                 new ServletContextHandler(contextHandlers, "/", ServletContextHandler.SESSIONS);
@@ -220,12 +223,12 @@ public class DataStoreServer
         String applicationName = "/" + DATA_STORE_SERVER_WEB_APPLICATION_NAME;
         servletContextHandler.addServlet(new ServletHolder(new DataStoreServlet()), "/"
                 + DATA_STORE_SERVER_SERVICE_NAME + "/*");
-        DatasetDownloadServlet.setDownloadUrl(configParameters.getDownloadURL());
+        DatasetDownloadServlet.setDownloadUrl(configParams.getDownloadURL());
         servletContextHandler.addServlet(DatasetDownloadServlet.class, applicationName + "/*");
 
-        initializeRpcServices(servletContextHandler, applicationContext, configParameters);
-        registerPluginServlets(servletContextHandler, configParameters.getPluginServlets());
-        registerImageOverviewServlet(servletContextHandler, configParameters);
+        initializeRpcServices(servletContextHandler, applicationContext);
+        registerPluginServlets(servletContextHandler, configParams.getPluginServlets());
+        registerImageOverviewServlet(servletContextHandler, configParams);
     }
 
     /**
@@ -235,7 +238,7 @@ public class DataStoreServer
     // Perhaps by using Spring and the dssApplicationContext.xml more effectively, or perhaps by
     // using annotations and reflection.
     private static void initializeRpcServices(final ServletContextHandler context,
-            final ApplicationContext applicationContext, final ConfigParameters configParameters)
+            final ApplicationContext applicationContext)
     {
         // Get the spring bean and do some additional configuration
         StreamSupportingHttpInvokerServiceExporter v1ServiceExporter =
@@ -278,9 +281,9 @@ public class DataStoreServer
 
     @SuppressWarnings("unchecked")
     private static void registerPluginServlets(ServletContextHandler context,
-            List<PluginServlet> pluginServlets)
+            List<PluginServletConfig> pluginServlets)
     {
-        for (PluginServlet pluginServlet : pluginServlets)
+        for (PluginServletConfig pluginServlet : pluginServlets)
         {
             Class<? extends Servlet> classInstance;
             try
@@ -307,15 +310,15 @@ public class DataStoreServer
     }
 
     private static void registerImageOverviewServlet(ServletContextHandler context,
-            ConfigParameters configParameters)
+            ConfigParameters configParams)
     {
-        DatasetImageOverviewServlet.initConfiguration(configParameters.getProperties());
+        DatasetImageOverviewServlet.initConfiguration(configParams.getProperties());
         context.addServlet(DatasetImageOverviewServlet.class, "/"
                 + DatasetImageOverviewUtilities.SERVLET_NAME + "/*");
     }
 
     private static void registerDssUploadClientHandler(Server thisServer,
-            ContextHandlerCollection context, ConfigParameters configParameters)
+            ContextHandlerCollection context, ConfigParameters configParams)
     {
         String servletPathSuffix = UPLOAD_GUI_SERVING_SERVLET_PATH;
         // Map this resource to a name that is accessible from outside
@@ -327,28 +330,28 @@ public class DataStoreServer
         // client.
         // This is the value assigned to the ${dss_upload_gui} variable in dss/build.xml .
         // We have set this up to be the same as the servletPathSuffix.
-        webstartContextHandler.setResourceBase(configParameters.getWebstartJarPath()
+        webstartContextHandler.setResourceBase(configParams.getWebstartJarPath()
                 + servletPathSuffix);
         // Add a resource handler to the webstart jar path to serve files from the file system.
         ResourceHandler webstartJarHandler = new ResourceHandler();
         webstartContextHandler.setHandler(webstartJarHandler);
     }
 
-    private static Connector createSocketConnector(ConfigParameters configParameters)
+    private static Connector createSocketConnector(ConfigParameters configParams)
     {
-        if (configParameters.isUseSSL())
+        if (configParams.isUseSSL())
         {
             final SslConnector socketConnector =
-                    configParameters.isUseNIO() ? new SslSelectChannelConnector()
+                    configParams.isUseNIO() ? new SslSelectChannelConnector()
                             : new SslSocketConnector();
-            socketConnector.setKeystore(configParameters.getKeystorePath());
-            socketConnector.setPassword(configParameters.getKeystorePassword());
-            socketConnector.setKeyPassword(configParameters.getKeystoreKeyPassword());
+            socketConnector.setKeystore(configParams.getKeystorePath());
+            socketConnector.setPassword(configParams.getKeystorePassword());
+            socketConnector.setKeyPassword(configParams.getKeystoreKeyPassword());
             return socketConnector;
         } else
         {
             operationLog.warn("creating connector to openBIS without SSL");
-            return configParameters.isUseNIO() ? new SelectChannelConnector()
+            return configParams.isUseNIO() ? new SelectChannelConnector()
                     : new SocketConnector();
         }
     }
@@ -369,30 +372,31 @@ public class DataStoreServer
         }
     }
 
-    final static ConfigParameters getConfigParameters()
+    public static ConfigParameters getConfigParameters()
     {
-        Properties properties;
-        try
-        {
-            properties = DssPropertyParametersUtil.loadServiceProperties();
-        } catch (ConfigurationFailureException ex)
+        if (configParameters == null)
         {
-            properties = new Properties();
-        }
-        final Properties systemProperties = System.getProperties();
-        final Enumeration<?> propertyNames = systemProperties.propertyNames();
-        while (propertyNames.hasMoreElements())
-        {
-            final String name = (String) propertyNames.nextElement();
-            if (name.startsWith(PREFIX))
+            Properties properties;
+            try
             {
-                final String value = systemProperties.getProperty(name);
-                properties.setProperty(name.substring(PREFIX_LENGTH), value);
+                properties = DssPropertyParametersUtil.loadServiceProperties();
+            } catch (ConfigurationFailureException ex)
+            {
+                properties = new Properties();
+            }
+            final Properties systemProperties = System.getProperties();
+            final Enumeration<?> propertyNames = systemProperties.propertyNames();
+            while (propertyNames.hasMoreElements())
+            {
+                final String name = (String) propertyNames.nextElement();
+                if (name.startsWith(PREFIX))
+                {
+                    final String value = systemProperties.getProperty(name);
+                    properties.setProperty(name.substring(PREFIX_LENGTH), value);
+                }
             }
+            configParameters = new ConfigParameters(ExtendedProperties.createWith(properties));
         }
-        final ConfigParameters configParameters =
-                new ConfigParameters(ExtendedProperties.createWith(properties));
-        configParameters.log();
         return configParameters;
     }
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IServletPropertiesManager.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IServletPropertiesManager.java
new file mode 100644
index 00000000000..1e07ea497a3
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IServletPropertiesManager.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.generic.server;
+
+import java.util.Properties;
+
+import javax.servlet.Servlet;
+
+import ch.systemsx.cisd.common.utilities.PropertyParametersUtil.SectionProperties;
+
+/**
+ * Manager of {@link Servlet} properties.
+ *
+ * @author Franz-Josef Elmer
+ */
+public interface IServletPropertiesManager
+{
+    /**
+     * Adds servlets properties from specified section properties. 
+     * 
+     * @param keyPrefix Prefix added to section key for error messaging.
+     */
+    public void addServletsProperties(String keyPrefix, SectionProperties[] servletsProperties);
+
+    /**
+     * Adds specified servlet properties.
+     * 
+     * @param propertiesName Name of the servlet properties used for error messaging.
+     */
+    public void addServletProperties(String propertiesName, Properties properties);
+
+}
\ No newline at end of file
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/AbstractPluginTaskFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/AbstractPluginTaskFactory.java
index 9f361864c39..e104a86a585 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/AbstractPluginTaskFactory.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/AbstractPluginTaskFactory.java
@@ -35,9 +35,11 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.utilities.ClassUtils;
+import ch.systemsx.cisd.common.utilities.ExtendedProperties;
 import ch.systemsx.cisd.common.utilities.PropertyParametersUtil;
 import ch.systemsx.cisd.common.utilities.PropertyParametersUtil.SectionProperties;
 import ch.systemsx.cisd.common.utilities.PropertyUtils;
+import ch.systemsx.cisd.openbis.dss.generic.server.IServletPropertiesManager;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.DssPropertyParametersUtil;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatastoreServiceDescription;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ReportingPluginType;
@@ -66,6 +68,12 @@ public abstract class AbstractPluginTaskFactory<T>
     @Private
     public final static String CLASS_PROPERTY_NAME = "class";
 
+    @Private
+    public static final String SERVLET_PROPERTY_NAME = "servlet";
+    
+    @Private
+    public static final String SERVLETS_PROPERTY_NAME = "servlets";
+    
     /**
      * Property name which stores a file path. The file should contain properties which are plugin
      * parameters.
@@ -84,12 +92,28 @@ public abstract class AbstractPluginTaskFactory<T>
 
     private final T pluginInstance;
 
-    protected AbstractPluginTaskFactory(SectionProperties sectionProperties, String datastoreCode,
-            Class<T> clazz, File storeRoot)
+    protected AbstractPluginTaskFactory(IServletPropertiesManager servletPropertiesManager,
+            SectionProperties sectionProperties, String datastoreCode, Class<T> clazz,
+            File storeRoot)
     {
         Properties pluginProperties = sectionProperties.getProperties();
-        String pluginKey = sectionProperties.getKey();
         String label = PropertyUtils.getMandatoryProperty(pluginProperties, LABEL_PROPERTY_NAME);
+        Properties props =
+                ExtendedProperties.getSubset(pluginProperties, SERVLET_PROPERTY_NAME + ".", true);
+        if (props.isEmpty())
+        {
+            SectionProperties[] servletsProperties =
+                    PropertyParametersUtil.extractSectionProperties(pluginProperties,
+                            SERVLETS_PROPERTY_NAME, false);
+            if (servletsProperties.length > 0)
+            {
+                servletPropertiesManager.addServletsProperties(label + ", ", servletsProperties);
+            }
+        } else
+        {
+            servletPropertiesManager.addServletProperties(label, props);
+        }
+        String pluginKey = sectionProperties.getKey();
         String[] datasetCodes = extractDatasetCodes(pluginProperties);
 
         this.className = PropertyUtils.getMandatoryProperty(pluginProperties, CLASS_PROPERTY_NAME);
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/PluginTaskProviders.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/PluginTaskProviders.java
index 41dcd181f10..14c77f4e351 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/PluginTaskProviders.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/PluginTaskProviders.java
@@ -22,6 +22,8 @@ import java.util.Properties;
 import ch.rinn.restrictions.Private;
 import ch.systemsx.cisd.common.utilities.PropertyParametersUtil;
 import ch.systemsx.cisd.common.utilities.PropertyParametersUtil.SectionProperties;
+import ch.systemsx.cisd.openbis.dss.generic.server.DataStoreServer;
+import ch.systemsx.cisd.openbis.dss.generic.server.IServletPropertiesManager;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.DssPropertyParametersUtil;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatastoreServiceDescriptions;
 
@@ -55,10 +57,12 @@ public class PluginTaskProviders
     /** for external injections */
     public static PluginTaskProviders create()
     {
+        IServletPropertiesManager servletPropertiesManager = DataStoreServer.getConfigParameters();
         Properties properties = DssPropertyParametersUtil.loadServiceProperties();
         String property = properties.getProperty(STOREROOT_DIR_KEY);
         File storeRoot = new File(property);
-        PluginTaskProviders providers = new PluginTaskProviders(properties, storeRoot);
+        PluginTaskProviders providers =
+                new PluginTaskProviders(properties, servletPropertiesManager, storeRoot);
         providers.check();
         providers.logConfigurations();
         return providers;
@@ -66,14 +70,17 @@ public class PluginTaskProviders
 
     @Private
     // public only for tests
-    public PluginTaskProviders(Properties serviceProperties, File storeRoot)
+    public PluginTaskProviders(Properties serviceProperties,
+            IServletPropertiesManager servletPropertiesManager, File storeRoot)
     {
         this.storeRoot = storeRoot;
         String datastoreCode = DssPropertyParametersUtil.getDataStoreCode(serviceProperties);
         this.reportingPlugins =
-                createReportingPluginsFactories(serviceProperties, datastoreCode, storeRoot);
+                createReportingPluginsFactories(serviceProperties, servletPropertiesManager,
+                        datastoreCode, storeRoot);
         this.processingPlugins =
-                createProcessingPluginsFactories(serviceProperties, datastoreCode, storeRoot);
+                createProcessingPluginsFactories(serviceProperties, servletPropertiesManager,
+                        datastoreCode, storeRoot);
         this.archiverTaskFactory = createArchiverTaskFactory(serviceProperties, datastoreCode);
     }
 
@@ -112,7 +119,7 @@ public class PluginTaskProviders
 
     @Private
     static PluginTaskProvider<IReportingPluginTask> createReportingPluginsFactories(
-            Properties serviceProperties, String datastoreCode, File storeRoot)
+            Properties serviceProperties, IServletPropertiesManager configParameters, String datastoreCode, File storeRoot)
     {
         SectionProperties[] sectionsProperties =
                 extractSectionProperties(serviceProperties, REPORTING_PLUGIN_NAMES);
@@ -121,14 +128,14 @@ public class PluginTaskProviders
         for (int i = 0; i < factories.length; i++)
         {
             factories[i] =
-                    new ReportingPluginTaskFactory(sectionsProperties[i], datastoreCode, storeRoot);
+                    new ReportingPluginTaskFactory(configParameters, sectionsProperties[i], datastoreCode, storeRoot);
         }
         return new PluginTaskProvider<IReportingPluginTask>(factories);
     }
 
     @Private
     static PluginTaskProvider<IProcessingPluginTask> createProcessingPluginsFactories(
-            Properties serviceProperties, String datastoreCode, File storeRoot)
+            Properties serviceProperties, IServletPropertiesManager configParameters, String datastoreCode, File storeRoot)
     {
         SectionProperties[] sectionsProperties =
                 extractSectionProperties(serviceProperties, PROCESSING_PLUGIN_NAMES);
@@ -137,7 +144,7 @@ public class PluginTaskProviders
         for (int i = 0; i < factories.length; i++)
         {
             factories[i] =
-                    new ProcessingPluginTaskFactory(sectionsProperties[i], datastoreCode, storeRoot);
+                    new ProcessingPluginTaskFactory(configParameters, sectionsProperties[i], datastoreCode, storeRoot);
         }
         return new PluginTaskProvider<IProcessingPluginTask>(factories);
     }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/ProcessingPluginTaskFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/ProcessingPluginTaskFactory.java
index 2d0965ccaee..fa296ac5b42 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/ProcessingPluginTaskFactory.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/ProcessingPluginTaskFactory.java
@@ -23,6 +23,7 @@ import org.apache.log4j.Logger;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.utilities.PropertyParametersUtil.SectionProperties;
+import ch.systemsx.cisd.openbis.dss.generic.server.IServletPropertiesManager;
 
 /**
  * Factory of Processing Plugin Tasks.
@@ -34,10 +35,11 @@ public class ProcessingPluginTaskFactory extends AbstractPluginTaskFactory<IProc
     private static final Logger operationLog =
             LogFactory.getLogger(LogCategory.OPERATION, ProcessingPluginTaskFactory.class);
 
-    public ProcessingPluginTaskFactory(SectionProperties sectionProperties, String datastoreCode,
-            File storeRoot)
+    public ProcessingPluginTaskFactory(IServletPropertiesManager servletPropertiesManager,
+            SectionProperties sectionProperties, String datastoreCode, File storeRoot)
     {
-        super(sectionProperties, datastoreCode, IProcessingPluginTask.class, storeRoot);
+        super(servletPropertiesManager, sectionProperties, datastoreCode, IProcessingPluginTask.class,
+                storeRoot);
     }
 
     @Override
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/ReportingPluginTaskFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/ReportingPluginTaskFactory.java
index a14dc84b0b7..fa53eab0c5d 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/ReportingPluginTaskFactory.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/ReportingPluginTaskFactory.java
@@ -23,6 +23,7 @@ import org.apache.log4j.Logger;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.utilities.PropertyParametersUtil.SectionProperties;
+import ch.systemsx.cisd.openbis.dss.generic.server.IServletPropertiesManager;
 
 /**
  * Factory of Reporting Plugin Tasks.
@@ -34,10 +35,11 @@ public class ReportingPluginTaskFactory extends AbstractPluginTaskFactory<IRepor
     private static final Logger operationLog =
             LogFactory.getLogger(LogCategory.OPERATION, ProcessingPluginTaskFactory.class);
 
-    public ReportingPluginTaskFactory(SectionProperties sectionProperties, String datastoreCode,
-            File storeRoot)
+    public ReportingPluginTaskFactory(IServletPropertiesManager servletPropertiesManager,
+            SectionProperties sectionProperties, String datastoreCode, File storeRoot)
     {
-        super(sectionProperties, datastoreCode, IReportingPluginTask.class, storeRoot);
+        super(servletPropertiesManager, sectionProperties, datastoreCode, IReportingPluginTask.class,
+                storeRoot);
     }
 
     @Override
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/dto/PluginServletConfig.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/dto/PluginServletConfig.java
new file mode 100644
index 00000000000..2e10fe252e8
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/dto/PluginServletConfig.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.generic.shared.dto;
+
+import java.util.Properties;
+
+/**
+ * Configuration parameters for Servlets.
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public final class PluginServletConfig
+{
+    private final String servletClass;
+
+    private final String servletPath;
+
+    private final Properties servletProperties;
+
+    public PluginServletConfig(String servletClass, String servletPath, Properties servletProperties)
+    {
+        this.servletClass = servletClass;
+        this.servletPath = servletPath;
+        this.servletProperties = servletProperties;
+    }
+
+    public String getServletClass()
+    {
+        return servletClass;
+    }
+
+    /** URL path at which the servlet will be deployed */
+    public String getServletPath()
+    {
+        return servletPath;
+    }
+
+    /** Any additional properties specified in the properties file */
+    public Properties getServletProperties()
+    {
+        return servletProperties;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "class = " + servletClass + ", path = " + servletPath;
+    }
+
+}
\ No newline at end of file
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParametersTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParametersTest.java
new file mode 100644
index 00000000000..459da385d55
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParametersTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.generic.server;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
+import ch.systemsx.cisd.openbis.dss.generic.shared.dto.PluginServletConfig;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class ConfigParametersTest extends AssertJUnit
+{
+    @Test
+    public void testMinimumConfig()
+    {
+        ConfigParameters configParameters = new ConfigParameters(createMandatoryProperties());
+        
+        assertEquals("store", configParameters.getStorePath().getPath());
+        assertEquals(4711, configParameters.getPort());
+        assertEquals("my-url", configParameters.getServerURL());
+        assertEquals("download-url", configParameters.getDownloadURL());
+        assertEquals(true, configParameters.isUseSSL());
+        assertEquals("key-store", configParameters.getKeystorePath());
+        assertEquals("key-store-password", configParameters.getKeystorePassword());
+        assertEquals("key-store-key-password", configParameters.getKeystoreKeyPassword());
+        assertEquals("dss_rpc_incoming", configParameters.getRpcIncomingDirectory().getName());
+        assertEquals(false, configParameters.isUseNIO());
+        assertEquals(ConfigParameters.DEFAULT_AUTH_CACHE_CLEANUP_TIMER_PERIOD_MINS,
+                configParameters.getAuthCacheCleanupTimerPeriodMins());
+        assertEquals(ConfigParameters.DEFAULT_AUTH_CACHE_EXPIRATION_TIME_MINS,
+                configParameters.getAuthCacheExpirationTimeMins());
+        assertEquals(0, configParameters.getPluginServlets().size());
+        assertEquals(ConfigParameters.WEBSTART_JAR_PATH_DEFAULT,
+                configParameters.getWebstartJarPath());
+    }
+    
+    @Test
+    public void testPluginServices()
+    {
+        Properties properties = createMandatoryProperties();
+        properties.setProperty(ConfigParameters.PLUGIN_SERVICES_LIST_KEY, "s1, s2");
+        properties.setProperty("s1." + ConfigParameters.PLUGIN_SERVICE_CLASS_KEY, "class1");
+        properties.setProperty("s1." + ConfigParameters.PLUGIN_SERVICE_PATH_KEY, "path1");
+        properties.setProperty("s2." + ConfigParameters.PLUGIN_SERVICE_CLASS_KEY, "class2");
+        properties.setProperty("s2." + ConfigParameters.PLUGIN_SERVICE_PATH_KEY, "path2");
+        properties.setProperty("s2.a", "alpha");
+        ConfigParameters configParameters = new ConfigParameters(properties);
+        
+        List<PluginServletConfig> pluginServlets = configParameters.getPluginServlets();
+        assertEquals("class1", pluginServlets.get(0).getServletClass());
+        assertEquals("path1", pluginServlets.get(0).getServletPath());
+        assertEquals("class2", pluginServlets.get(1).getServletClass());
+        assertEquals("path2", pluginServlets.get(1).getServletPath());
+        assertTrue(pluginServlets.get(1).getServletProperties().containsKey("a"));
+        assertEquals(2, pluginServlets.size());
+    }
+    
+    @Test
+    public void testPluginServicesWithDuplicatedPath()
+    {
+        Properties properties = createMandatoryProperties();
+        properties.setProperty(ConfigParameters.PLUGIN_SERVICES_LIST_KEY, "s1, s2");
+        properties.setProperty("s1." + ConfigParameters.PLUGIN_SERVICE_CLASS_KEY, "class1");
+        properties.setProperty("s1." + ConfigParameters.PLUGIN_SERVICE_PATH_KEY, "path1");
+        properties.setProperty("s2." + ConfigParameters.PLUGIN_SERVICE_CLASS_KEY, "class2");
+        properties.setProperty("s2." + ConfigParameters.PLUGIN_SERVICE_PATH_KEY, "path1");
+        properties.setProperty("s2.a", "alpha");
+        try
+        {
+            new ConfigParameters(properties);
+            fail("ConfigurationFailureException expected");
+        } catch (ConfigurationFailureException ex)
+        {
+            assertEquals("Servlet configuration [s2]: There has already been a servlet "
+                    + "configured for the path 'path1'.", ex.getMessage());
+        }
+    }
+    
+    @Test
+    public void testAddServletProperties()
+    {
+        ConfigParameters configParameters = new ConfigParameters(createMandatoryProperties());
+        
+        Properties properties = new Properties();
+        properties.setProperty(ConfigParameters.PLUGIN_SERVICE_CLASS_KEY, "class1");
+        properties.setProperty(ConfigParameters.PLUGIN_SERVICE_PATH_KEY, "path1");
+        configParameters.addServletProperties("my-servlet", properties);
+
+        List<PluginServletConfig> pluginServlets = configParameters.getPluginServlets();
+        assertEquals("class1", pluginServlets.get(0).getServletClass());
+        assertEquals("path1", pluginServlets.get(0).getServletPath());
+        assertEquals(1, pluginServlets.size());
+    }
+
+    @Test
+    public void testAddServletPropertiesWithExistingPath()
+    {
+        Properties properties = createMandatoryProperties();
+        properties.setProperty(ConfigParameters.PLUGIN_SERVICES_LIST_KEY, "s1");
+        properties.setProperty("s1." + ConfigParameters.PLUGIN_SERVICE_CLASS_KEY, "class1");
+        properties.setProperty("s1." + ConfigParameters.PLUGIN_SERVICE_PATH_KEY, "path1");
+        ConfigParameters configParameters = new ConfigParameters(properties);
+        Properties props = new Properties();
+        props.setProperty(ConfigParameters.PLUGIN_SERVICE_CLASS_KEY, "class1");
+        props.setProperty(ConfigParameters.PLUGIN_SERVICE_PATH_KEY, "path1");
+
+        try
+        {
+            configParameters.addServletProperties("my-servlet", props);
+            fail("ConfigurationFailureException expected");
+        } catch (ConfigurationFailureException ex)
+        {
+            assertEquals("Servlet configuration [my-servlet]: There has already been a servlet "
+                    + "configured for the path 'path1'.", ex.getMessage());
+        }
+    }
+    
+    private Properties createMandatoryProperties()
+    {
+        Properties properties = new Properties();
+        properties.setProperty(ConfigParameters.STOREROOT_DIR_KEY, "store");
+        properties.setProperty(ConfigParameters.PORT_KEY, "4711");
+        properties.setProperty(ConfigParameters.SERVER_URL_KEY, "my-url");
+        properties.setProperty(ConfigParameters.DOWNLOAD_URL, "download-url");
+        properties.setProperty(ConfigParameters.SESSION_TIMEOUT_KEY, "42");
+        properties.setProperty(ConfigParameters.KEYSTORE_PATH_KEY, "key-store");
+        properties.setProperty(ConfigParameters.KEYSTORE_PASSWORD_KEY, "key-store-password");
+        properties.setProperty(ConfigParameters.KEYSTORE_KEY_PASSWORD_KEY, "key-store-key-password");
+        return properties;
+    }
+    
+    
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/PluginTaskParametersTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/PluginTaskParametersTest.java
index eb1bc5ae393..76d0fc4ac3e 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/PluginTaskParametersTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/tasks/PluginTaskParametersTest.java
@@ -22,6 +22,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Properties;
 
@@ -38,7 +39,10 @@ import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.io.DefaultFileBasedHierarchicalContentFactory;
 import ch.systemsx.cisd.common.io.IHierarchicalContent;
+import ch.systemsx.cisd.common.test.RecordingMatcher;
 import ch.systemsx.cisd.common.utilities.IDelegatedAction;
+import ch.systemsx.cisd.common.utilities.PropertyParametersUtil.SectionProperties;
+import ch.systemsx.cisd.openbis.dss.generic.server.IServletPropertiesManager;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.demo.DemoProcessingPlugin;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.demo.DemoReportingPlugin;
 import ch.systemsx.cisd.openbis.dss.generic.shared.DataSetProcessingContext;
@@ -65,10 +69,13 @@ public class PluginTaskParametersTest extends AbstractFileSystemTestCase
 
     private IHierarchicalContentProvider contentProvider;
 
+    private IServletPropertiesManager servletPropertiesManager;
+
     @BeforeMethod
     public void beforeMethod()
     {
         context = new Mockery();
+        servletPropertiesManager = context.mock(IServletPropertiesManager.class);
         contentProvider = new IHierarchicalContentProvider()
             {
 
@@ -111,6 +118,7 @@ public class PluginTaskParametersTest extends AbstractFileSystemTestCase
     public void afterTest()
     {
         ServiceProviderTestWrapper.restoreApplicationContext();
+        context.assertIsSatisfied();
     }
 
     @Test
@@ -120,22 +128,49 @@ public class PluginTaskParametersTest extends AbstractFileSystemTestCase
         String plugin1 = "plugin1";
         String plugin2 = "plugin2";
 
-        props.put(PluginTaskProviders.REPORTING_PLUGIN_NAMES, plugin1 + ", " + plugin2);
+        props.setProperty(PluginTaskProviders.REPORTING_PLUGIN_NAMES, plugin1 + ", " + plugin2);
         String pluginLabel1 = "Demo Reporting 1";
         String datasetCodes1 = "MZXML, EICML";
-        putPluginProperties(props, plugin1, pluginLabel1, datasetCodes1, DemoReportingPlugin.class);
+        setPluginProperties(props, plugin1, pluginLabel1, datasetCodes1, DemoReportingPlugin.class);
 
-        String pluginLabel2 = "Demo Reporting 2";
+        final String pluginLabel2 = "Demo Reporting 2";
         String datasetCodes2 = "EICML";
-        putPluginProperties(props, plugin2, pluginLabel2, datasetCodes2, DemoReportingPlugin.class);
+        setPluginProperties(props, plugin2, pluginLabel2, datasetCodes2, DemoReportingPlugin.class);
+        setPluginProperty(props, plugin2, AbstractPluginTaskFactory.SERVLETS_PROPERTY_NAME,
+                "s1, s2");
+        setPluginProperty(props, plugin2, "s1.a", "alpha");
+        setPluginProperty(props, plugin2, "s2.b", "beta");
+        final RecordingMatcher<SectionProperties[]> sectionPropertiesMatcher =
+                new RecordingMatcher<SectionProperties[]>();
+        context.checking(new Expectations()
+            {
+                {
+                    one(servletPropertiesManager).addServletsProperties(with(pluginLabel2 + ", "),
+                            with(sectionPropertiesMatcher));
+                }
+            });
 
         PluginTaskProvider<IReportingPluginTask> factories = createReportingPluginsFactories(props);
+        
         factories.check(false);
         factories.logConfigurations();
         IReportingPluginTask pluginInstance1 = factories.getPluginInstance(plugin1);
         pluginInstance1.createReport(createDatasetDescriptions(), new DataSetProcessingContext(
                 null, null, null, null));
 
+        SectionProperties[] sectionProperties = sectionPropertiesMatcher.recordedObject();
+        Arrays.sort(sectionProperties, new Comparator<SectionProperties>()
+            {
+                public int compare(SectionProperties sp1, SectionProperties sp2)
+                {
+                    return sp1.getKey().compareTo(sp2.getKey());
+                }
+            });
+        assertEquals("s1", sectionProperties[0].getKey());
+        assertEquals("{a=alpha}", sectionProperties[0].getProperties().toString());
+        assertEquals("s2", sectionProperties[1].getKey());
+        assertEquals("{b=beta}", sectionProperties[1].getProperties().toString());
+        assertEquals(2, sectionProperties.length);
         List<DatastoreServiceDescription> descriptions = factories.getPluginDescriptions();
         assertEquals(2, descriptions.size());
         for (DatastoreServiceDescription desc : descriptions)
@@ -151,26 +186,29 @@ public class PluginTaskParametersTest extends AbstractFileSystemTestCase
                 assertEquals(plugin1, key);
             }
         }
+        context.assertIsSatisfied();
     }
 
     @Test(expectedExceptions = ConfigurationFailureException.class)
     public void testMissingPluginSpecFails() throws Exception
     {
         Properties props = new Properties();
-        props.put(PluginTaskProviders.REPORTING_PLUGIN_NAMES, "plugin1");
+        props.setProperty(PluginTaskProviders.REPORTING_PLUGIN_NAMES, "plugin1");
         createReportingPluginsFactories(props);
     }
 
-    private static PluginTaskProvider<IReportingPluginTask> createReportingPluginsFactories(
+    private PluginTaskProvider<IReportingPluginTask> createReportingPluginsFactories(
             Properties props)
     {
-        return PluginTaskProviders.createReportingPluginsFactories(props, "dss", STORE_ROOT);
+        return PluginTaskProviders.createReportingPluginsFactories(props, servletPropertiesManager,
+                "dss", STORE_ROOT);
     }
 
-    private static PluginTaskProvider<IProcessingPluginTask> createProcessingPluginsFactories(
+    private PluginTaskProvider<IProcessingPluginTask> createProcessingPluginsFactories(
             Properties props)
     {
-        return PluginTaskProviders.createProcessingPluginsFactories(props, "dss", STORE_ROOT);
+        return PluginTaskProviders.createProcessingPluginsFactories(props,
+                servletPropertiesManager, "dss", STORE_ROOT);
     }
 
     @Test
@@ -193,16 +231,28 @@ public class PluginTaskParametersTest extends AbstractFileSystemTestCase
         Properties props = new Properties();
         String plugin1 = "plugin1";
 
-        props.put(PluginTaskProviders.PROCESSING_PLUGIN_NAMES, plugin1);
-        putPluginProperties(props, plugin1, "pluginLabel1", "datasetCodes1",
+        props.setProperty(PluginTaskProviders.PROCESSING_PLUGIN_NAMES, plugin1);
+        setPluginProperties(props, plugin1, "pluginLabel1", "datasetCodes1",
                 DemoProcessingPlugin.class);
+        setPluginProperty(props, plugin1, AbstractPluginTaskFactory.SERVLET_PROPERTY_NAME + ".a",
+                "alpha");
+        context.checking(new Expectations()
+            {
+                {
+                    Properties properties = new Properties();
+                    properties.setProperty("a", "alpha");
+                    one(servletPropertiesManager).addServletProperties("pluginLabel1", properties);
+                }
+            });
 
         PluginTaskProvider<IProcessingPluginTask> factories =
                 createProcessingPluginsFactories(props);
+        
         factories.check(true);
         factories.logConfigurations();
         IProcessingPluginTask pluginInstance1 = factories.getPluginInstance(plugin1);
         pluginInstance1.process(createDatasetDescriptions(), null);
+        context.assertIsSatisfied();
     }
 
     private static List<DatasetDescription> createDatasetDescriptions()
@@ -219,19 +269,19 @@ public class PluginTaskParametersTest extends AbstractFileSystemTestCase
         return Arrays.asList(description);
     }
 
-    private void putPluginProperties(Properties props, String pluginId, String pluginLabel,
+    private void setPluginProperties(Properties props, String pluginId, String pluginLabel,
             String datasetCodes, Class<?> pluginClass) throws IOException, FileNotFoundException
     {
-        putPluginProperty(props, pluginId, AbstractPluginTaskFactory.LABEL_PROPERTY_NAME,
+        setPluginProperty(props, pluginId, AbstractPluginTaskFactory.LABEL_PROPERTY_NAME,
                 pluginLabel);
-        putPluginProperty(props, pluginId, AbstractPluginTaskFactory.DATASET_CODES_PROPERTY_NAME,
+        setPluginProperty(props, pluginId, AbstractPluginTaskFactory.DATASET_CODES_PROPERTY_NAME,
                 datasetCodes);
-        putPluginProperty(props, pluginId, AbstractPluginTaskFactory.CLASS_PROPERTY_NAME,
+        setPluginProperty(props, pluginId, AbstractPluginTaskFactory.CLASS_PROPERTY_NAME,
                 pluginClass.getName());
         String[] pluginParams = new String[]
             { "param1 = X", "param2 = Y" };
         File paramsFile = createPluginPropertiesFile(pluginParams);
-        putPluginProperty(props, pluginId,
+        setPluginProperty(props, pluginId,
                 AbstractPluginTaskFactory.PARAMS_FILE_PATH_PROPERTY_NAME, paramsFile.getPath());
     }
 
@@ -244,10 +294,10 @@ public class PluginTaskParametersTest extends AbstractFileSystemTestCase
         return paramsFile;
     }
 
-    private static void putPluginProperty(Properties props, String pluginKey, String propertyName,
+    private static void setPluginProperty(Properties props, String pluginKey, String propertyName,
             String value)
     {
-        props.put(pluginKey + "." + propertyName, value);
+        props.setProperty(pluginKey + "." + propertyName, value);
 
     }
 }
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/PluginUtilTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/PluginUtilTest.java
index baab27ff943..764868584d2 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/PluginUtilTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/PluginUtilTest.java
@@ -32,6 +32,6 @@ public class PluginUtilTest
     {
         Properties serviceProperties = new Properties();
         serviceProperties.put(DssPropertyParametersUtil.DSS_CODE_KEY, "dss");
-        return new PluginTaskProviders(serviceProperties, storeRoot);
+        return new PluginTaskProviders(serviceProperties, null, storeRoot);
     }
 }
-- 
GitLab