diff --git a/common/.classpath b/common/.classpath
index 66fbbdbc5cc4b4115ddec3bb62daf7ce4cae25c1..c67ed51f260246203298714f7cb108d6259c0ff5 100644
--- a/common/.classpath
+++ b/common/.classpath
@@ -35,5 +35,10 @@
 	<classpathentry kind="lib" path="/libraries/spring/spring-context.jar"/>
 	<classpathentry kind="lib" path="/libraries/spring/spring-aop.jar" sourcepath="/libraries/spring/src.jar"/>
 	<classpathentry kind="lib" path="/libraries/spring/third-party/aopalliance.jar"/>
+	<classpathentry kind="lib" path="/libraries/jetty7/lib/server/servlet-api-2.5.jar"/>
+	<classpathentry kind="lib" path="/libraries/spring/spring.jar" sourcepath="/libraries/spring/src.jar"/>
+	<classpathentry kind="lib" path="/libraries/spring/webmvc/spring-webmvc.jar" sourcepath="/libraries/spring/webmvc/src.jar"/>
+	<classpathentry kind="lib" path="/libraries/gwt2.4/gwt-servlet.jar"/>
+	<classpathentry kind="lib" path="/libraries/spring/third-party/stream-supporting-httpinvoker.jar" sourcepath="/libraries/spring/third-party/stream-supporting-httpinvoker-src.zip"/>
 	<classpathentry kind="output" path="targets/classes"/>
 </classpath>
diff --git a/common/source/java/ch/systemsx/cisd/common/servlet/AbstractActionLog.java b/common/source/java/ch/systemsx/cisd/common/servlet/AbstractActionLog.java
new file mode 100644
index 0000000000000000000000000000000000000000..d69e358bb8726cee0594f5c3e5c1a2ae9f57327d
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/servlet/AbstractActionLog.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2008 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.common.servlet;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.common.server.IRemoteHostProvider;
+
+/**
+ * Abstract super class of action logs. Action logs are logged by one of the following loggers:
+ * Authentication logger, access logger, and tracking logger.
+ * 
+ * @author Franz-Josef Elmer
+ */
+public abstract class AbstractActionLog implements IActionLog
+{
+    private static final String FAILED = "FAILED";
+
+    private static final String OK = "OK";
+
+    private static final String USER_SESSION_TEMPLATE = "{USER: %s, WEBSESSION: %s} logout%s";
+
+    private static final String USER_HOST_SESSION_TEMPLATE =
+            "{USER: %s, HOST: %s, WEBSESSION: %s} ";
+
+    /** Authentication logger. Name is specified by {@link LogCategory#AUTH}. */
+    protected final Logger authenticationLog = LogFactory.getLogger(LogCategory.AUTH);
+
+    /** Access logger. Name is specified by {@link LogCategory#ACCESS}. */
+    protected final Logger accessLog = LogFactory.getLogger(LogCategory.ACCESS);
+
+    /** Tracking logger. Name is specified by {@link LogCategory#TRACKING}. */
+    protected final Logger trackingLog = LogFactory.getLogger(LogCategory.TRACKING);
+
+    protected final IRequestContextProvider requestContextProvider;
+
+    protected final IRemoteHostProvider remoteHostProvider;
+
+    protected static String getSuccessString(final boolean success)
+    {
+        return success ? OK : FAILED;
+    }
+
+    /**
+     * Creates an instance for the specified request context provider. It is used to provide
+     * {@link HttpSession} in all methods except {@link #logLogout(HttpSession)}.
+     * 
+     * @param requestContextProvider
+     */
+    public AbstractActionLog(final IRequestContextProvider requestContextProvider)
+    {
+        this.requestContextProvider = requestContextProvider;
+        this.remoteHostProvider = new RequestContextProviderAdapter(requestContextProvider);
+    }
+
+    @Override
+    public void logFailedLoginAttempt(final String userCode)
+    {
+        if (authenticationLog.isInfoEnabled())
+        {
+            final String logMessage =
+                    String.format("{USER: %s, HOST: %s} login: FAILED", userCode,
+                            remoteHostProvider.getRemoteHost());
+            authenticationLog.info(logMessage);
+        }
+    }
+
+    @Override
+    public void logSuccessfulLogin()
+    {
+        if (authenticationLog.isInfoEnabled())
+        {
+            final String userHostSessionDescription = getUserHostSessionDescription();
+            authenticationLog.info(userHostSessionDescription + "login: OK");
+        }
+    }
+
+    @Override
+    public void logLogout(final HttpSession httpSession)
+    {
+        if (authenticationLog.isInfoEnabled())
+        {
+            final String userName = getUserCode(httpSession);
+            final String id = httpSession.getId();
+            final long diff = System.currentTimeMillis() - httpSession.getLastAccessedTime();
+            final boolean timedOut = diff / 1000.0 >= httpSession.getMaxInactiveInterval();
+            final String logoutMsg =
+                    String.format(USER_SESSION_TEMPLATE, userName, id,
+                            timedOut ? LogoutReason.SESSION_TIMEOUT.getLogText()
+                                    : LogoutReason.SESSION_LOGOUT.getLogText());
+            authenticationLog.info(logoutMsg);
+        }
+    }
+
+    @Override
+    public void logSetSessionUser(String oldUserCode, String newUserCode, final boolean success)
+    {
+        if (authenticationLog.isInfoEnabled())
+        {
+            authenticationLog.info(getUserHostSessionDescription()
+                    + String.format("set_user_code to '%s': %s", newUserCode,
+                            getSuccessString(success)));
+        }
+    }
+
+    /**
+     * Returns a short description which contains user code, client host, and session id.
+     */
+    protected String getUserHostSessionDescription()
+    {
+        final HttpSession httpSession = getHttpSession();
+        final String remoteHost = remoteHostProvider.getRemoteHost();
+        final String userName;
+        final String id;
+        if (httpSession == null)
+        {
+            userName = "UNKNOWN";
+            id = "UNKNOWN";
+        } else
+        {
+            userName = getUserCode(httpSession);
+            id = httpSession.getId();
+        }
+        return String.format(USER_HOST_SESSION_TEMPLATE, userName, remoteHost, id);
+    }
+
+    /**
+     * Extracts the user code from the specified session.
+     */
+    protected abstract String getUserCode(HttpSession httpSession);
+
+    protected HttpSession getHttpSession()
+    {
+        try
+        {
+            return requestContextProvider.getHttpServletRequest().getSession();
+        } catch (RuntimeException ex)
+        {
+            return null;
+        }
+    }
+
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/servlet/AbstractCrossOriginFilter.java b/common/source/java/ch/systemsx/cisd/common/servlet/AbstractCrossOriginFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5ce1f5fe2e82dc4a2383da2ed9a632dfc2e6e7d
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/servlet/AbstractCrossOriginFilter.java
@@ -0,0 +1,144 @@
+/*
+ * 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.common.servlet;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Abstract filter that implements CORS (Cross Origin Resource Sharing) to allow a web page served
+ * from a given domain to access resources the openBIS AS/DSS.
+ * <p>
+ * NOTE: According to the definition of "origin" the openBIS AS and DSS are two different things
+ * (because they run on different ports). So the {@link AbstractCrossOriginFilter} makes it possible
+ * to share resources between them e.g. access an openBIS AS resource from a web page served by
+ * openBIS DSS.
+ * </p>
+ * <p>
+ * For more details on CORS see
+ * http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/.
+ * 
+ * @author Kaloyan Enimanev
+ */
+public abstract class AbstractCrossOriginFilter implements Filter
+{
+    protected static final String ORIGIN_HEADER = "Origin";
+
+    protected static final String ACCESS_CONTROL_ALLOW_ORIGIN_HEADER =
+            "Access-Control-Allow-Origin";
+
+    protected static final String ALLOWED_ORIGINS_KEY = "TODO";
+
+    private static final String ALLOW_ALL_ORIGINS = "*";
+
+    private FilterConfig filterConfig;
+
+    /**
+     * Returns the openBIS AS and DSS domains.
+     */
+    protected abstract List<String> getOwnDomains();
+
+    /**
+     * Returns a list of configured trusted domains.
+     */
+    protected abstract List<String> getConfiguredTrustedDomains();
+
+    /**
+     * Returns all trusted domains.
+     */
+    protected List<String> getAllTrustedDomains()
+    {
+        List<String> allowedOrigins = new ArrayList<String>();
+        allowedOrigins.addAll(getOwnDomains());
+        allowedOrigins.addAll(getConfiguredTrustedDomains());
+        return allowedOrigins;
+    }
+
+    private boolean isAllowedOrigin(String origin)
+    {
+        for (String allowedOrigin : getAllTrustedDomains())
+        {
+            if (isMatching(allowedOrigin, origin))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    private boolean isMatching(String allowedOrigin, String origin)
+    {
+        if (allowedOrigin.equalsIgnoreCase(origin))
+        {
+            return true;
+        }
+        if (ALLOW_ALL_ORIGINS.equals(allowedOrigin))
+        {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response,
+            FilterChain filterChain)
+            throws IOException, ServletException
+    {
+
+        final HttpServletRequest httpRequest = (HttpServletRequest) request;
+        String originHeader = httpRequest.getHeader(ORIGIN_HEADER);
+
+        if (originHeader != null && isAllowedOrigin(originHeader))
+        {
+            final HttpServletResponse httpResponse = (HttpServletResponse) response;
+            httpResponse.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, originHeader);
+        }
+
+        filterChain.doFilter(request, response);
+    }
+
+    @Override
+    public void destroy()
+    {
+    }
+
+    @Override
+    public void init(FilterConfig fc) throws ServletException
+    {
+        this.filterConfig = fc;
+    }
+
+    /**
+     * Return the servlet context.
+     */
+    protected ServletContext getServletContext()
+    {
+        return filterConfig.getServletContext();
+    }
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/servlet/CISDContextLoaderListener.java b/common/source/java/ch/systemsx/cisd/common/servlet/CISDContextLoaderListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..4259eecad82466c5b5352e22f757f5a3e159cd80
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/servlet/CISDContextLoaderListener.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2007 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.common.servlet;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.reflect.Field;
+import java.util.List;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.apache.log4j.Logger;
+import org.springframework.web.context.ContextLoaderListener;
+import org.springframework.web.util.Log4jConfigListener;
+
+import ch.systemsx.cisd.base.utilities.AbstractBuildAndEnvironmentInfo;
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.common.logging.LogInitializer;
+
+/**
+ * Extension of Spring's <code>ContextLoaderListener</code> which initializes logging via
+ * {@link LogInitializer#init()} and registers an default handler for uncaught exceptions.
+ * 
+ * @see Log4jConfigListener
+ * @see ServletContextListener
+ * @see UncaughtExceptionHandler
+ * @author Christian Ribeaud
+ */
+public final class CISDContextLoaderListener extends ContextLoaderListener
+{
+    private static final Logger statusLog =
+            LogFactory.getLogger(LogCategory.STATUS, CISDContextLoaderListener.class);
+
+    //
+    // ContextLoaderListener
+    //
+
+    @Override
+    public final void contextInitialized(final ServletContextEvent event)
+    {
+        registerDefaultUncaughtExceptionHandler();
+        LogInitializer.init();
+        printBuildAndEnvironmentInfo(event);
+        try
+        {
+            super.contextInitialized(event);
+        } catch (Exception ex)
+        {
+            statusLog.error("Couldn't create application context.", ex);
+        }
+    }
+
+    private void printBuildAndEnvironmentInfo(final ServletContextEvent event)
+    {
+        String nameOfBuildInfoAndEnvironmentClass = event.getServletContext().getInitParameter("infoClass");
+        try
+        {
+            Class<?> clazz = Class.forName(nameOfBuildInfoAndEnvironmentClass);
+            Field field = clazz.getField("INSTANCE");
+            AbstractBuildAndEnvironmentInfo info = (AbstractBuildAndEnvironmentInfo) field.get(null);
+            List<String> environmentInfo = info.getEnvironmentInfo();
+            for (String line : environmentInfo)
+            {
+                statusLog.info(line);
+            }
+        } catch (Exception ex)
+        {
+            statusLog.warn("Couldn't get build and environment info: " + ex);
+        }
+    }
+
+    private void registerDefaultUncaughtExceptionHandler()
+    {
+        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
+            {
+                @Override
+                public void uncaughtException(Thread thread, Throwable th)
+                {
+                    statusLog.error(String.format("An unexpected error occured in thread [%s].",
+                            thread.getName()), th);
+                }
+            });
+    }
+
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/servlet/GWTRPCServiceExporter.java b/common/source/java/ch/systemsx/cisd/common/servlet/GWTRPCServiceExporter.java
new file mode 100644
index 0000000000000000000000000000000000000000..73e66ee791b274f3e93715dad3da8575d3d963a2
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/servlet/GWTRPCServiceExporter.java
@@ -0,0 +1,284 @@
+/*
+ * 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.common.servlet;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.web.context.ServletConfigAware;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.Controller;
+
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.rpc.RPC;
+import com.google.gwt.user.server.rpc.RPCRequest;
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.common.logging.LogInitializer;
+import ch.systemsx.cisd.common.shared.basic.IOptionalStackTraceLoggingException;
+import ch.systemsx.cisd.common.utilities.ClassUtils;
+import ch.systemsx.cisd.common.utilities.MethodUtils;
+
+/**
+ * This component publishes an object (see {@link #getService()}) as a service to the <i>GWT</i>
+ * RPC protocol.
+ * <p>
+ * Inspired by <a href="http://gwt-widget.sourceforge.net/">http://gwt-widget.sourceforge.net/</a>.
+ * </p>
+ * 
+ * @author Christian Ribeaud
+ */
+public abstract class GWTRPCServiceExporter extends RemoteServiceServlet implements
+        InitializingBean, ServletConfigAware, DisposableBean, BeanNameAware, Controller
+{
+    private static final long serialVersionUID = 1L;
+
+    private static final Logger operationLog =
+            LogFactory.getLogger(LogCategory.OPERATION, GWTRPCServiceExporter.class);
+
+    private ServletConfig servletConfig;
+
+    private String beanName;
+
+    private Map<Method, Method> methodCache = new HashMap<Method, Method>();
+
+    private final String invokeMethodOnService(final Method targetMethod,
+            final Object[] targetParameters, final RPCRequest rpcRequest)
+            throws SerializationException
+    {
+        final Object result = ClassUtils.invokeMethod(targetMethod, getService(), targetParameters);
+        return RPC.encodeResponseForSuccess(rpcRequest.getMethod(), result,
+                rpcRequest.getSerializationPolicy());
+    }
+
+    private final synchronized Method getMethodToInvoke(final Method decodedMethod)
+    {
+        Method method = methodCache.get(decodedMethod);
+        if (method != null)
+        {
+            return method;
+        }
+        try
+        {
+            method =
+                    getService().getClass().getMethod(decodedMethod.getName(),
+                            decodedMethod.getParameterTypes());
+            methodCache.put(decodedMethod, method);
+            return method;
+        } catch (final NoSuchMethodException ex)
+        {
+            throw new CheckedExceptionTunnel(ex);
+        }
+    }
+
+    private final static void logException(final Exception e)
+    {
+        operationLog.error(String.format("An '%s' was thrown while processing this call.", e
+                .getClass().getSimpleName()), e);
+    }
+
+    /**
+     * Invoked by {@link #processCall(String)} when RPC throws an
+     * {@link IncompatibleRemoteServiceException}. This implementation propagates the exception
+     * back to the client via RPC.
+     */
+    private final String handleIncompatibleRemoteServiceException(
+            final IncompatibleRemoteServiceException e) throws SerializationException
+    {
+        logException(e);
+        return RPC.encodeResponseForFailure(null, e);
+    }
+
+    /**
+     * Handles exceptions thrown by the target service, which are wrapped in
+     * {@link CheckedExceptionTunnel}s due to
+     * {@link ClassUtils#invokeMethod(Method, Object, Object...)} invocation. This method is invoked
+     * by {@link #processCall(String)}. This implementation encodes exceptions as RPC errors and
+     * returns them. For details on arguments please consult
+     * {@link #invokeMethodOnService(Method, Object[], RPCRequest)}.
+     */
+    private final String handleInvocationException(final RuntimeException e,
+            final Method targetMethod, final RPCRequest rpcRequest) throws Exception
+    {
+        final Exception cause = CheckedExceptionTunnel.unwrapIfNecessary(e);
+        logInvocationException(targetMethod, cause);
+        if (rpcRequest != null)
+        {
+            final String failurePayload =
+                    RPC.encodeResponseForFailure(rpcRequest.getMethod(), cause, rpcRequest
+                            .getSerializationPolicy());
+            return failurePayload;
+        }
+        return RPC.encodeResponseForFailure(null, cause);
+    }
+
+    private void logInvocationException(Method targetMethod, Exception cause)
+    {
+        final String methodDescription =
+                String.format("Invoking method '%s' failed.", targetMethod == null ? "<unknown>"
+                        : MethodUtils.describeMethod(targetMethod));
+        if (cause instanceof IOptionalStackTraceLoggingException)
+        {
+            operationLog.error(methodDescription + ": " + cause.getMessage());
+        } else
+        {
+            operationLog.error(methodDescription, cause);
+        }
+    }
+
+    /**
+     * Invoked by {@link #processCall(String)} for an exception if no suitable exception handler was
+     * found. This is the outermost exception handler, catching any exceptions not caught by other
+     * exception handlers or even thrown by those handlers.
+     */
+    private final String handleExporterProcessingException(final Exception e)
+    {
+        logException(e);
+        throw CheckedExceptionTunnel.wrapIfNecessary(e);
+    }
+
+    /**
+     * The corresponding <i>GWT</i> service object.
+     * <p>
+     * Must be specified in subclasses.
+     * </p>
+     */
+    protected abstract Object getService();
+
+    //
+    // ServletConfigAware
+    //
+
+    @Override
+    public final void setServletConfig(final ServletConfig servletConfig)
+    {
+        assert servletConfig != null;
+        if (operationLog.isTraceEnabled())
+        {
+            final String message =
+                    "Setting servlet config for class '" + getClass().getSimpleName() + "'.";
+            operationLog.trace(message);
+        }
+        this.servletConfig = servletConfig;
+    }
+
+    //
+    // BeanNameAware
+    //
+
+    @Override
+    public final void setBeanName(final String beanName)
+    {
+        this.beanName = beanName;
+    }
+
+    //
+    // InitializingBean
+    //
+
+    /**
+     * Note that {@link #setServletConfig(ServletConfig)} gets called before this method.
+     */
+    @Override
+    public final void afterPropertiesSet() throws Exception
+    {
+        LogInitializer.init();
+        if (getService() == null)
+        {
+            throw new ServletException("You must specify a service object.");
+        }
+        if (operationLog.isTraceEnabled())
+        {
+            final String message =
+                    "All the properties have been set for bean '" + beanName
+                            + "'. Time to initialize this servlet.";
+            operationLog.trace(message);
+        }
+        init(servletConfig);
+    }
+
+    //
+    // RemoteServiceServlet
+    //
+
+    @Override
+    public final ServletConfig getServletConfig()
+    {
+        return servletConfig;
+    }
+
+    @Override
+    protected void doUnexpectedFailure(Throwable throwable)
+    {
+        operationLog.error("Unexpected throwable", throwable);
+        super.doUnexpectedFailure(throwable);
+    }
+
+
+    /**
+     * Overridden from {@link RemoteServiceServlet} and invoked by the servlet code.
+     */
+    @Override
+    public final String processCall(final String payload) throws SerializationException
+    {
+        try
+        {
+            RPCRequest request = null;
+            Method targetMethod = null;
+            try
+            {
+                request = RPC.decodeRequest(payload, getService().getClass(), this);
+                targetMethod = getMethodToInvoke(request.getMethod());
+                final Object[] targetParameters = request.getParameters();
+                return invokeMethodOnService(targetMethod, targetParameters, request);
+            } catch (final RuntimeException e)
+            {
+                return handleInvocationException(e, targetMethod, request);
+            }
+        } catch (final IncompatibleRemoteServiceException e)
+        {
+            return handleIncompatibleRemoteServiceException(e);
+        } catch (final Exception e)
+        {
+            return handleExporterProcessingException(e);
+        }
+    }
+
+    //
+    // Controller
+    //
+
+    @Override
+    public final ModelAndView handleRequest(final HttpServletRequest request,
+            final HttpServletResponse response) throws Exception
+    {
+        doPost(request, response);
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/common/source/java/ch/systemsx/cisd/common/servlet/GWTSpringController.java b/common/source/java/ch/systemsx/cisd/common/servlet/GWTSpringController.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a042cb23bdc4446c3b60d1e6171654f6c31b3e3
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/servlet/GWTSpringController.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2007 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.common.servlet;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.web.context.ServletConfigAware;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.Controller;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.common.logging.LogInitializer;
+
+/**
+ * An abstract <code>RemoteServiceServlet</code> that merges <i>Spring</i>'s {@link Controller}
+ * and manages this servlet the same way <i>Spring</i> beans are.
+ * 
+ * @author Christian Ribeaud
+ */
+public abstract class GWTSpringController extends RemoteServiceServlet implements Controller,
+        InitializingBean, ServletConfigAware, DisposableBean, BeanNameAware, RemoteService
+{
+
+    private static final Logger operationLog =
+            LogFactory.getLogger(LogCategory.OPERATION, GWTSpringController.class);
+
+    private static final long serialVersionUID = 1L;
+
+    private ServletConfig servletConfig;
+
+    private String beanName;
+
+    //
+    // RemoteServiceServlet
+    //
+
+    @Override
+    public String processCall(final String payload) throws SerializationException
+    {
+        try
+        {
+            return super.processCall(payload);
+        } catch (final Throwable th)
+        {
+            operationLog.error("Error processing request.", th);
+            if (th instanceof Error)
+            {
+                throw (Error) th;
+            } else if (th instanceof RuntimeException)
+            {
+                throw (RuntimeException) th;
+            } else
+            {
+                throw new Error("Unexpected error: " + th.getMessage());
+            }
+        }
+    }
+
+    @Override
+    public final ServletConfig getServletConfig()
+    {
+        return servletConfig;
+    }
+
+    //
+    // Controller
+    //
+
+    @Override
+    public final ModelAndView handleRequest(final HttpServletRequest request,
+            final HttpServletResponse response) throws Exception
+    {
+        doPost(request, response);
+        return null;
+    }
+
+    //
+    // InitializingBean
+    //
+
+    /**
+     * Note that {@link #setServletConfig(ServletConfig)} gets called before this method.
+     */
+    @Override
+    public final void afterPropertiesSet() throws Exception
+    {
+        LogInitializer.init();
+        if (operationLog.isTraceEnabled())
+        {
+            final String message =
+                    "All the properties have been set for bean '" + beanName
+                            + "'. Time to initialize this servlet.";
+            operationLog.trace(message);
+        }
+        init(servletConfig);
+    }
+
+    //
+    // ServletConfigAware
+    //
+
+    @Override
+    public final void setServletConfig(final ServletConfig servletConfig)
+    {
+        assert servletConfig != null;
+        if (operationLog.isTraceEnabled())
+        {
+            final String message =
+                    "Setting servlet config for class '" + getClass().getSimpleName() + "'.";
+            operationLog.trace(message);
+        }
+        this.servletConfig = servletConfig;
+    }
+
+    //
+    // BeanNameAware
+    //
+
+    @Override
+    public final void setBeanName(final String beanName)
+    {
+        this.beanName = beanName;
+    }
+
+}
\ No newline at end of file
diff --git a/common/source/java/ch/systemsx/cisd/common/servlet/IActionLog.java b/common/source/java/ch/systemsx/cisd/common/servlet/IActionLog.java
new file mode 100644
index 0000000000000000000000000000000000000000..67993a17c30e09c5be9af8136cea0ef406e2cef0
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/servlet/IActionLog.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2008 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.common.servlet;
+
+import javax.servlet.http.HttpSession;
+
+
+/**
+ * Super interface of all action log classes. Has action methods concerning authentication.
+ *
+ * @author Franz-Josef Elmer
+ */
+public interface IActionLog
+{
+    public enum LogoutReason
+    {
+        SESSION_LOGOUT(""), SESSION_TIMEOUT(" (session timed out)"), USER_DELETED(
+                " (user was removed)");
+
+        private final String logText;
+
+        LogoutReason(String logText)
+        {
+            this.logText = logText;
+        }
+
+        public String getLogText()
+        {
+            return logText;
+        }
+
+    }
+
+    /**
+     * Logs a failed authentication attempt for the specified user code.
+     */
+    public void logFailedLoginAttempt(String userCode);
+
+    /**
+     * Logs success authentication.
+     */
+    public void logSuccessfulLogin();
+
+    /**
+     * Logs a logout.
+     * 
+     * @param httpSession Session objects which might contain information useful to be logged (e.g.
+     *      user to be logged out).
+     */
+    public void logLogout(HttpSession httpSession);
+
+    /**
+     * Logs a call to set a new session user. 
+     */
+    public void logSetSessionUser(String oldUserCode, String newUserCode, final boolean success);
+    
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/servlet/IRequestContextProvider.java b/common/source/java/ch/systemsx/cisd/common/servlet/IRequestContextProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..52425fb6cdc7ed140d0d1c4b977246999b396d23
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/servlet/IRequestContextProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2007 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.common.servlet;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Implementation of this interface provides a way to expose the native {@link HttpServletRequest}.
+ * 
+ * @author Christian Ribeaud
+ */
+public interface IRequestContextProvider
+{
+
+    /** Returns the <code>HttpServletRequest</code> object. */
+    public HttpServletRequest getHttpServletRequest();
+
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/servlet/RequestContextProviderAdapter.java b/common/source/java/ch/systemsx/cisd/common/servlet/RequestContextProviderAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..0700dc7545d7d3458eeacd136d542425b0dd86b4
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/servlet/RequestContextProviderAdapter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2008 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.common.servlet;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang.StringUtils;
+
+import ch.systemsx.cisd.common.server.IRemoteHostProvider;
+
+/**
+ * A <code>IRemoteHostProvider</code> implementation which adapts an encapsulated
+ * <code>IRequestContextProvider</code>.
+ * 
+ * @author Christian Ribeaud
+ */
+public final class RequestContextProviderAdapter implements IRemoteHostProvider
+{
+    private final IRequestContextProvider requestContextProvider;
+
+    public RequestContextProviderAdapter(final IRequestContextProvider requestContextProvider)
+    {
+        assert requestContextProvider != null : "Undefined IRequestContextProvider.";
+        this.requestContextProvider = requestContextProvider;
+    }
+
+    //
+    // IRemoteHostProvider
+    //
+
+    @Override
+    public final String getRemoteHost()
+    {
+        final HttpServletRequest request = requestContextProvider.getHttpServletRequest();
+        if (request == null)
+        {
+            return UNKNOWN;
+        }
+        final String remoteHost = request.getRemoteHost();
+        if (StringUtils.isEmpty(remoteHost))
+        {
+            return StringUtils.defaultIfEmpty(request.getRemoteAddr(), UNKNOWN);
+        }
+        return remoteHost;
+    }
+}
\ No newline at end of file
diff --git a/common/source/java/ch/systemsx/cisd/common/servlet/SpringRequestContextProvider.java b/common/source/java/ch/systemsx/cisd/common/servlet/SpringRequestContextProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd468904075d196e280186fc28ba35c41f0eecce
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/servlet/SpringRequestContextProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 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.common.servlet;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+/**
+ * The <i>Spring</i> implementation of <code>IRequestContextProvider</code>.
+ * <p>
+ * This internally uses <code>RequestContextHolder</code> to do its job.
+ * </p>
+ * 
+ * @author Christian Ribeaud
+ */
+public final class SpringRequestContextProvider implements IRequestContextProvider
+{
+    // This is used only in system tests
+    private HttpServletRequest request;
+
+    //
+    // IRequestContextProvider
+    //
+
+    public void setRequest(HttpServletRequest request)
+    {
+        this.request = request;
+    }
+
+    @Override
+    public final HttpServletRequest getHttpServletRequest()
+    {
+        if (request != null)
+        {
+            return request;
+        }
+        try
+        {
+            ServletRequestAttributes attributes =
+                    (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
+            return attributes.getRequest();
+        } catch (RuntimeException ex)
+        {
+            return null;
+        }
+    }
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/spring/ExposablePropertyPlaceholderConfigurer.java b/common/source/java/ch/systemsx/cisd/common/spring/ExposablePropertyPlaceholderConfigurer.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f58a6ccc9d5e935c3861de0decb7d41b795d1db
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/spring/ExposablePropertyPlaceholderConfigurer.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2008 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.common.spring;
+
+import java.util.HashSet;
+import java.util.Properties;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
+import org.springframework.util.StringUtils;
+
+/**
+ * Bean that should be used instead of the {@link PropertyPlaceholderConfigurer} if you want to have
+ * access to the resolved properties not obligatory from the Spring context. e.g. from JSP or so.
+ * 
+ * @author Christian Ribeaud
+ */
+public final class ExposablePropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer
+{
+    /** Standard bean name in an application context file. */
+    public static final String PROPERTY_CONFIGURER_BEAN_NAME = "propertyConfigurer";
+    
+    private Properties resolvedProps;
+
+    /** Returns the resolved properties. */
+    public final Properties getResolvedProps()
+    {
+        return resolvedProps;
+    }
+
+    //
+    // PropertyPlaceholderConfigurer
+    //
+
+    @Override
+    protected final String convertPropertyValue(final String originalValue)
+    {
+        // Can handle null value
+        return StringUtils.trimWhitespace(originalValue);
+    }
+
+    @Override
+    protected final void processProperties(
+            final ConfigurableListableBeanFactory beanFactoryToProcess, final Properties props)
+            throws BeansException
+    {
+        super.processProperties(beanFactoryToProcess, props);
+        resolvedProps = new Properties();
+        for (final Object key : props.keySet())
+        {
+            final String keyStr = key.toString();
+            resolvedProps.setProperty(keyStr, parseStringValue(props.getProperty(keyStr), props,
+                    new HashSet<Object>()));
+        }
+    }
+}
\ No newline at end of file
diff --git a/common/source/java/ch/systemsx/cisd/common/spring/HttpInvokerUtils.java b/common/source/java/ch/systemsx/cisd/common/spring/HttpInvokerUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..42f35d1e85b51e005889cea02042fd7e39090ff6
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/spring/HttpInvokerUtils.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2009 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.common.spring;
+
+import java.net.InetSocketAddress;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor;
+import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
+
+import com.marathon.util.spring.StreamSupportingHttpInvokerProxyFactoryBean;
+
+/**
+ * Utility methods for HTTP Invocations. <br>
+ * This class is used by clients to create service stubs. If a dependency to external libraries is
+ * added to this class then build scripts used to build the API jar have to be changed as well.
+ * 
+ * @author Franz-Josef Elmer
+ */
+public class HttpInvokerUtils
+{
+    /**
+     * Creates a service stub for the specified service interface.
+     * 
+     * @param serviceURL URL providing the service via HTTP tunneling.
+     * @param serverTimeoutInMillis Service time out in milliseconds. A values of 0 means never
+     *            timeout.
+     */
+    public static <T> T createServiceStub(final Class<T> serviceInterface, final String serviceURL,
+            final long serverTimeoutInMillis)
+    {
+        final HttpInvokerProxyFactoryBean httpInvokerProxy = new HttpInvokerProxyFactoryBean();
+        httpInvokerProxy.setBeanClassLoader(serviceInterface.getClassLoader());
+        httpInvokerProxy.setServiceUrl(serviceURL);
+        httpInvokerProxy.setServiceInterface(serviceInterface);
+        final CommonsHttpInvokerRequestExecutor httpInvokerRequestExecutor =
+                new CommonsHttpInvokerRequestExecutor();
+        httpInvokerRequestExecutor.setReadTimeout((int) serverTimeoutInMillis);
+        final InetSocketAddress proxyAddressOrNull = tryFindProxy(serviceURL);
+        if (proxyAddressOrNull != null)
+        {
+            httpInvokerRequestExecutor.getHttpClient().getHostConfiguration().setProxy(
+                    proxyAddressOrNull.getHostName(), proxyAddressOrNull.getPort());
+        }
+        httpInvokerProxy.setHttpInvokerRequestExecutor(httpInvokerRequestExecutor);
+        httpInvokerProxy.afterPropertiesSet();
+        return getCastedService(httpInvokerProxy);
+    }
+
+    public static <T> T createStreamSupportingServiceStub(final Class<T> serviceInterface,
+            final String serviceURL, final long serverTimeoutInMillis)
+    {
+        final StreamSupportingHttpInvokerProxyFactoryBean httpInvokerProxy =
+                new StreamSupportingHttpInvokerProxyFactoryBean();
+        httpInvokerProxy.setBeanClassLoader(serviceInterface.getClassLoader());
+        httpInvokerProxy.setServiceUrl(serviceURL);
+        httpInvokerProxy.setServiceInterface(serviceInterface);
+        ((CommonsHttpInvokerRequestExecutor) httpInvokerProxy.getHttpInvokerRequestExecutor())
+                .setReadTimeout((int) serverTimeoutInMillis);
+        final InetSocketAddress proxyAddressOrNull = HttpInvokerUtils.tryFindProxy(serviceURL);
+        if (proxyAddressOrNull != null)
+        {
+            ((CommonsHttpInvokerRequestExecutor) httpInvokerProxy.getHttpInvokerRequestExecutor())
+                    .getHttpClient().getHostConfiguration().setProxy(
+                            proxyAddressOrNull.getHostName(), proxyAddressOrNull.getPort());
+        }
+        httpInvokerProxy.afterPropertiesSet();
+        return getCastedService(httpInvokerProxy);
+    }
+
+    @SuppressWarnings("unchecked")
+    private final static <T> T getCastedService(final HttpInvokerProxyFactoryBean httpInvokerProxy)
+    {
+        return (T) httpInvokerProxy.getObject();
+    }
+
+    /**
+     * Returns the proxy's inet address for <var>serviceURL</var>, or <code>null</code>, if no proxy
+     * is defined.
+     */
+    public static InetSocketAddress tryFindProxy(String serviceURL)
+    {
+        try
+        {
+            final ProxySelector selector = ProxySelector.getDefault();
+            final List<java.net.Proxy> proxyList = selector.select(new URI(serviceURL));
+            for (java.net.Proxy proxy : proxyList)
+            {
+                if (java.net.Proxy.Type.HTTP == proxy.type())
+                {
+                    return (InetSocketAddress) proxy.address();
+                }
+            }
+        } catch (IllegalArgumentException e)
+        {
+        } catch (URISyntaxException ex)
+        {
+        }
+        return null;
+    }
+
+    private HttpInvokerUtils()
+    {
+    }
+
+}
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/servlet/AbstractCrossOriginFilterTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/servlet/AbstractCrossOriginFilterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d9e86e8783d0d77637fa921c26dead5d2c69e32
--- /dev/null
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/servlet/AbstractCrossOriginFilterTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.common.servlet;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * @author Kaloyan Enimanev
+ */
+public class AbstractCrossOriginFilterTest extends AssertJUnit
+{
+
+    private Mockery context;
+
+    private HttpServletRequest request;
+
+    private HttpServletResponse response;
+
+    private FilterChain filterChain;
+
+    @BeforeMethod
+    public void setUp()
+    {
+        context = new Mockery();
+        request = context.mock(HttpServletRequest.class);
+        response = context.mock(HttpServletResponse.class);
+        filterChain = context.mock(FilterChain.class);
+    }
+
+    @Test
+    public void testNoAdditionalConfiguration() throws Exception
+    {
+        List<String> ownDomains = Arrays.asList("https://host:8443", "https://host:8444");
+        List<String> trustedDomains = Collections.emptyList();
+
+        CrossOriginFilterImpl filter = new CrossOriginFilterImpl(ownDomains, trustedDomains);
+
+        assertAllowedOrigin(filter, "https://host:8443");
+        assertAllowedOrigin(filter, "https://host:8444");
+
+        assertForbiddenOrigin(filter, "http://host:8444");
+        assertForbiddenOrigin(filter, "http://host:8443");
+        assertForbiddenOrigin(filter, "https://random-host");
+        assertForbiddenOrigin(filter, null);
+    }
+
+    @Test
+    public void testAdditionalConfiguration() throws Exception
+    {
+        List<String> ownDomains = Arrays.asList("https://host:8443", "https://host:8444");
+        List<String> trustedDomains =
+                Arrays.asList("http://otherhost", "http://thirdhost.com:1234");
+
+        CrossOriginFilterImpl filter = new CrossOriginFilterImpl(ownDomains, trustedDomains);
+
+        assertAllowedOrigin(filter, "https://host:8443");
+        assertAllowedOrigin(filter, "https://host:8444");
+        assertAllowedOrigin(filter, "http://otherhost");
+        assertAllowedOrigin(filter, "http://thirdhost.com:1234");
+
+        assertForbiddenOrigin(filter, "http://otherhost:8444");
+        assertForbiddenOrigin(filter, "https://thirdhost.com:1234");
+        assertForbiddenOrigin(filter, "https://randomhost");
+        assertForbiddenOrigin(filter, null);
+    }
+
+    @Test
+    public void testWildCard() throws Exception
+    {
+        List<String> ownDomains = Arrays.asList("https://host:8443", "https://host:8444");
+        List<String> trustedDomains = Arrays.asList("*");
+
+        CrossOriginFilterImpl filter = new CrossOriginFilterImpl(ownDomains, trustedDomains);
+
+        assertAllowedOrigin(filter, "https://host:8443");
+        assertAllowedOrigin(filter, "https://host:8444");
+        assertAllowedOrigin(filter, "http://host:8443");
+        assertAllowedOrigin(filter, "http://host:8444");
+        assertAllowedOrigin(filter, "http://otherhost:8444");
+        assertAllowedOrigin(filter, "https://thirdhost.com:1234");
+
+        assertForbiddenOrigin(filter, null);
+    }
+
+    private void assertAllowedOrigin(CrossOriginFilterImpl filter, final String origin)
+            throws Exception
+    {
+        
+        context.checking(new Expectations() {
+            {
+                    one(request).getHeader(AbstractCrossOriginFilter.ORIGIN_HEADER);
+                    will(returnValue(origin));
+                
+                    // origin allowed
+                    one(response).setHeader(
+                            AbstractCrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, origin);
+                    
+                    one(filterChain).doFilter(request, response);
+            }
+        });
+        filter.doFilter(request, response, filterChain);
+        context.assertIsSatisfied();
+    }
+
+    private void assertForbiddenOrigin(CrossOriginFilterImpl filter, final String origin)
+            throws Exception
+    {
+
+        context.checking(new Expectations()
+            {
+                {
+                    one(request).getHeader(AbstractCrossOriginFilter.ORIGIN_HEADER);
+                    will(returnValue(origin));
+
+                    one(filterChain).doFilter(request, response);
+                }
+            });
+        filter.doFilter(request, response, filterChain);
+    }
+
+    static class CrossOriginFilterImpl extends AbstractCrossOriginFilter
+    {
+
+        private final List<String> ownDomains;
+
+        private final List<String> trustedDomains;
+
+        public CrossOriginFilterImpl(List<String> ownDomains, List<String> trustedDomains)
+        {
+            super();
+            this.ownDomains = ownDomains;
+            this.trustedDomains = trustedDomains;
+        }
+
+        @Override
+        protected List<String> getOwnDomains()
+        {
+            return ownDomains;
+        }
+
+        @Override
+        protected List<String> getConfiguredTrustedDomains()
+        {
+            return trustedDomains;
+        }
+    }
+
+}
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/spring/ExposablePropertyPlaceholderConfigurerTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/spring/ExposablePropertyPlaceholderConfigurerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca287057896aff97b9852b0533c2bd232ee4472d
--- /dev/null
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/spring/ExposablePropertyPlaceholderConfigurerTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2008 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.common.spring;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.util.Properties;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class ExposablePropertyPlaceholderConfigurerTest
+{
+    private Mockery context;
+    private ConfigurableListableBeanFactory beanFactory;
+
+    @BeforeMethod
+    public void setUp()
+    {
+        context = new Mockery();
+        beanFactory = context.mock(ConfigurableListableBeanFactory.class);
+        context.checking(new Expectations()
+            {
+                {
+                    ignoring(beanFactory);
+                }
+            });
+    }
+    
+    @Test
+    public void test()
+    {
+        ExposablePropertyPlaceholderConfigurer configurer = new ExposablePropertyPlaceholderConfigurer();
+        Properties properties = new Properties();
+        properties.setProperty("a", "alpha");
+        properties.setProperty("b", "beta");
+        properties.setProperty("ab", "${a} ${b}");
+        
+        configurer.processProperties(beanFactory, properties);
+        Properties resolvedProps = configurer.getResolvedProps();
+        
+        assertEquals("alpha", resolvedProps.get("a"));
+        assertEquals("beta", resolvedProps.get("b"));
+        assertEquals("alpha beta", resolvedProps.get("ab"));
+    }
+}
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/test/AbstractDependencyCheckingTestCase.java b/common/sourceTest/java/ch/systemsx/cisd/common/test/AbstractDependencyCheckingTestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0110eb7c09402ed7c525064221e58c57a2978da
--- /dev/null
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/test/AbstractDependencyCheckingTestCase.java
@@ -0,0 +1,35 @@
+/*
+ * 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.common.test;
+
+
+/**
+ * Abstract super class of dependency checking tests for web servers where classes files
+ * are in <tt>targets/www/WEB-INF/classes</tt>.
+ *
+ * @author Franz-Josef Elmer
+ */
+public abstract class AbstractDependencyCheckingTestCase extends DependencyCheckingTest
+{
+
+    @Override
+    protected String getPathToClassesCompiledByEclipse()
+    {
+        return "targets/www/WEB-INF/classes";
+    }
+
+}