]> source.dussan.org Git - vaadin-framework.git/commitdiff
Combine ApplicationServlet and AAS and rename to VaadinServlet (#9460)
authorLeif Åstrand <leif@vaadin.com>
Mon, 3 Sep 2012 10:57:20 +0000 (13:57 +0300)
committerLeif Åstrand <leif@vaadin.com>
Mon, 3 Sep 2012 11:05:31 +0000 (14:05 +0300)
19 files changed:
WebContent/WEB-INF/web.xml
client/src/com/vaadin/client/ApplicationConfiguration.java
server/src/com/vaadin/Application.java
server/src/com/vaadin/data/Validator.java
server/src/com/vaadin/server/AbstractApplicationServlet.java [deleted file]
server/src/com/vaadin/server/AbstractCommunicationManager.java
server/src/com/vaadin/server/AbstractErrorMessage.java
server/src/com/vaadin/server/ApplicationServlet.java [deleted file]
server/src/com/vaadin/server/BootstrapHandler.java
server/src/com/vaadin/server/CommunicationManager.java
server/src/com/vaadin/server/GAEApplicationServlet.java [deleted file]
server/src/com/vaadin/server/GAEVaadinServlet.java [new file with mode: 0644]
server/src/com/vaadin/server/ServletApplicationContext.java
server/src/com/vaadin/server/SystemError.java
server/src/com/vaadin/server/VaadinServlet.java [new file with mode: 0644]
server/src/com/vaadin/server/WebBrowser.java
server/src/com/vaadin/ui/UI.java
server/tests/src/com/vaadin/server/TestAbstractApplicationServletStaticFilesLocation.java
uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java

index 58a125c10f9b8d9c425e2604eddf9e68e9334db4..70c875b10cd6627062c076aad271e75ba75a850d 100644 (file)
                <servlet-class>com.vaadin.launcher.ApplicationRunnerServlet</servlet-class>
        </servlet>
 
-       <!-- For testing GAE - the deployment script changes this to use GAEApplicationServlet -->
+       <!-- For testing GAE - the deployment script changes this to use GAEVaadinServlet -->
        <servlet>
                <servlet-name>IntegrationTest</servlet-name>
-               <servlet-class>com.vaadin.server.ApplicationServlet</servlet-class>
+               <servlet-class>com.vaadin.server.VaadinServlet</servlet-class>
                <init-param>
                        <param-name>application</param-name>
                        <param-value>com.vaadin.tests.integration.IntegrationTestApplication</param-value>
index 0bb9bf1989d774b1ae2e64acbab3ad24d6b03c72..f620a39a70d7ea1b76a110f5be493963ceb0960a 100644 (file)
@@ -146,7 +146,7 @@ public class ApplicationConfiguration implements EntryPoint {
          * 
          * @return a string with the version
          * 
-         * @see com.vaadin.server.AbstractApplicationServlet#VERSION
+         * @see com.vaadin.server.VaadinServlet#VERSION
          */
         private native String getVaadinVersion()
         /*-{
index 8a043154d789d564d7f411a39cc1b8a5f702ca87..2af3c6a59ff329b8ddadeefa8e14442ec4de443b 100644 (file)
@@ -48,7 +48,6 @@ import com.vaadin.data.util.converter.Converter;
 import com.vaadin.data.util.converter.ConverterFactory;
 import com.vaadin.data.util.converter.DefaultConverterFactory;
 import com.vaadin.event.EventRouter;
-import com.vaadin.server.AbstractApplicationServlet;
 import com.vaadin.server.AbstractErrorMessage;
 import com.vaadin.server.ApplicationContext;
 import com.vaadin.server.BootstrapFragmentResponse;
@@ -64,6 +63,7 @@ import com.vaadin.server.RequestHandler;
 import com.vaadin.server.ServletApplicationContext;
 import com.vaadin.server.Terminal;
 import com.vaadin.server.UIProvider;
+import com.vaadin.server.VaadinServlet;
 import com.vaadin.server.VariableOwner;
 import com.vaadin.server.WrappedRequest;
 import com.vaadin.server.WrappedRequest.BrowserDetails;
@@ -1728,7 +1728,7 @@ public class Application implements Terminal.ErrorListener, Serializable {
      * Handles a request by passing it to each registered {@link RequestHandler}
      * in turn until one produces a response. This method is used for requests
      * that have not been handled by any specific functionality in the terminal
-     * implementation (e.g. {@link AbstractApplicationServlet}).
+     * implementation (e.g. {@link VaadinServlet}).
      * <p>
      * The request handlers are invoked in the revere order in which they were
      * added to the application until a response has been produced. This means
index 1755a44920a945ec2daadfc67f332ca7cc2545f6..421d88f5740e06a2e122a37a163c83f77c35bb11 100644 (file)
@@ -18,7 +18,7 @@ package com.vaadin.data;
 
 import java.io.Serializable;
 
-import com.vaadin.server.AbstractApplicationServlet;
+import com.vaadin.server.VaadinServlet;
 
 /**
  * Interface that implements a method for validating if an {@link Object} is
@@ -144,8 +144,7 @@ public interface Validator extends Serializable {
          * Note that this API may change in future versions.
          */
         public String getHtmlMessage() {
-            return AbstractApplicationServlet
-                    .safeEscapeForHtml(getLocalizedMessage());
+            return VaadinServlet.safeEscapeForHtml(getLocalizedMessage());
         }
 
         /**
diff --git a/server/src/com/vaadin/server/AbstractApplicationServlet.java b/server/src/com/vaadin/server/AbstractApplicationServlet.java
deleted file mode 100644 (file)
index 8b3103b..0000000
+++ /dev/null
@@ -1,1557 +0,0 @@
-/*
- * Copyright 2011 Vaadin Ltd.
- * 
- * 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 com.vaadin.server;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Serializable;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.security.GeneralSecurityException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Properties;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import com.vaadin.Application;
-import com.vaadin.Application.ApplicationStartEvent;
-import com.vaadin.server.AbstractCommunicationManager.Callback;
-import com.vaadin.shared.ApplicationConstants;
-import com.vaadin.ui.UI;
-
-/**
- * Abstract implementation of the ApplicationServlet which handles all
- * communication between the client and the server.
- * 
- * It is possible to extend this class to provide own functionality but in most
- * cases this is unnecessary.
- * 
- * 
- * @author Vaadin Ltd.
- * @since 6.0
- */
-
-@SuppressWarnings("serial")
-public abstract class AbstractApplicationServlet extends HttpServlet implements
-        Constants {
-
-    private static class AbstractApplicationServletWrapper implements Callback {
-
-        private final AbstractApplicationServlet servlet;
-
-        public AbstractApplicationServletWrapper(
-                AbstractApplicationServlet servlet) {
-            this.servlet = servlet;
-        }
-
-        @Override
-        public void criticalNotification(WrappedRequest request,
-                WrappedResponse response, String cap, String msg,
-                String details, String outOfSyncURL) throws IOException {
-            servlet.criticalNotification(
-                    WrappedHttpServletRequest.cast(request),
-                    ((WrappedHttpServletResponse) response), cap, msg, details,
-                    outOfSyncURL);
-        }
-    }
-
-    // TODO Move some (all?) of the constants to a separate interface (shared
-    // with portlet)
-
-    private final String resourcePath = null;
-
-    private DeploymentConfiguration deploymentConfiguration;
-
-    private AddonContext addonContext;
-
-    /**
-     * Called by the servlet container to indicate to a servlet that the servlet
-     * is being placed into service.
-     * 
-     * @param servletConfig
-     *            the object containing the servlet's configuration and
-     *            initialization parameters
-     * @throws javax.servlet.ServletException
-     *             if an exception has occurred that interferes with the
-     *             servlet's normal operation.
-     */
-    @Override
-    public void init(javax.servlet.ServletConfig servletConfig)
-            throws javax.servlet.ServletException {
-        super.init(servletConfig);
-        Properties applicationProperties = new Properties();
-
-        // Read default parameters from server.xml
-        final ServletContext context = servletConfig.getServletContext();
-        for (final Enumeration<String> e = context.getInitParameterNames(); e
-                .hasMoreElements();) {
-            final String name = e.nextElement();
-            applicationProperties.setProperty(name,
-                    context.getInitParameter(name));
-        }
-
-        // Override with application config from web.xml
-        for (final Enumeration<String> e = servletConfig
-                .getInitParameterNames(); e.hasMoreElements();) {
-            final String name = e.nextElement();
-            applicationProperties.setProperty(name,
-                    servletConfig.getInitParameter(name));
-        }
-
-        deploymentConfiguration = new AbstractDeploymentConfiguration(
-                getClass(), applicationProperties) {
-
-            @Override
-            public String getStaticFileLocation(WrappedRequest request) {
-                HttpServletRequest servletRequest = WrappedHttpServletRequest
-                        .cast(request);
-                return AbstractApplicationServlet.this
-                        .getStaticFilesLocation(servletRequest);
-            }
-
-            @Override
-            public String getConfiguredWidgetset(WrappedRequest request) {
-                return getApplicationOrSystemProperty(
-                        AbstractApplicationServlet.PARAMETER_WIDGETSET,
-                        AbstractApplicationServlet.DEFAULT_WIDGETSET);
-            }
-
-            @Override
-            public String getConfiguredTheme(WrappedRequest request) {
-                // Use the default
-                return AbstractApplicationServlet.getDefaultTheme();
-            }
-
-            @Override
-            public boolean isStandalone(WrappedRequest request) {
-                return true;
-            }
-
-            @Override
-            public String getMimeType(String resourceName) {
-                return getServletContext().getMimeType(resourceName);
-            }
-
-            @Override
-            public SystemMessages getSystemMessages() {
-                return AbstractApplicationServlet.this.getSystemMessages();
-            }
-        };
-
-        addonContext = new AddonContext(deploymentConfiguration);
-        addonContext.init();
-    }
-
-    @Override
-    public void destroy() {
-        super.destroy();
-
-        addonContext.destroy();
-    }
-
-    /**
-     * Returns true if the servlet is running in production mode. Production
-     * mode disables all debug facilities.
-     * 
-     * @return true if in production mode, false if in debug mode
-     */
-    public boolean isProductionMode() {
-        return getDeploymentConfiguration().isProductionMode();
-    }
-
-    /**
-     * Returns the number of seconds the browser should cache a file. Default is
-     * 1 hour (3600 s).
-     * 
-     * @return The number of seconds files are cached in the browser
-     */
-    public int getResourceCacheTime() {
-        return getDeploymentConfiguration().getResourceCacheTime();
-    }
-
-    /**
-     * Receives standard HTTP requests from the public service method and
-     * dispatches them.
-     * 
-     * @param request
-     *            the object that contains the request the client made of the
-     *            servlet.
-     * @param response
-     *            the object that contains the response the servlet returns to
-     *            the client.
-     * @throws ServletException
-     *             if an input or output error occurs while the servlet is
-     *             handling the TRACE request.
-     * @throws IOException
-     *             if the request for the TRACE cannot be handled.
-     */
-
-    @Override
-    protected void service(HttpServletRequest request,
-            HttpServletResponse response) throws ServletException, IOException {
-        service(createWrappedRequest(request), createWrappedResponse(response));
-    }
-
-    private void service(WrappedHttpServletRequest request,
-            WrappedHttpServletResponse response) throws ServletException,
-            IOException {
-        RequestTimer requestTimer = new RequestTimer();
-        requestTimer.start();
-
-        AbstractApplicationServletWrapper servletWrapper = new AbstractApplicationServletWrapper(
-                this);
-
-        RequestType requestType = getRequestType(request);
-        if (!ensureCookiesEnabled(requestType, request, response)) {
-            return;
-        }
-
-        if (requestType == RequestType.STATIC_FILE) {
-            serveStaticResources(request, response);
-            return;
-        }
-
-        Application application = null;
-        boolean transactionStarted = false;
-        boolean requestStarted = false;
-        boolean applicationRunning = false;
-
-        try {
-            // If a duplicate "close application" URL is received for an
-            // application that is not open, redirect to the application's main
-            // page.
-            // This is needed as e.g. Spring Security remembers the last
-            // URL from the application, which is the logout URL, and repeats
-            // it.
-            // We can tell apart a real onunload request from a repeated one
-            // based on the real one having content (at least the UIDL security
-            // key).
-            if (requestType == RequestType.UIDL
-                    && request.getParameterMap().containsKey(
-                            ApplicationConstants.PARAM_UNLOADBURST)
-                    && request.getContentLength() < 1
-                    && getExistingApplication(request, false) == null) {
-                redirectToApplication(request, response);
-                return;
-            }
-
-            // Find out which application this request is related to
-            application = findApplicationInstance(request, requestType);
-            if (application == null) {
-                return;
-            }
-            Application.setCurrent(application);
-
-            /*
-             * Get or create a WebApplicationContext and an ApplicationManager
-             * for the session
-             */
-            ServletApplicationContext webApplicationContext = getApplicationContext(request
-                    .getSession());
-            CommunicationManager applicationManager = webApplicationContext
-                    .getApplicationManager(application, this);
-
-            if (requestType == RequestType.CONNECTOR_RESOURCE) {
-                applicationManager.serveConnectorResource(request, response);
-                return;
-            } else if (requestType == RequestType.HEARTBEAT) {
-                applicationManager.handleHeartbeatRequest(request, response,
-                        application);
-                return;
-            }
-
-            /* Update browser information from the request */
-            webApplicationContext.getBrowser().updateRequestDetails(request);
-
-            /*
-             * Call application requestStart before Application.init() is called
-             * (bypasses the limitation in TransactionListener)
-             */
-            if (application instanceof HttpServletRequestListener) {
-                ((HttpServletRequestListener) application).onRequestStart(
-                        request, response);
-                requestStarted = true;
-            }
-
-            // Start the application if it's newly created
-            startApplication(request, application, webApplicationContext);
-            applicationRunning = true;
-
-            /*
-             * Transaction starts. Call transaction listeners. Transaction end
-             * is called in the finally block below.
-             */
-            webApplicationContext.startTransaction(application, request);
-            transactionStarted = true;
-
-            /* Handle the request */
-            if (requestType == RequestType.FILE_UPLOAD) {
-                // UI is resolved in communication manager
-                applicationManager.handleFileUpload(application, request,
-                        response);
-                return;
-            } else if (requestType == RequestType.UIDL) {
-                UI uI = application.getUIForRequest(request);
-                if (uI == null) {
-                    throw new ServletException(ERROR_NO_UI_FOUND);
-                }
-                // Handles AJAX UIDL requests
-                applicationManager.handleUidlRequest(request, response,
-                        servletWrapper, uI);
-                return;
-            } else if (requestType == RequestType.BROWSER_DETAILS) {
-                // Browser details - not related to a specific UI
-                applicationManager.handleBrowserDetailsRequest(request,
-                        response, application);
-                return;
-            }
-
-            // Removes application if it has stopped (maybe by thread or
-            // transactionlistener)
-            if (!application.isRunning()) {
-                endApplication(request, response, application);
-                return;
-            }
-
-            if (applicationManager.handleApplicationRequest(request, response)) {
-                return;
-            }
-            // TODO Should return 404 error here and not do anything more
-
-        } catch (final SessionExpiredException e) {
-            // Session has expired, notify user
-            handleServiceSessionExpired(request, response);
-        } catch (final GeneralSecurityException e) {
-            handleServiceSecurityException(request, response);
-        } catch (final Throwable e) {
-            handleServiceException(request, response, application, e);
-        } finally {
-
-            if (applicationRunning) {
-                application.closeInactiveUIs();
-            }
-
-            // Notifies transaction end
-            try {
-                if (transactionStarted) {
-                    ((ServletApplicationContext) application.getContext())
-                            .endTransaction(application, request);
-
-                }
-
-            } finally {
-                try {
-                    if (requestStarted) {
-                        ((HttpServletRequestListener) application)
-                                .onRequestEnd(request, response);
-                    }
-                } finally {
-                    UI.setCurrent(null);
-                    Application.setCurrent(null);
-
-                    HttpSession session = request.getSession(false);
-                    if (session != null) {
-                        requestTimer.stop(getApplicationContext(session));
-                    }
-                }
-            }
-
-        }
-    }
-
-    private WrappedHttpServletResponse createWrappedResponse(
-            HttpServletResponse response) {
-        WrappedHttpServletResponse wrappedResponse = new WrappedHttpServletResponse(
-                response, getDeploymentConfiguration());
-        return wrappedResponse;
-    }
-
-    /**
-     * Create a wrapped request for a http servlet request. This method can be
-     * overridden if the wrapped request should have special properties.
-     * 
-     * @param request
-     *            the original http servlet request
-     * @return a wrapped request for the original request
-     */
-    protected WrappedHttpServletRequest createWrappedRequest(
-            HttpServletRequest request) {
-        return new WrappedHttpServletRequest(request,
-                getDeploymentConfiguration());
-    }
-
-    /**
-     * Gets a the deployment configuration for this servlet.
-     * 
-     * @return the deployment configuration
-     */
-    protected DeploymentConfiguration getDeploymentConfiguration() {
-        return deploymentConfiguration;
-    }
-
-    /**
-     * Check that cookie support is enabled in the browser. Only checks UIDL
-     * requests.
-     * 
-     * @param requestType
-     *            Type of the request as returned by
-     *            {@link #getRequestType(HttpServletRequest)}
-     * @param request
-     *            The request from the browser
-     * @param response
-     *            The response to which an error can be written
-     * @return false if cookies are disabled, true otherwise
-     * @throws IOException
-     */
-    private boolean ensureCookiesEnabled(RequestType requestType,
-            WrappedHttpServletRequest request,
-            WrappedHttpServletResponse response) throws IOException {
-        if (requestType == RequestType.UIDL && !isRepaintAll(request)) {
-            // In all other but the first UIDL request a cookie should be
-            // returned by the browser.
-            // This can be removed if cookieless mode (#3228) is supported
-            if (request.getRequestedSessionId() == null) {
-                // User has cookies disabled
-                criticalNotification(request, response, getSystemMessages()
-                        .getCookiesDisabledCaption(), getSystemMessages()
-                        .getCookiesDisabledMessage(), null, getSystemMessages()
-                        .getCookiesDisabledURL());
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Send a notification to client's application. Used to notify client of
-     * critical errors, session expiration and more. Server has no knowledge of
-     * what application client refers to.
-     * 
-     * @param request
-     *            the HTTP request instance.
-     * @param response
-     *            the HTTP response to write to.
-     * @param caption
-     *            the notification caption
-     * @param message
-     *            to notification body
-     * @param details
-     *            a detail message to show in addition to the message. Currently
-     *            shown directly below the message but could be hidden behind a
-     *            details drop down in the future. Mainly used to give
-     *            additional information not necessarily useful to the end user.
-     * @param url
-     *            url to load when the message is dismissed. Null will reload
-     *            the current page.
-     * @throws IOException
-     *             if the writing failed due to input/output error.
-     */
-    protected void criticalNotification(WrappedHttpServletRequest request,
-            HttpServletResponse response, String caption, String message,
-            String details, String url) throws IOException {
-
-        if (ServletPortletHelper.isUIDLRequest(request)) {
-
-            if (caption != null) {
-                caption = "\"" + JsonPaintTarget.escapeJSON(caption) + "\"";
-            }
-            if (details != null) {
-                if (message == null) {
-                    message = details;
-                } else {
-                    message += "<br/><br/>" + details;
-                }
-            }
-
-            if (message != null) {
-                message = "\"" + JsonPaintTarget.escapeJSON(message) + "\"";
-            }
-            if (url != null) {
-                url = "\"" + JsonPaintTarget.escapeJSON(url) + "\"";
-            }
-
-            String output = "for(;;);[{\"changes\":[], \"meta\" : {"
-                    + "\"appError\": {" + "\"caption\":" + caption + ","
-                    + "\"message\" : " + message + "," + "\"url\" : " + url
-                    + "}}, \"resources\": {}, \"locales\":[]}]";
-            writeResponse(response, "application/json; charset=UTF-8", output);
-        } else {
-            // Create an HTML reponse with the error
-            String output = "";
-
-            if (url != null) {
-                output += "<a href=\"" + url + "\">";
-            }
-            if (caption != null) {
-                output += "<b>" + caption + "</b><br/>";
-            }
-            if (message != null) {
-                output += message;
-                output += "<br/><br/>";
-            }
-
-            if (details != null) {
-                output += details;
-                output += "<br/><br/>";
-            }
-            if (url != null) {
-                output += "</a>";
-            }
-            writeResponse(response, "text/html; charset=UTF-8", output);
-
-        }
-
-    }
-
-    /**
-     * Writes the response in {@code output} using the contentType given in
-     * {@code contentType} to the provided {@link HttpServletResponse}
-     * 
-     * @param response
-     * @param contentType
-     * @param output
-     *            Output to write (UTF-8 encoded)
-     * @throws IOException
-     */
-    private void writeResponse(HttpServletResponse response,
-            String contentType, String output) throws IOException {
-        response.setContentType(contentType);
-        final ServletOutputStream out = response.getOutputStream();
-        // Set the response type
-        final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
-                new OutputStreamWriter(out, "UTF-8")));
-        outWriter.print(output);
-        outWriter.flush();
-        outWriter.close();
-        out.flush();
-
-    }
-
-    /**
-     * Returns the application instance to be used for the request. If an
-     * existing instance is not found a new one is created or null is returned
-     * to indicate that the application is not available.
-     * 
-     * @param request
-     * @param requestType
-     * @return
-     * @throws MalformedURLException
-     * @throws IllegalAccessException
-     * @throws InstantiationException
-     * @throws ServletException
-     * @throws SessionExpiredException
-     */
-    private Application findApplicationInstance(HttpServletRequest request,
-            RequestType requestType) throws MalformedURLException,
-            ServletException, SessionExpiredException {
-
-        boolean requestCanCreateApplication = requestCanCreateApplication(
-                request, requestType);
-
-        /* Find an existing application for this request. */
-        Application application = getExistingApplication(request,
-                requestCanCreateApplication);
-
-        if (application != null) {
-            /*
-             * There is an existing application. We can use this as long as the
-             * user not specifically requested to close or restart it.
-             */
-
-            final boolean restartApplication = (request
-                    .getParameter(URL_PARAMETER_RESTART_APPLICATION) != null);
-            final boolean closeApplication = (request
-                    .getParameter(URL_PARAMETER_CLOSE_APPLICATION) != null);
-
-            if (restartApplication) {
-                closeApplication(application, request.getSession(false));
-                return createApplication(request);
-            } else if (closeApplication) {
-                closeApplication(application, request.getSession(false));
-                return null;
-            } else {
-                return application;
-            }
-        }
-
-        // No existing application was found
-
-        if (requestCanCreateApplication) {
-            /*
-             * If the request is such that it should create a new application if
-             * one as not found, we do that.
-             */
-            return createApplication(request);
-        } else {
-            /*
-             * The application was not found and a new one should not be
-             * created. Assume the session has expired.
-             */
-            throw new SessionExpiredException();
-        }
-
-    }
-
-    /**
-     * Check if the request should create an application if an existing
-     * application is not found.
-     * 
-     * @param request
-     * @param requestType
-     * @return true if an application should be created, false otherwise
-     */
-    boolean requestCanCreateApplication(HttpServletRequest request,
-            RequestType requestType) {
-        if (requestType == RequestType.UIDL && isRepaintAll(request)) {
-            /*
-             * UIDL request contains valid repaintAll=1 event, the user probably
-             * wants to initiate a new application through a custom index.html
-             * without using the bootstrap page.
-             */
-            return true;
-
-        } else if (requestType == RequestType.OTHER) {
-            /*
-             * I.e URIs that are not application resources or static (theme)
-             * files.
-             */
-            return true;
-
-        }
-
-        return false;
-    }
-
-    /**
-     * Gets resource path using different implementations. Required to
-     * supporting different servlet container implementations (application
-     * servers).
-     * 
-     * @param servletContext
-     * @param path
-     *            the resource path.
-     * @return the resource path.
-     */
-    protected static String getResourcePath(ServletContext servletContext,
-            String path) {
-        String resultPath = null;
-        resultPath = servletContext.getRealPath(path);
-        if (resultPath != null) {
-            return resultPath;
-        } else {
-            try {
-                final URL url = servletContext.getResource(path);
-                resultPath = url.getFile();
-            } catch (final Exception e) {
-                // FIXME: Handle exception
-                getLogger().log(Level.INFO,
-                        "Could not find resource path " + path, e);
-            }
-        }
-        return resultPath;
-    }
-
-    /**
-     * Creates a new application and registers it into WebApplicationContext
-     * (aka session). This is not meant to be overridden. Override
-     * getNewApplication to create the application instance in a custom way.
-     * 
-     * @param request
-     * @return
-     * @throws ServletException
-     * @throws MalformedURLException
-     */
-    private Application createApplication(HttpServletRequest request)
-            throws ServletException, MalformedURLException {
-        Application newApplication = getNewApplication(request);
-
-        final ServletApplicationContext context = getApplicationContext(request
-                .getSession());
-        context.addApplication(newApplication);
-
-        return newApplication;
-    }
-
-    private void handleServiceException(WrappedHttpServletRequest request,
-            WrappedHttpServletResponse response, Application application,
-            Throwable e) throws IOException, ServletException {
-        // if this was an UIDL request, response UIDL back to client
-        if (getRequestType(request) == RequestType.UIDL) {
-            SystemMessages ci = getSystemMessages();
-            criticalNotification(request, response,
-                    ci.getInternalErrorCaption(), ci.getInternalErrorMessage(),
-                    null, ci.getInternalErrorURL());
-            if (application != null) {
-                application.getErrorHandler()
-                        .terminalError(new RequestError(e));
-            } else {
-                throw new ServletException(e);
-            }
-        } else {
-            // Re-throw other exceptions
-            throw new ServletException(e);
-        }
-
-    }
-
-    /**
-     * A helper method to strip away characters that might somehow be used for
-     * XSS attacs. Leaves at least alphanumeric characters intact. Also removes
-     * eg. ( and ), so values should be safe in javascript too.
-     * 
-     * @param themeName
-     * @return
-     */
-    protected static String stripSpecialChars(String themeName) {
-        StringBuilder sb = new StringBuilder();
-        char[] charArray = themeName.toCharArray();
-        for (int i = 0; i < charArray.length; i++) {
-            char c = charArray[i];
-            if (!CHAR_BLACKLIST.contains(c)) {
-                sb.append(c);
-            }
-        }
-        return sb.toString();
-    }
-
-    private static final Collection<Character> CHAR_BLACKLIST = new HashSet<Character>(
-            Arrays.asList(new Character[] { '&', '"', '\'', '<', '>', '(', ')',
-                    ';' }));
-
-    /**
-     * Returns the default theme. Must never return null.
-     * 
-     * @return
-     */
-    public static String getDefaultTheme() {
-        return DEFAULT_THEME_NAME;
-    }
-
-    void handleServiceSessionExpired(WrappedHttpServletRequest request,
-            WrappedHttpServletResponse response) throws IOException,
-            ServletException {
-
-        if (isOnUnloadRequest(request)) {
-            /*
-             * Request was an unload request (e.g. window close event) and the
-             * client expects no response if it fails.
-             */
-            return;
-        }
-
-        try {
-            SystemMessages ci = getSystemMessages();
-            if (getRequestType(request) != RequestType.UIDL) {
-                // 'plain' http req - e.g. browser reload;
-                // just go ahead redirect the browser
-                response.sendRedirect(ci.getSessionExpiredURL());
-            } else {
-                /*
-                 * Invalidate session (weird to have session if we're saying
-                 * that it's expired, and worse: portal integration will fail
-                 * since the session is not created by the portal.
-                 * 
-                 * Session must be invalidated before criticalNotification as it
-                 * commits the response.
-                 */
-                request.getSession().invalidate();
-
-                // send uidl redirect
-                criticalNotification(request, response,
-                        ci.getSessionExpiredCaption(),
-                        ci.getSessionExpiredMessage(), null,
-                        ci.getSessionExpiredURL());
-
-            }
-        } catch (SystemMessageException ee) {
-            throw new ServletException(ee);
-        }
-
-    }
-
-    private void handleServiceSecurityException(
-            WrappedHttpServletRequest request,
-            WrappedHttpServletResponse response) throws IOException,
-            ServletException {
-        if (isOnUnloadRequest(request)) {
-            /*
-             * Request was an unload request (e.g. window close event) and the
-             * client expects no response if it fails.
-             */
-            return;
-        }
-
-        try {
-            SystemMessages ci = getSystemMessages();
-            if (getRequestType(request) != RequestType.UIDL) {
-                // 'plain' http req - e.g. browser reload;
-                // just go ahead redirect the browser
-                response.sendRedirect(ci.getCommunicationErrorURL());
-            } else {
-                // send uidl redirect
-                criticalNotification(request, response,
-                        ci.getCommunicationErrorCaption(),
-                        ci.getCommunicationErrorMessage(),
-                        INVALID_SECURITY_KEY_MSG, ci.getCommunicationErrorURL());
-                /*
-                 * Invalidate session. Portal integration will fail otherwise
-                 * since the session is not created by the portal.
-                 */
-                request.getSession().invalidate();
-            }
-        } catch (SystemMessageException ee) {
-            throw new ServletException(ee);
-        }
-
-        log("Invalid security key received from " + request.getRemoteHost());
-    }
-
-    /**
-     * Creates a new application for the given request.
-     * 
-     * @param request
-     *            the HTTP request.
-     * @return A new Application instance.
-     * @throws ServletException
-     */
-    protected abstract Application getNewApplication(HttpServletRequest request)
-            throws ServletException;
-
-    /**
-     * Starts the application if it is not already running.
-     * 
-     * @param request
-     * @param application
-     * @param webApplicationContext
-     * @throws ServletException
-     * @throws MalformedURLException
-     */
-    private void startApplication(HttpServletRequest request,
-            Application application, ServletApplicationContext webApplicationContext)
-            throws ServletException, MalformedURLException {
-
-        if (!application.isRunning()) {
-            // Create application
-            final URL applicationUrl = getApplicationUrl(request);
-
-            // Initial locale comes from the request
-            Locale locale = request.getLocale();
-            application.setLocale(locale);
-            application.start(new ApplicationStartEvent(applicationUrl,
-                    getDeploymentConfiguration(), webApplicationContext));
-            addonContext.fireApplicationStarted(application);
-        }
-    }
-
-    /**
-     * Check if this is a request for a static resource and, if it is, serve the
-     * resource to the client.
-     * 
-     * @param request
-     * @param response
-     * @return true if a file was served and the request has been handled, false
-     *         otherwise.
-     * @throws IOException
-     * @throws ServletException
-     */
-    private boolean serveStaticResources(HttpServletRequest request,
-            HttpServletResponse response) throws IOException, ServletException {
-
-        // FIXME What does 10 refer to?
-        String pathInfo = request.getPathInfo();
-        if (pathInfo == null || pathInfo.length() <= 10) {
-            return false;
-        }
-
-        if ((request.getContextPath() != null)
-                && (request.getRequestURI().startsWith("/VAADIN/"))) {
-            serveStaticResourcesInVAADIN(request.getRequestURI(), request,
-                    response);
-            return true;
-        } else if (request.getRequestURI().startsWith(
-                request.getContextPath() + "/VAADIN/")) {
-            serveStaticResourcesInVAADIN(
-                    request.getRequestURI().substring(
-                            request.getContextPath().length()), request,
-                    response);
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Serve resources from VAADIN directory.
-     * 
-     * @param filename
-     *            The filename to serve. Should always start with /VAADIN/.
-     * @param request
-     * @param response
-     * @throws IOException
-     * @throws ServletException
-     */
-    private void serveStaticResourcesInVAADIN(String filename,
-            HttpServletRequest request, HttpServletResponse response)
-            throws IOException, ServletException {
-
-        final ServletContext sc = getServletContext();
-        URL resourceUrl = sc.getResource(filename);
-        if (resourceUrl == null) {
-            // try if requested file is found from classloader
-
-            // strip leading "/" otherwise stream from JAR wont work
-            filename = filename.substring(1);
-            resourceUrl = getDeploymentConfiguration().getClassLoader()
-                    .getResource(filename);
-
-            if (resourceUrl == null) {
-                // cannot serve requested file
-                getLogger()
-                        .info("Requested resource ["
-                                + filename
-                                + "] not found from filesystem or through class loader."
-                                + " Add widgetset and/or theme JAR to your classpath or add files to WebContent/VAADIN folder.");
-                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
-                return;
-            }
-
-            // security check: do not permit navigation out of the VAADIN
-            // directory
-            if (!isAllowedVAADINResourceUrl(request, resourceUrl)) {
-                getLogger()
-                        .info("Requested resource ["
-                                + filename
-                                + "] not accessible in the VAADIN directory or access to it is forbidden.");
-                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-                return;
-            }
-        }
-
-        // Find the modification timestamp
-        long lastModifiedTime = 0;
-        URLConnection connection = null;
-        try {
-            connection = resourceUrl.openConnection();
-            lastModifiedTime = connection.getLastModified();
-            // Remove milliseconds to avoid comparison problems (milliseconds
-            // are not returned by the browser in the "If-Modified-Since"
-            // header).
-            lastModifiedTime = lastModifiedTime - lastModifiedTime % 1000;
-
-            if (browserHasNewestVersion(request, lastModifiedTime)) {
-                response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
-                return;
-            }
-        } catch (Exception e) {
-            // Failed to find out last modified timestamp. Continue without it.
-            getLogger()
-                    .log(Level.FINEST,
-                            "Failed to find out last modified timestamp. Continuing without it.",
-                            e);
-        } finally {
-            if (connection instanceof URLConnection) {
-                try {
-                    // Explicitly close the input stream to prevent it
-                    // from remaining hanging
-                    // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4257700
-                    InputStream is = connection.getInputStream();
-                    if (is != null) {
-                        is.close();
-                    }
-                } catch (IOException e) {
-                    getLogger().log(Level.INFO,
-                            "Error closing URLConnection input stream", e);
-                }
-            }
-        }
-
-        // Set type mime type if we can determine it based on the filename
-        final String mimetype = sc.getMimeType(filename);
-        if (mimetype != null) {
-            response.setContentType(mimetype);
-        }
-
-        // Provide modification timestamp to the browser if it is known.
-        if (lastModifiedTime > 0) {
-            response.setDateHeader("Last-Modified", lastModifiedTime);
-            /*
-             * The browser is allowed to cache for 1 hour without checking if
-             * the file has changed. This forces browsers to fetch a new version
-             * when the Vaadin version is updated. This will cause more requests
-             * to the servlet than without this but for high volume sites the
-             * static files should never be served through the servlet. The
-             * cache timeout can be configured by setting the resourceCacheTime
-             * parameter in web.xml
-             */
-            response.setHeader("Cache-Control",
-                    "max-age= " + String.valueOf(getResourceCacheTime()));
-        }
-
-        // Write the resource to the client.
-        final OutputStream os = response.getOutputStream();
-        final byte buffer[] = new byte[DEFAULT_BUFFER_SIZE];
-        int bytes;
-        InputStream is = resourceUrl.openStream();
-        while ((bytes = is.read(buffer)) >= 0) {
-            os.write(buffer, 0, bytes);
-        }
-        is.close();
-    }
-
-    /**
-     * Check whether a URL obtained from a classloader refers to a valid static
-     * resource in the directory VAADIN.
-     * 
-     * Warning: Overriding of this method is not recommended, but is possible to
-     * support non-default classloaders or servers that may produce URLs
-     * different from the normal ones. The method prototype may change in the
-     * future. Care should be taken not to expose class files or other resources
-     * outside the VAADIN directory if the method is overridden.
-     * 
-     * @param request
-     * @param resourceUrl
-     * @return
-     * 
-     * @since 6.6.7
-     */
-    protected boolean isAllowedVAADINResourceUrl(HttpServletRequest request,
-            URL resourceUrl) {
-        if ("jar".equals(resourceUrl.getProtocol())) {
-            // This branch is used for accessing resources directly from the
-            // Vaadin JAR in development environments and in similar cases.
-
-            // Inside a JAR, a ".." would mean a real directory named ".." so
-            // using it in paths should just result in the file not being found.
-            // However, performing a check in case some servers or class loaders
-            // try to normalize the path by collapsing ".." before the class
-            // loader sees it.
-
-            if (!resourceUrl.getPath().contains("!/VAADIN/")) {
-                getLogger().info(
-                        "Blocked attempt to access a JAR entry not starting with /VAADIN/: "
-                                + resourceUrl);
-                return false;
-            }
-            getLogger().fine(
-                    "Accepted access to a JAR entry using a class loader: "
-                            + resourceUrl);
-            return true;
-        } else {
-            // Some servers such as GlassFish extract files from JARs (file:)
-            // and e.g. JBoss 5+ use protocols vsf: and vfsfile: .
-
-            // Check that the URL is in a VAADIN directory and does not contain
-            // "/../"
-            if (!resourceUrl.getPath().contains("/VAADIN/")
-                    || resourceUrl.getPath().contains("/../")) {
-                getLogger().info(
-                        "Blocked attempt to access file: " + resourceUrl);
-                return false;
-            }
-            getLogger().fine(
-                    "Accepted access to a file using a class loader: "
-                            + resourceUrl);
-            return true;
-        }
-    }
-
-    /**
-     * Checks if the browser has an up to date cached version of requested
-     * resource. Currently the check is performed using the "If-Modified-Since"
-     * header. Could be expanded if needed.
-     * 
-     * @param request
-     *            The HttpServletRequest from the browser.
-     * @param resourceLastModifiedTimestamp
-     *            The timestamp when the resource was last modified. 0 if the
-     *            last modification time is unknown.
-     * @return true if the If-Modified-Since header tells the cached version in
-     *         the browser is up to date, false otherwise
-     */
-    private boolean browserHasNewestVersion(HttpServletRequest request,
-            long resourceLastModifiedTimestamp) {
-        if (resourceLastModifiedTimestamp < 1) {
-            // We do not know when it was modified so the browser cannot have an
-            // up-to-date version
-            return false;
-        }
-        /*
-         * The browser can request the resource conditionally using an
-         * If-Modified-Since header. Check this against the last modification
-         * time.
-         */
-        try {
-            // If-Modified-Since represents the timestamp of the version cached
-            // in the browser
-            long headerIfModifiedSince = request
-                    .getDateHeader("If-Modified-Since");
-
-            if (headerIfModifiedSince >= resourceLastModifiedTimestamp) {
-                // Browser has this an up-to-date version of the resource
-                return true;
-            }
-        } catch (Exception e) {
-            // Failed to parse header. Fail silently - the browser does not have
-            // an up-to-date version in its cache.
-        }
-        return false;
-    }
-
-    protected enum RequestType {
-        FILE_UPLOAD, BROWSER_DETAILS, UIDL, OTHER, STATIC_FILE, APPLICATION_RESOURCE, CONNECTOR_RESOURCE, HEARTBEAT;
-    }
-
-    protected RequestType getRequestType(WrappedHttpServletRequest request) {
-        if (ServletPortletHelper.isFileUploadRequest(request)) {
-            return RequestType.FILE_UPLOAD;
-        } else if (ServletPortletHelper.isConnectorResourceRequest(request)) {
-            return RequestType.CONNECTOR_RESOURCE;
-        } else if (isBrowserDetailsRequest(request)) {
-            return RequestType.BROWSER_DETAILS;
-        } else if (ServletPortletHelper.isUIDLRequest(request)) {
-            return RequestType.UIDL;
-        } else if (isStaticResourceRequest(request)) {
-            return RequestType.STATIC_FILE;
-        } else if (ServletPortletHelper.isApplicationResourceRequest(request)) {
-            return RequestType.APPLICATION_RESOURCE;
-        } else if (ServletPortletHelper.isHeartbeatRequest(request)) {
-            return RequestType.HEARTBEAT;
-        }
-        return RequestType.OTHER;
-
-    }
-
-    private static boolean isBrowserDetailsRequest(HttpServletRequest request) {
-        return "POST".equals(request.getMethod())
-                && request.getParameter("browserDetails") != null;
-    }
-
-    private boolean isStaticResourceRequest(HttpServletRequest request) {
-        String pathInfo = request.getPathInfo();
-        if (pathInfo == null || pathInfo.length() <= 10) {
-            return false;
-        }
-
-        if ((request.getContextPath() != null)
-                && (request.getRequestURI().startsWith("/VAADIN/"))) {
-            return true;
-        } else if (request.getRequestURI().startsWith(
-                request.getContextPath() + "/VAADIN/")) {
-            return true;
-        }
-
-        return false;
-    }
-
-    private boolean isOnUnloadRequest(HttpServletRequest request) {
-        return request.getParameter(ApplicationConstants.PARAM_UNLOADBURST) != null;
-    }
-
-    /**
-     * Get system messages
-     * 
-     * @return
-     */
-    protected SystemMessages getSystemMessages() {
-        return ServletPortletHelper.DEFAULT_SYSTEM_MESSAGES;
-    }
-
-    /**
-     * Return the URL from where static files, e.g. the widgetset and the theme,
-     * are served. In a standard configuration the VAADIN folder inside the
-     * returned folder is what is used for widgetsets and themes.
-     * 
-     * The returned folder is usually the same as the context path and
-     * independent of the application.
-     * 
-     * @param request
-     * @return The location of static resources (should contain the VAADIN
-     *         directory). Never ends with a slash (/).
-     */
-    protected String getStaticFilesLocation(HttpServletRequest request) {
-
-        return getWebApplicationsStaticFileLocation(request);
-    }
-
-    /**
-     * The default method to fetch static files location (URL). This method does
-     * not check for request attribute {@value #REQUEST_VAADIN_STATIC_FILE_PATH}
-     * 
-     * @param request
-     * @return
-     */
-    private String getWebApplicationsStaticFileLocation(
-            HttpServletRequest request) {
-        String staticFileLocation;
-        // if property is defined in configurations, use that
-        staticFileLocation = getDeploymentConfiguration()
-                .getApplicationOrSystemProperty(PARAMETER_VAADIN_RESOURCES,
-                        null);
-        if (staticFileLocation != null) {
-            return staticFileLocation;
-        }
-
-        // the last (but most common) option is to generate default location
-        // from request
-
-        // if context is specified add it to widgetsetUrl
-        String ctxPath = request.getContextPath();
-
-        // FIXME: ctxPath.length() == 0 condition is probably unnecessary and
-        // might even be wrong.
-
-        if (ctxPath.length() == 0
-                && request.getAttribute("javax.servlet.include.context_path") != null) {
-            // include request (e.g portlet), get context path from
-            // attribute
-            ctxPath = (String) request
-                    .getAttribute("javax.servlet.include.context_path");
-        }
-
-        // Remove heading and trailing slashes from the context path
-        ctxPath = removeHeadingOrTrailing(ctxPath, "/");
-
-        if (ctxPath.equals("")) {
-            return "";
-        } else {
-            return "/" + ctxPath;
-        }
-    }
-
-    /**
-     * Remove any heading or trailing "what" from the "string".
-     * 
-     * @param string
-     * @param what
-     * @return
-     */
-    private static String removeHeadingOrTrailing(String string, String what) {
-        while (string.startsWith(what)) {
-            string = string.substring(1);
-        }
-
-        while (string.endsWith(what)) {
-            string = string.substring(0, string.length() - 1);
-        }
-
-        return string;
-    }
-
-    /**
-     * Write a redirect response to the main page of the application.
-     * 
-     * @param request
-     * @param response
-     * @throws IOException
-     *             if sending the redirect fails due to an input/output error or
-     *             a bad application URL
-     */
-    private void redirectToApplication(HttpServletRequest request,
-            HttpServletResponse response) throws IOException {
-        String applicationUrl = getApplicationUrl(request).toExternalForm();
-        response.sendRedirect(response.encodeRedirectURL(applicationUrl));
-    }
-
-    /**
-     * Gets the current application URL from request.
-     * 
-     * @param request
-     *            the HTTP request.
-     * @throws MalformedURLException
-     *             if the application is denied access to the persistent data
-     *             store represented by the given URL.
-     */
-    protected URL getApplicationUrl(HttpServletRequest request)
-            throws MalformedURLException {
-        final URL reqURL = new URL(
-                (request.isSecure() ? "https://" : "http://")
-                        + request.getServerName()
-                        + ((request.isSecure() && request.getServerPort() == 443)
-                                || (!request.isSecure() && request
-                                        .getServerPort() == 80) ? "" : ":"
-                                + request.getServerPort())
-                        + request.getRequestURI());
-        String servletPath = "";
-        if (request.getAttribute("javax.servlet.include.servlet_path") != null) {
-            // this is an include request
-            servletPath = request.getAttribute(
-                    "javax.servlet.include.context_path").toString()
-                    + request
-                            .getAttribute("javax.servlet.include.servlet_path");
-
-        } else {
-            servletPath = request.getContextPath() + request.getServletPath();
-        }
-
-        if (servletPath.length() == 0
-                || servletPath.charAt(servletPath.length() - 1) != '/') {
-            servletPath = servletPath + "/";
-        }
-        URL u = new URL(reqURL, servletPath);
-        return u;
-    }
-
-    /**
-     * Gets the existing application for given request. Looks for application
-     * instance for given request based on the requested URL.
-     * 
-     * @param request
-     *            the HTTP request.
-     * @param allowSessionCreation
-     *            true if a session should be created if no session exists,
-     *            false if no session should be created
-     * @return Application instance, or null if the URL does not map to valid
-     *         application.
-     * @throws MalformedURLException
-     *             if the application is denied access to the persistent data
-     *             store represented by the given URL.
-     * @throws IllegalAccessException
-     * @throws InstantiationException
-     * @throws SessionExpiredException
-     */
-    protected Application getExistingApplication(HttpServletRequest request,
-            boolean allowSessionCreation) throws MalformedURLException,
-            SessionExpiredException {
-
-        // Ensures that the session is still valid
-        final HttpSession session = request.getSession(allowSessionCreation);
-        if (session == null) {
-            throw new SessionExpiredException();
-        }
-
-        ServletApplicationContext context = getApplicationContext(session);
-
-        // Gets application list for the session.
-        final Collection<Application> applications = context.getApplications();
-
-        // Search for the application (using the application URI) from the list
-        for (final Iterator<Application> i = applications.iterator(); i
-                .hasNext();) {
-            final Application sessionApplication = i.next();
-            final String sessionApplicationPath = sessionApplication.getURL()
-                    .getPath();
-            String requestApplicationPath = getApplicationUrl(request)
-                    .getPath();
-
-            if (requestApplicationPath.equals(sessionApplicationPath)) {
-                // Found a running application
-                if (sessionApplication.isRunning()) {
-                    return sessionApplication;
-                }
-                // Application has stopped, so remove it before creating a new
-                // application
-                getApplicationContext(session).removeApplication(
-                        sessionApplication);
-                break;
-            }
-        }
-
-        // Existing application not found
-        return null;
-    }
-
-    /**
-     * Ends the application.
-     * 
-     * @param request
-     *            the HTTP request.
-     * @param response
-     *            the HTTP response to write to.
-     * @param application
-     *            the application to end.
-     * @throws IOException
-     *             if the writing failed due to input/output error.
-     */
-    private void endApplication(HttpServletRequest request,
-            HttpServletResponse response, Application application)
-            throws IOException {
-
-        String logoutUrl = application.getLogoutURL();
-        if (logoutUrl == null) {
-            logoutUrl = application.getURL().toString();
-        }
-
-        final HttpSession session = request.getSession();
-        if (session != null) {
-            getApplicationContext(session).removeApplication(application);
-        }
-
-        response.sendRedirect(response.encodeRedirectURL(logoutUrl));
-    }
-
-    /**
-     * Returns the path info; note that this _can_ be different than
-     * request.getPathInfo(). Examples where this might be useful:
-     * <ul>
-     * <li>An application runner servlet that runs different Vaadin applications
-     * based on an identifier.</li>
-     * <li>Providing a REST interface in the context root, while serving a
-     * Vaadin UI on a sub-URI using only one servlet (e.g. REST on
-     * http://example.com/foo, UI on http://example.com/foo/vaadin)</li>
-     * 
-     * @param request
-     * @return
-     */
-    protected String getRequestPathInfo(HttpServletRequest request) {
-        return request.getPathInfo();
-    }
-
-    /**
-     * Gets relative location of a theme resource.
-     * 
-     * @param theme
-     *            the Theme name.
-     * @param resource
-     *            the Theme resource.
-     * @return External URI specifying the resource
-     */
-    public String getResourceLocation(String theme, ThemeResource resource) {
-
-        if (resourcePath == null) {
-            return resource.getResourceId();
-        }
-        return resourcePath + theme + "/" + resource.getResourceId();
-    }
-
-    private boolean isRepaintAll(HttpServletRequest request) {
-        return (request.getParameter(URL_PARAMETER_REPAINT_ALL) != null)
-                && (request.getParameter(URL_PARAMETER_REPAINT_ALL).equals("1"));
-    }
-
-    private void closeApplication(Application application, HttpSession session) {
-        if (application == null) {
-            return;
-        }
-
-        application.close();
-        if (session != null) {
-            ServletApplicationContext context = getApplicationContext(session);
-            context.removeApplication(application);
-        }
-    }
-
-    /**
-     * 
-     * Gets the application context from an HttpSession. If no context is
-     * currently stored in a session a new context is created and stored in the
-     * session.
-     * 
-     * @param session
-     *            the HTTP session.
-     * @return the application context for HttpSession.
-     */
-    protected ServletApplicationContext getApplicationContext(HttpSession session) {
-        /*
-         * TODO the ApplicationContext.getApplicationContext() should be removed
-         * and logic moved here. Now overriding context type is possible, but
-         * the whole creation logic should be here. MT 1101
-         */
-        return ServletApplicationContext.getApplicationContext(session);
-    }
-
-    public class RequestError implements Terminal.ErrorEvent, Serializable {
-
-        private final Throwable throwable;
-
-        public RequestError(Throwable throwable) {
-            this.throwable = throwable;
-        }
-
-        @Override
-        public Throwable getThrowable() {
-            return throwable;
-        }
-
-    }
-
-    /**
-     * Override this method if you need to use a specialized communicaiton
-     * mananger implementation.
-     * 
-     * @deprecated Instead of overriding this method, override
-     *             {@link ServletApplicationContext} implementation via
-     *             {@link AbstractApplicationServlet#getApplicationContext(HttpSession)}
-     *             method and in that customized implementation return your
-     *             CommunicationManager in
-     *             {@link ServletApplicationContext#getApplicationManager(Application, AbstractApplicationServlet)}
-     *             method.
-     * 
-     * @param application
-     * @return
-     */
-    @Deprecated
-    public CommunicationManager createCommunicationManager(
-            Application application) {
-        return new CommunicationManager(application);
-    }
-
-    /**
-     * Escapes characters to html entities. An exception is made for some
-     * "safe characters" to keep the text somewhat readable.
-     * 
-     * @param unsafe
-     * @return a safe string to be added inside an html tag
-     */
-    public static final String safeEscapeForHtml(String unsafe) {
-        if (null == unsafe) {
-            return null;
-        }
-        StringBuilder safe = new StringBuilder();
-        char[] charArray = unsafe.toCharArray();
-        for (int i = 0; i < charArray.length; i++) {
-            char c = charArray[i];
-            if (isSafe(c)) {
-                safe.append(c);
-            } else {
-                safe.append("&#");
-                safe.append((int) c);
-                safe.append(";");
-            }
-        }
-
-        return safe.toString();
-    }
-
-    private static boolean isSafe(char c) {
-        return //
-        c > 47 && c < 58 || // alphanum
-                c > 64 && c < 91 || // A-Z
-                c > 96 && c < 123 // a-z
-        ;
-    }
-
-    private static final Logger getLogger() {
-        return Logger.getLogger(AbstractApplicationServlet.class.getName());
-    }
-}
index 740ecf843b2877ee281c2663646f3f31254a8de3..2e42f51249021d09853f5aea364e2c5069b31dcd 100644 (file)
@@ -1484,7 +1484,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
             themeName = requestThemeName;
         }
         if (themeName == null) {
-            themeName = AbstractApplicationServlet.getDefaultTheme();
+            themeName = VaadinServlet.getDefaultTheme();
         }
         return themeName;
     }
@@ -1502,7 +1502,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
     public boolean isXSRFEnabled(Application application) {
         return !"true"
                 .equals(application
-                        .getProperty(AbstractApplicationServlet.SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION));
+                        .getProperty(VaadinServlet.SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION));
     }
 
     /**
index 0928ea4bd4bec6c008bc4287e995d4fbbe737ff6..4e30dff06f0c78f8aee7f6dcf7923e611e26623b 100644 (file)
@@ -109,12 +109,11 @@ public abstract class AbstractErrorMessage implements ErrorMessage {
         String result = null;
         switch (getMode()) {
         case TEXT:
-            result = AbstractApplicationServlet.safeEscapeForHtml(getMessage());
+            result = VaadinServlet.safeEscapeForHtml(getMessage());
             break;
         case PREFORMATTED:
-            result = "<pre>"
-                    + AbstractApplicationServlet
-                            .safeEscapeForHtml(getMessage()) + "</pre>";
+            result = "<pre>" + VaadinServlet.safeEscapeForHtml(getMessage())
+                    + "</pre>";
             break;
         case XHTML:
             result = getMessage();
diff --git a/server/src/com/vaadin/server/ApplicationServlet.java b/server/src/com/vaadin/server/ApplicationServlet.java
deleted file mode 100644 (file)
index 905b48d..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* 
- * Copyright 2011 Vaadin Ltd.
- * 
- * 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 com.vaadin.server;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-
-import com.vaadin.Application;
-import com.vaadin.server.ServletPortletHelper.ApplicationClassException;
-
-/**
- * This servlet connects a Vaadin Application to Web.
- * 
- * @author Vaadin Ltd.
- * @since 5.0
- */
-
-@SuppressWarnings("serial")
-public class ApplicationServlet extends AbstractApplicationServlet {
-
-    // Private fields
-    private Class<? extends Application> applicationClass;
-
-    /**
-     * Called by the servlet container to indicate to a servlet that the servlet
-     * is being placed into service.
-     * 
-     * @param servletConfig
-     *            the object containing the servlet's configuration and
-     *            initialization parameters
-     * @throws javax.servlet.ServletException
-     *             if an exception has occurred that interferes with the
-     *             servlet's normal operation.
-     */
-    @Override
-    public void init(javax.servlet.ServletConfig servletConfig)
-            throws javax.servlet.ServletException {
-        super.init(servletConfig);
-
-        // Loads the application class using the classloader defined in the
-        // deployment configuration
-
-        try {
-            applicationClass = ServletPortletHelper
-                    .getApplicationClass(getDeploymentConfiguration());
-        } catch (ApplicationClassException e) {
-            throw new ServletException(e);
-        }
-    }
-
-    @Override
-    protected Application getNewApplication(HttpServletRequest request)
-            throws ServletException {
-
-        // Creates a new application instance
-        try {
-            final Application application = getApplicationClass().newInstance();
-            application.addUIProvider(new DefaultUIProvider());
-
-            return application;
-        } catch (final IllegalAccessException e) {
-            throw new ServletException("getNewApplication failed", e);
-        } catch (final InstantiationException e) {
-            throw new ServletException("getNewApplication failed", e);
-        }
-    }
-
-    protected Class<? extends Application> getApplicationClass() {
-        return applicationClass;
-    }
-}
index 98ed1071dedac14df10fded696f63acc97dd08da..a1438312b6cde261ce6bc9ca4c02747758dfdd0d 100644 (file)
@@ -277,7 +277,7 @@ public abstract class BootstrapHandler implements RequestHandler {
                     .getConfiguredWidgetset(request);
         }
 
-        widgetset = AbstractApplicationServlet.stripSpecialChars(widgetset);
+        widgetset = VaadinServlet.stripSpecialChars(widgetset);
         return widgetset;
     }
 
@@ -450,7 +450,7 @@ public abstract class BootstrapHandler implements RequestHandler {
         String staticFileLocation = deploymentConfiguration
                 .getStaticFileLocation(request);
         String widgetsetBase = staticFileLocation + "/"
-                + AbstractApplicationServlet.WIDGETSET_DIRECTORY_PATH;
+                + VaadinServlet.WIDGETSET_DIRECTORY_PATH;
         defaults.put("widgetsetBase", widgetsetBase);
 
         if (!application.isProductionMode()) {
@@ -486,8 +486,8 @@ public abstract class BootstrapHandler implements RequestHandler {
         WrappedRequest request = context.getRequest();
         final String staticFilePath = request.getDeploymentConfiguration()
                 .getStaticFileLocation(request);
-        return staticFilePath + "/"
-                + AbstractApplicationServlet.THEME_DIRECTORY_PATH + themeName;
+        return staticFilePath + "/" + VaadinServlet.THEME_DIRECTORY_PATH
+                + themeName;
     }
 
     /**
@@ -517,7 +517,7 @@ public abstract class BootstrapHandler implements RequestHandler {
 
         // XSS preventation, theme names shouldn't contain special chars anyway.
         // The servlet denies them via url parameter.
-        themeName = AbstractApplicationServlet.stripSpecialChars(themeName);
+        themeName = VaadinServlet.stripSpecialChars(themeName);
 
         return themeName;
     }
index af28438f57a09921a3cd155da54d83a44c15e5c4..cc920239193ef1d9833b402afaf6492744e6fe00 100644 (file)
@@ -45,7 +45,7 @@ public class CommunicationManager extends AbstractCommunicationManager {
      */
     @Deprecated
     public CommunicationManager(Application application,
-            AbstractApplicationServlet applicationServlet) {
+            VaadinServlet applicationServlet) {
         super(application);
     }
 
@@ -100,7 +100,7 @@ public class CommunicationManager extends AbstractCommunicationManager {
             @Override
             public String getThemeName(BootstrapContext context) {
                 String themeName = context.getRequest().getParameter(
-                        AbstractApplicationServlet.URL_PARAMETER_THEME);
+                        VaadinServlet.URL_PARAMETER_THEME);
                 if (themeName == null) {
                     themeName = super.getThemeName(context);
                 }
@@ -117,7 +117,7 @@ public class CommunicationManager extends AbstractCommunicationManager {
         ServletContext servletContext = context.getHttpSession()
                 .getServletContext();
         return servletContext.getResourceAsStream("/"
-                + AbstractApplicationServlet.THEME_DIRECTORY_PATH + themeName
-                + "/" + resource);
+                + VaadinServlet.THEME_DIRECTORY_PATH + themeName + "/"
+                + resource);
     }
 }
diff --git a/server/src/com/vaadin/server/GAEApplicationServlet.java b/server/src/com/vaadin/server/GAEApplicationServlet.java
deleted file mode 100644 (file)
index 240984c..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright 2011 Vaadin Ltd.
- * 
- * 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 com.vaadin.server;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.NotSerializableException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import com.google.appengine.api.datastore.Blob;
-import com.google.appengine.api.datastore.DatastoreService;
-import com.google.appengine.api.datastore.DatastoreServiceFactory;
-import com.google.appengine.api.datastore.Entity;
-import com.google.appengine.api.datastore.EntityNotFoundException;
-import com.google.appengine.api.datastore.FetchOptions.Builder;
-import com.google.appengine.api.datastore.Key;
-import com.google.appengine.api.datastore.KeyFactory;
-import com.google.appengine.api.datastore.PreparedQuery;
-import com.google.appengine.api.datastore.Query;
-import com.google.appengine.api.datastore.Query.FilterOperator;
-import com.google.appengine.api.memcache.Expiration;
-import com.google.appengine.api.memcache.MemcacheService;
-import com.google.appengine.api.memcache.MemcacheServiceFactory;
-import com.google.apphosting.api.DeadlineExceededException;
-
-/**
- * ApplicationServlet to be used when deploying to Google App Engine, in
- * web.xml:
- * 
- * <pre>
- *      &lt;servlet&gt;
- *              &lt;servlet-name&gt;HelloWorld&lt;/servlet-name&gt;
- *              &lt;servlet-class&gt;com.vaadin.server.GAEApplicationServlet&lt;/servlet-class&gt;
- *              &lt;init-param&gt;
- *                      &lt;param-name&gt;application&lt;/param-name&gt;
- *                      &lt;param-value&gt;com.vaadin.demo.HelloWorld&lt;/param-value&gt;
- *              &lt;/init-param&gt;
- *      &lt;/servlet&gt;
- * </pre>
- * 
- * Session support must be enabled in appengine-web.xml:
- * 
- * <pre>
- *      &lt;sessions-enabled&gt;true&lt;/sessions-enabled&gt;
- * </pre>
- * 
- * Appengine datastore cleanup can be invoked by calling one of the applications
- * with an additional path "/CLEAN". This can be set up as a cron-job in
- * cron.xml (see appengine documentation for more information):
- * 
- * <pre>
- * &lt;cronentries&gt;
- *   &lt;cron&gt;
- *     &lt;url&gt;/HelloWorld/CLEAN&lt;/url&gt;
- *     &lt;description&gt;Clean up sessions&lt;/description&gt;
- *     &lt;schedule&gt;every 2 hours&lt;/schedule&gt;
- *   &lt;/cron&gt;
- * &lt;/cronentries&gt;
- * </pre>
- * 
- * It is recommended (but not mandatory) to extract themes and widgetsets and
- * have App Engine server these statically. Extract VAADIN folder (and it's
- * contents) 'next to' the WEB-INF folder, and add the following to
- * appengine-web.xml:
- * 
- * <pre>
- *      &lt;static-files&gt;
- *           &lt;include path=&quot;/VAADIN/**&quot; /&gt;
- *      &lt;/static-files&gt;
- * </pre>
- * 
- * Additional limitations:
- * <ul>
- * <li/>Do not change application state when serving an ApplicationResource.
- * <li/>Avoid changing application state in transaction handlers, unless you're
- * confident you fully understand the synchronization issues in App Engine.
- * <li/>The application remains locked while uploading - no progressbar is
- * possible.
- * </ul>
- */
-public class GAEApplicationServlet extends ApplicationServlet {
-
-    // memcache mutex is MUTEX_BASE + sessio id
-    private static final String MUTEX_BASE = "_vmutex";
-
-    // used identify ApplicationContext in memcache and datastore
-    private static final String AC_BASE = "_vac";
-
-    // UIDL requests will attempt to gain access for this long before telling
-    // the client to retry
-    private static final int MAX_UIDL_WAIT_MILLISECONDS = 5000;
-
-    // Tell client to retry after this delay.
-    // Note: currently interpreting Retry-After as ms, not sec
-    private static final int RETRY_AFTER_MILLISECONDS = 100;
-
-    // Properties used in the datastore
-    private static final String PROPERTY_EXPIRES = "expires";
-    private static final String PROPERTY_DATA = "data";
-
-    // path used for cleanup
-    private static final String CLEANUP_PATH = "/CLEAN";
-    // max entities to clean at once
-    private static final int CLEANUP_LIMIT = 200;
-    // appengine session kind
-    private static final String APPENGINE_SESSION_KIND = "_ah_SESSION";
-    // appengine session expires-parameter
-    private static final String PROPERTY_APPENGINE_EXPIRES = "_expires";
-
-    protected void sendDeadlineExceededNotification(
-            WrappedHttpServletRequest request,
-            WrappedHttpServletResponse response) throws IOException {
-        criticalNotification(
-                request,
-                response,
-                "Deadline Exceeded",
-                "I'm sorry, but the operation took too long to complete. We'll try reloading to see where we're at, please take note of any unsaved data...",
-                "", null);
-    }
-
-    protected void sendNotSerializableNotification(
-            WrappedHttpServletRequest request,
-            WrappedHttpServletResponse response) throws IOException {
-        criticalNotification(
-                request,
-                response,
-                "NotSerializableException",
-                "I'm sorry, but there seems to be a serious problem, please contact the administrator. And please take note of any unsaved data...",
-                "", getApplicationUrl(request).toString()
-                        + "?restartApplication");
-    }
-
-    protected void sendCriticalErrorNotification(
-            WrappedHttpServletRequest request,
-            WrappedHttpServletResponse response) throws IOException {
-        criticalNotification(
-                request,
-                response,
-                "Critical error",
-                "I'm sorry, but there seems to be a serious problem, please contact the administrator. And please take note of any unsaved data...",
-                "", getApplicationUrl(request).toString()
-                        + "?restartApplication");
-    }
-
-    @Override
-    protected void service(HttpServletRequest unwrappedRequest,
-            HttpServletResponse unwrappedResponse) throws ServletException,
-            IOException {
-        WrappedHttpServletRequest request = new WrappedHttpServletRequest(
-                unwrappedRequest, getDeploymentConfiguration());
-        WrappedHttpServletResponse response = new WrappedHttpServletResponse(
-                unwrappedResponse, getDeploymentConfiguration());
-
-        if (isCleanupRequest(request)) {
-            cleanDatastore();
-            return;
-        }
-
-        RequestType requestType = getRequestType(request);
-
-        if (requestType == RequestType.STATIC_FILE) {
-            // no locking needed, let superclass handle
-            super.service(request, response);
-            cleanSession(request);
-            return;
-        }
-
-        if (requestType == RequestType.APPLICATION_RESOURCE) {
-            // no locking needed, let superclass handle
-            getApplicationContext(request,
-                    MemcacheServiceFactory.getMemcacheService());
-            super.service(request, response);
-            cleanSession(request);
-            return;
-        }
-
-        final HttpSession session = request
-                .getSession(requestCanCreateApplication(request, requestType));
-        if (session == null) {
-            handleServiceSessionExpired(request, response);
-            cleanSession(request);
-            return;
-        }
-
-        boolean locked = false;
-        MemcacheService memcache = null;
-        String mutex = MUTEX_BASE + session.getId();
-        memcache = MemcacheServiceFactory.getMemcacheService();
-        try {
-            // try to get lock
-            long started = new Date().getTime();
-            // non-UIDL requests will try indefinitely
-            while (requestType != RequestType.UIDL
-                    || new Date().getTime() - started < MAX_UIDL_WAIT_MILLISECONDS) {
-                locked = memcache.put(mutex, 1, Expiration.byDeltaSeconds(40),
-                        MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT);
-                if (locked) {
-                    break;
-                }
-                try {
-                    Thread.sleep(RETRY_AFTER_MILLISECONDS);
-                } catch (InterruptedException e) {
-                    getLogger().finer(
-                            "Thread.sleep() interrupted while waiting for lock. Trying again. "
-                                    + e);
-                }
-            }
-
-            if (!locked) {
-                // Not locked; only UIDL can get trough here unlocked: tell
-                // client to retry
-                response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
-                // Note: currently interpreting Retry-After as ms, not sec
-                response.setHeader("Retry-After", "" + RETRY_AFTER_MILLISECONDS);
-                return;
-            }
-
-            // de-serialize or create application context, store in session
-            ApplicationContext ctx = getApplicationContext(request, memcache);
-
-            super.service(request, response);
-
-            // serialize
-            started = new Date().getTime();
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(baos);
-            oos.writeObject(ctx);
-            oos.flush();
-            byte[] bytes = baos.toByteArray();
-
-            started = new Date().getTime();
-
-            String id = AC_BASE + session.getId();
-            Date expire = new Date(started
-                    + (session.getMaxInactiveInterval() * 1000));
-            Expiration expires = Expiration.onDate(expire);
-
-            memcache.put(id, bytes, expires);
-
-            DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
-            Entity entity = new Entity(AC_BASE, id);
-            entity.setProperty(PROPERTY_EXPIRES, expire.getTime());
-            entity.setProperty(PROPERTY_DATA, new Blob(bytes));
-            ds.put(entity);
-
-        } catch (DeadlineExceededException e) {
-            getLogger().warning("DeadlineExceeded for " + session.getId());
-            sendDeadlineExceededNotification(request, response);
-        } catch (NotSerializableException e) {
-            getLogger().log(Level.SEVERE, "Not serializable!", e);
-
-            // TODO this notification is usually not shown - should we redirect
-            // in some other way - can we?
-            sendNotSerializableNotification(request, response);
-        } catch (Exception e) {
-            getLogger().log(Level.WARNING,
-                    "An exception occurred while servicing request.", e);
-
-            sendCriticalErrorNotification(request, response);
-        } finally {
-            // "Next, please!"
-            if (locked) {
-                memcache.delete(mutex);
-            }
-            cleanSession(request);
-        }
-    }
-
-    protected ApplicationContext getApplicationContext(
-            HttpServletRequest request, MemcacheService memcache) {
-        HttpSession session = request.getSession();
-        String id = AC_BASE + session.getId();
-        byte[] serializedAC = (byte[]) memcache.get(id);
-        if (serializedAC == null) {
-            DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
-            Key key = KeyFactory.createKey(AC_BASE, id);
-            Entity entity = null;
-            try {
-                entity = ds.get(key);
-            } catch (EntityNotFoundException e) {
-                // Ok, we were a bit optimistic; we'll create a new one later
-            }
-            if (entity != null) {
-                Blob blob = (Blob) entity.getProperty(PROPERTY_DATA);
-                serializedAC = blob.getBytes();
-                // bring it to memcache
-                memcache.put(AC_BASE + session.getId(), serializedAC,
-                        Expiration.byDeltaSeconds(session
-                                .getMaxInactiveInterval()),
-                        MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT);
-            }
-        }
-        if (serializedAC != null) {
-            ByteArrayInputStream bais = new ByteArrayInputStream(serializedAC);
-            ObjectInputStream ois;
-            try {
-                ois = new ObjectInputStream(bais);
-                ApplicationContext applicationContext = (ApplicationContext) ois
-                        .readObject();
-                session.setAttribute(ServletApplicationContext.class.getName(),
-                        applicationContext);
-            } catch (IOException e) {
-                getLogger().log(
-                        Level.WARNING,
-                        "Could not de-serialize ApplicationContext for "
-                                + session.getId()
-                                + " A new one will be created. ", e);
-            } catch (ClassNotFoundException e) {
-                getLogger().log(
-                        Level.WARNING,
-                        "Could not de-serialize ApplicationContext for "
-                                + session.getId()
-                                + " A new one will be created. ", e);
-            }
-        }
-        // will create new context if the above did not
-        return getApplicationContext(session);
-
-    }
-
-    private boolean isCleanupRequest(HttpServletRequest request) {
-        String path = getRequestPathInfo(request);
-        if (path != null && path.equals(CLEANUP_PATH)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Removes the ApplicationContext from the session in order to minimize the
-     * data serialized to datastore and memcache.
-     * 
-     * @param request
-     */
-    private void cleanSession(HttpServletRequest request) {
-        HttpSession session = request.getSession(false);
-        if (session != null) {
-            session.removeAttribute(ServletApplicationContext.class.getName());
-        }
-    }
-
-    /**
-     * This will look at the timestamp and delete expired persisted Vaadin and
-     * appengine sessions from the datastore.
-     * 
-     * TODO Possible improvements include: 1. Use transactions (requires entity
-     * groups - overkill?) 2. Delete one-at-a-time, catch possible exception,
-     * continue w/ next.
-     */
-    private void cleanDatastore() {
-        long expire = new Date().getTime();
-        try {
-            DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
-            // Vaadin stuff first
-            {
-                Query q = new Query(AC_BASE);
-                q.setKeysOnly();
-
-                q.addFilter(PROPERTY_EXPIRES,
-                        FilterOperator.LESS_THAN_OR_EQUAL, expire);
-                PreparedQuery pq = ds.prepare(q);
-                List<Entity> entities = pq.asList(Builder
-                        .withLimit(CLEANUP_LIMIT));
-                if (entities != null) {
-                    getLogger().info(
-                            "Vaadin cleanup deleting " + entities.size()
-                                    + " expired Vaadin sessions.");
-                    List<Key> keys = new ArrayList<Key>();
-                    for (Entity e : entities) {
-                        keys.add(e.getKey());
-                    }
-                    ds.delete(keys);
-                }
-            }
-            // Also cleanup GAE sessions
-            {
-                Query q = new Query(APPENGINE_SESSION_KIND);
-                q.setKeysOnly();
-                q.addFilter(PROPERTY_APPENGINE_EXPIRES,
-                        FilterOperator.LESS_THAN_OR_EQUAL, expire);
-                PreparedQuery pq = ds.prepare(q);
-                List<Entity> entities = pq.asList(Builder
-                        .withLimit(CLEANUP_LIMIT));
-                if (entities != null) {
-                    getLogger().info(
-                            "Vaadin cleanup deleting " + entities.size()
-                                    + " expired appengine sessions.");
-                    List<Key> keys = new ArrayList<Key>();
-                    for (Entity e : entities) {
-                        keys.add(e.getKey());
-                    }
-                    ds.delete(keys);
-                }
-            }
-        } catch (Exception e) {
-            getLogger().log(Level.WARNING, "Exception while cleaning.", e);
-        }
-    }
-
-    private static final Logger getLogger() {
-        return Logger.getLogger(GAEApplicationServlet.class.getName());
-    }
-}
diff --git a/server/src/com/vaadin/server/GAEVaadinServlet.java b/server/src/com/vaadin/server/GAEVaadinServlet.java
new file mode 100644 (file)
index 0000000..642737f
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * 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 com.vaadin.server;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import com.google.appengine.api.datastore.Blob;
+import com.google.appengine.api.datastore.DatastoreService;
+import com.google.appengine.api.datastore.DatastoreServiceFactory;
+import com.google.appengine.api.datastore.Entity;
+import com.google.appengine.api.datastore.EntityNotFoundException;
+import com.google.appengine.api.datastore.FetchOptions.Builder;
+import com.google.appengine.api.datastore.Key;
+import com.google.appengine.api.datastore.KeyFactory;
+import com.google.appengine.api.datastore.PreparedQuery;
+import com.google.appengine.api.datastore.Query;
+import com.google.appengine.api.datastore.Query.FilterOperator;
+import com.google.appengine.api.memcache.Expiration;
+import com.google.appengine.api.memcache.MemcacheService;
+import com.google.appengine.api.memcache.MemcacheServiceFactory;
+import com.google.apphosting.api.DeadlineExceededException;
+
+/**
+ * ApplicationServlet to be used when deploying to Google App Engine, in
+ * web.xml:
+ * 
+ * <pre>
+ *      &lt;servlet&gt;
+ *              &lt;servlet-name&gt;HelloWorld&lt;/servlet-name&gt;
+ *              &lt;servlet-class&gt;com.vaadin.server.GAEApplicationServlet&lt;/servlet-class&gt;
+ *              &lt;init-param&gt;
+ *                      &lt;param-name&gt;application&lt;/param-name&gt;
+ *                      &lt;param-value&gt;com.vaadin.demo.HelloWorld&lt;/param-value&gt;
+ *              &lt;/init-param&gt;
+ *      &lt;/servlet&gt;
+ * </pre>
+ * 
+ * Session support must be enabled in appengine-web.xml:
+ * 
+ * <pre>
+ *      &lt;sessions-enabled&gt;true&lt;/sessions-enabled&gt;
+ * </pre>
+ * 
+ * Appengine datastore cleanup can be invoked by calling one of the applications
+ * with an additional path "/CLEAN". This can be set up as a cron-job in
+ * cron.xml (see appengine documentation for more information):
+ * 
+ * <pre>
+ * &lt;cronentries&gt;
+ *   &lt;cron&gt;
+ *     &lt;url&gt;/HelloWorld/CLEAN&lt;/url&gt;
+ *     &lt;description&gt;Clean up sessions&lt;/description&gt;
+ *     &lt;schedule&gt;every 2 hours&lt;/schedule&gt;
+ *   &lt;/cron&gt;
+ * &lt;/cronentries&gt;
+ * </pre>
+ * 
+ * It is recommended (but not mandatory) to extract themes and widgetsets and
+ * have App Engine server these statically. Extract VAADIN folder (and it's
+ * contents) 'next to' the WEB-INF folder, and add the following to
+ * appengine-web.xml:
+ * 
+ * <pre>
+ *      &lt;static-files&gt;
+ *           &lt;include path=&quot;/VAADIN/**&quot; /&gt;
+ *      &lt;/static-files&gt;
+ * </pre>
+ * 
+ * Additional limitations:
+ * <ul>
+ * <li/>Do not change application state when serving an ApplicationResource.
+ * <li/>Avoid changing application state in transaction handlers, unless you're
+ * confident you fully understand the synchronization issues in App Engine.
+ * <li/>The application remains locked while uploading - no progressbar is
+ * possible.
+ * </ul>
+ */
+public class GAEVaadinServlet extends VaadinServlet {
+
+    // memcache mutex is MUTEX_BASE + sessio id
+    private static final String MUTEX_BASE = "_vmutex";
+
+    // used identify ApplicationContext in memcache and datastore
+    private static final String AC_BASE = "_vac";
+
+    // UIDL requests will attempt to gain access for this long before telling
+    // the client to retry
+    private static final int MAX_UIDL_WAIT_MILLISECONDS = 5000;
+
+    // Tell client to retry after this delay.
+    // Note: currently interpreting Retry-After as ms, not sec
+    private static final int RETRY_AFTER_MILLISECONDS = 100;
+
+    // Properties used in the datastore
+    private static final String PROPERTY_EXPIRES = "expires";
+    private static final String PROPERTY_DATA = "data";
+
+    // path used for cleanup
+    private static final String CLEANUP_PATH = "/CLEAN";
+    // max entities to clean at once
+    private static final int CLEANUP_LIMIT = 200;
+    // appengine session kind
+    private static final String APPENGINE_SESSION_KIND = "_ah_SESSION";
+    // appengine session expires-parameter
+    private static final String PROPERTY_APPENGINE_EXPIRES = "_expires";
+
+    protected void sendDeadlineExceededNotification(
+            WrappedHttpServletRequest request,
+            WrappedHttpServletResponse response) throws IOException {
+        criticalNotification(
+                request,
+                response,
+                "Deadline Exceeded",
+                "I'm sorry, but the operation took too long to complete. We'll try reloading to see where we're at, please take note of any unsaved data...",
+                "", null);
+    }
+
+    protected void sendNotSerializableNotification(
+            WrappedHttpServletRequest request,
+            WrappedHttpServletResponse response) throws IOException {
+        criticalNotification(
+                request,
+                response,
+                "NotSerializableException",
+                "I'm sorry, but there seems to be a serious problem, please contact the administrator. And please take note of any unsaved data...",
+                "", getApplicationUrl(request).toString()
+                        + "?restartApplication");
+    }
+
+    protected void sendCriticalErrorNotification(
+            WrappedHttpServletRequest request,
+            WrappedHttpServletResponse response) throws IOException {
+        criticalNotification(
+                request,
+                response,
+                "Critical error",
+                "I'm sorry, but there seems to be a serious problem, please contact the administrator. And please take note of any unsaved data...",
+                "", getApplicationUrl(request).toString()
+                        + "?restartApplication");
+    }
+
+    @Override
+    protected void service(HttpServletRequest unwrappedRequest,
+            HttpServletResponse unwrappedResponse) throws ServletException,
+            IOException {
+        WrappedHttpServletRequest request = new WrappedHttpServletRequest(
+                unwrappedRequest, getDeploymentConfiguration());
+        WrappedHttpServletResponse response = new WrappedHttpServletResponse(
+                unwrappedResponse, getDeploymentConfiguration());
+
+        if (isCleanupRequest(request)) {
+            cleanDatastore();
+            return;
+        }
+
+        RequestType requestType = getRequestType(request);
+
+        if (requestType == RequestType.STATIC_FILE) {
+            // no locking needed, let superclass handle
+            super.service(request, response);
+            cleanSession(request);
+            return;
+        }
+
+        if (requestType == RequestType.APPLICATION_RESOURCE) {
+            // no locking needed, let superclass handle
+            getApplicationContext(request,
+                    MemcacheServiceFactory.getMemcacheService());
+            super.service(request, response);
+            cleanSession(request);
+            return;
+        }
+
+        final HttpSession session = request
+                .getSession(requestCanCreateApplication(request, requestType));
+        if (session == null) {
+            handleServiceSessionExpired(request, response);
+            cleanSession(request);
+            return;
+        }
+
+        boolean locked = false;
+        MemcacheService memcache = null;
+        String mutex = MUTEX_BASE + session.getId();
+        memcache = MemcacheServiceFactory.getMemcacheService();
+        try {
+            // try to get lock
+            long started = new Date().getTime();
+            // non-UIDL requests will try indefinitely
+            while (requestType != RequestType.UIDL
+                    || new Date().getTime() - started < MAX_UIDL_WAIT_MILLISECONDS) {
+                locked = memcache.put(mutex, 1, Expiration.byDeltaSeconds(40),
+                        MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT);
+                if (locked) {
+                    break;
+                }
+                try {
+                    Thread.sleep(RETRY_AFTER_MILLISECONDS);
+                } catch (InterruptedException e) {
+                    getLogger().finer(
+                            "Thread.sleep() interrupted while waiting for lock. Trying again. "
+                                    + e);
+                }
+            }
+
+            if (!locked) {
+                // Not locked; only UIDL can get trough here unlocked: tell
+                // client to retry
+                response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+                // Note: currently interpreting Retry-After as ms, not sec
+                response.setHeader("Retry-After", "" + RETRY_AFTER_MILLISECONDS);
+                return;
+            }
+
+            // de-serialize or create application context, store in session
+            ApplicationContext ctx = getApplicationContext(request, memcache);
+
+            super.service(request, response);
+
+            // serialize
+            started = new Date().getTime();
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            oos.writeObject(ctx);
+            oos.flush();
+            byte[] bytes = baos.toByteArray();
+
+            started = new Date().getTime();
+
+            String id = AC_BASE + session.getId();
+            Date expire = new Date(started
+                    + (session.getMaxInactiveInterval() * 1000));
+            Expiration expires = Expiration.onDate(expire);
+
+            memcache.put(id, bytes, expires);
+
+            DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
+            Entity entity = new Entity(AC_BASE, id);
+            entity.setProperty(PROPERTY_EXPIRES, expire.getTime());
+            entity.setProperty(PROPERTY_DATA, new Blob(bytes));
+            ds.put(entity);
+
+        } catch (DeadlineExceededException e) {
+            getLogger().warning("DeadlineExceeded for " + session.getId());
+            sendDeadlineExceededNotification(request, response);
+        } catch (NotSerializableException e) {
+            getLogger().log(Level.SEVERE, "Not serializable!", e);
+
+            // TODO this notification is usually not shown - should we redirect
+            // in some other way - can we?
+            sendNotSerializableNotification(request, response);
+        } catch (Exception e) {
+            getLogger().log(Level.WARNING,
+                    "An exception occurred while servicing request.", e);
+
+            sendCriticalErrorNotification(request, response);
+        } finally {
+            // "Next, please!"
+            if (locked) {
+                memcache.delete(mutex);
+            }
+            cleanSession(request);
+        }
+    }
+
+    protected ApplicationContext getApplicationContext(
+            HttpServletRequest request, MemcacheService memcache) {
+        HttpSession session = request.getSession();
+        String id = AC_BASE + session.getId();
+        byte[] serializedAC = (byte[]) memcache.get(id);
+        if (serializedAC == null) {
+            DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
+            Key key = KeyFactory.createKey(AC_BASE, id);
+            Entity entity = null;
+            try {
+                entity = ds.get(key);
+            } catch (EntityNotFoundException e) {
+                // Ok, we were a bit optimistic; we'll create a new one later
+            }
+            if (entity != null) {
+                Blob blob = (Blob) entity.getProperty(PROPERTY_DATA);
+                serializedAC = blob.getBytes();
+                // bring it to memcache
+                memcache.put(AC_BASE + session.getId(), serializedAC,
+                        Expiration.byDeltaSeconds(session
+                                .getMaxInactiveInterval()),
+                        MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT);
+            }
+        }
+        if (serializedAC != null) {
+            ByteArrayInputStream bais = new ByteArrayInputStream(serializedAC);
+            ObjectInputStream ois;
+            try {
+                ois = new ObjectInputStream(bais);
+                ApplicationContext applicationContext = (ApplicationContext) ois
+                        .readObject();
+                session.setAttribute(ServletApplicationContext.class.getName(),
+                        applicationContext);
+            } catch (IOException e) {
+                getLogger().log(
+                        Level.WARNING,
+                        "Could not de-serialize ApplicationContext for "
+                                + session.getId()
+                                + " A new one will be created. ", e);
+            } catch (ClassNotFoundException e) {
+                getLogger().log(
+                        Level.WARNING,
+                        "Could not de-serialize ApplicationContext for "
+                                + session.getId()
+                                + " A new one will be created. ", e);
+            }
+        }
+        // will create new context if the above did not
+        return getApplicationContext(session);
+
+    }
+
+    private boolean isCleanupRequest(HttpServletRequest request) {
+        String path = getRequestPathInfo(request);
+        if (path != null && path.equals(CLEANUP_PATH)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Removes the ApplicationContext from the session in order to minimize the
+     * data serialized to datastore and memcache.
+     * 
+     * @param request
+     */
+    private void cleanSession(HttpServletRequest request) {
+        HttpSession session = request.getSession(false);
+        if (session != null) {
+            session.removeAttribute(ServletApplicationContext.class.getName());
+        }
+    }
+
+    /**
+     * This will look at the timestamp and delete expired persisted Vaadin and
+     * appengine sessions from the datastore.
+     * 
+     * TODO Possible improvements include: 1. Use transactions (requires entity
+     * groups - overkill?) 2. Delete one-at-a-time, catch possible exception,
+     * continue w/ next.
+     */
+    private void cleanDatastore() {
+        long expire = new Date().getTime();
+        try {
+            DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
+            // Vaadin stuff first
+            {
+                Query q = new Query(AC_BASE);
+                q.setKeysOnly();
+
+                q.addFilter(PROPERTY_EXPIRES,
+                        FilterOperator.LESS_THAN_OR_EQUAL, expire);
+                PreparedQuery pq = ds.prepare(q);
+                List<Entity> entities = pq.asList(Builder
+                        .withLimit(CLEANUP_LIMIT));
+                if (entities != null) {
+                    getLogger().info(
+                            "Vaadin cleanup deleting " + entities.size()
+                                    + " expired Vaadin sessions.");
+                    List<Key> keys = new ArrayList<Key>();
+                    for (Entity e : entities) {
+                        keys.add(e.getKey());
+                    }
+                    ds.delete(keys);
+                }
+            }
+            // Also cleanup GAE sessions
+            {
+                Query q = new Query(APPENGINE_SESSION_KIND);
+                q.setKeysOnly();
+                q.addFilter(PROPERTY_APPENGINE_EXPIRES,
+                        FilterOperator.LESS_THAN_OR_EQUAL, expire);
+                PreparedQuery pq = ds.prepare(q);
+                List<Entity> entities = pq.asList(Builder
+                        .withLimit(CLEANUP_LIMIT));
+                if (entities != null) {
+                    getLogger().info(
+                            "Vaadin cleanup deleting " + entities.size()
+                                    + " expired appengine sessions.");
+                    List<Key> keys = new ArrayList<Key>();
+                    for (Entity e : entities) {
+                        keys.add(e.getKey());
+                    }
+                    ds.delete(keys);
+                }
+            }
+        } catch (Exception e) {
+            getLogger().log(Level.WARNING, "Exception while cleaning.", e);
+        }
+    }
+
+    private static final Logger getLogger() {
+        return Logger.getLogger(GAEVaadinServlet.class.getName());
+    }
+}
index 639ff117d13ca6de8c30760d18f3469e2566b7f7..a38c5232542b2b1cd67ff57761729cfd8a91d82e 100644 (file)
@@ -123,7 +123,7 @@ public class ServletApplicationContext extends ApplicationContext {
      */
     @Override
     public File getBaseDirectory() {
-        final String realPath = ApplicationServlet.getResourcePath(
+        final String realPath = VaadinServlet.getResourcePath(
                 session.getServletContext(), "/");
         if (realPath == null) {
             return null;
@@ -175,7 +175,7 @@ public class ServletApplicationContext extends ApplicationContext {
      * @return CommunicationManager
      */
     public CommunicationManager getApplicationManager(Application application,
-            AbstractApplicationServlet servlet) {
+            VaadinServlet servlet) {
         CommunicationManager mgr = (CommunicationManager) applicationToAjaxAppMgrMap
                 .get(application);
 
index 5d1426e87c3b165ff2ed07b4d8c1641503ee0815..aa9ffcaf52aa8e72100b8cfe77d3c607a2c51ce5 100644 (file)
@@ -80,8 +80,7 @@ public class SystemError extends AbstractErrorMessage {
         StringBuilder sb = new StringBuilder();
         if (getMessage() != null) {
             sb.append("<h2>");
-            sb.append(AbstractApplicationServlet
-                    .safeEscapeForHtml(getMessage()));
+            sb.append(VaadinServlet.safeEscapeForHtml(getMessage()));
             sb.append("</h2>");
         }
         return sb.toString();
diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java
new file mode 100644 (file)
index 0000000..0766d46
--- /dev/null
@@ -0,0 +1,1564 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * 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 com.vaadin.server;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import com.vaadin.Application;
+import com.vaadin.Application.ApplicationStartEvent;
+import com.vaadin.server.AbstractCommunicationManager.Callback;
+import com.vaadin.server.ServletPortletHelper.ApplicationClassException;
+import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.ui.UI;
+
+@SuppressWarnings("serial")
+public class VaadinServlet extends HttpServlet implements Constants {
+
+    private static class AbstractApplicationServletWrapper implements Callback {
+
+        private final VaadinServlet servlet;
+
+        public AbstractApplicationServletWrapper(VaadinServlet servlet) {
+            this.servlet = servlet;
+        }
+
+        @Override
+        public void criticalNotification(WrappedRequest request,
+                WrappedResponse response, String cap, String msg,
+                String details, String outOfSyncURL) throws IOException {
+            servlet.criticalNotification(
+                    WrappedHttpServletRequest.cast(request),
+                    ((WrappedHttpServletResponse) response), cap, msg, details,
+                    outOfSyncURL);
+        }
+    }
+
+    // TODO Move some (all?) of the constants to a separate interface (shared
+    // with portlet)
+
+    private final String resourcePath = null;
+
+    private DeploymentConfiguration deploymentConfiguration;
+
+    private AddonContext addonContext;
+
+    /**
+     * Called by the servlet container to indicate to a servlet that the servlet
+     * is being placed into service.
+     * 
+     * @param servletConfig
+     *            the object containing the servlet's configuration and
+     *            initialization parameters
+     * @throws javax.servlet.ServletException
+     *             if an exception has occurred that interferes with the
+     *             servlet's normal operation.
+     */
+    @Override
+    public void init(javax.servlet.ServletConfig servletConfig)
+            throws javax.servlet.ServletException {
+        super.init(servletConfig);
+        Properties applicationProperties = new Properties();
+
+        // Read default parameters from server.xml
+        final ServletContext context = servletConfig.getServletContext();
+        for (final Enumeration<String> e = context.getInitParameterNames(); e
+                .hasMoreElements();) {
+            final String name = e.nextElement();
+            applicationProperties.setProperty(name,
+                    context.getInitParameter(name));
+        }
+
+        // Override with application config from web.xml
+        for (final Enumeration<String> e = servletConfig
+                .getInitParameterNames(); e.hasMoreElements();) {
+            final String name = e.nextElement();
+            applicationProperties.setProperty(name,
+                    servletConfig.getInitParameter(name));
+        }
+
+        deploymentConfiguration = new AbstractDeploymentConfiguration(
+                getClass(), applicationProperties) {
+
+            @Override
+            public String getStaticFileLocation(WrappedRequest request) {
+                HttpServletRequest servletRequest = WrappedHttpServletRequest
+                        .cast(request);
+                return VaadinServlet.this
+                        .getStaticFilesLocation(servletRequest);
+            }
+
+            @Override
+            public String getConfiguredWidgetset(WrappedRequest request) {
+                return getApplicationOrSystemProperty(
+                        VaadinServlet.PARAMETER_WIDGETSET,
+                        VaadinServlet.DEFAULT_WIDGETSET);
+            }
+
+            @Override
+            public String getConfiguredTheme(WrappedRequest request) {
+                // Use the default
+                return VaadinServlet.getDefaultTheme();
+            }
+
+            @Override
+            public boolean isStandalone(WrappedRequest request) {
+                return true;
+            }
+
+            @Override
+            public String getMimeType(String resourceName) {
+                return getServletContext().getMimeType(resourceName);
+            }
+
+            @Override
+            public SystemMessages getSystemMessages() {
+                return VaadinServlet.this.getSystemMessages();
+            }
+        };
+
+        addonContext = new AddonContext(deploymentConfiguration);
+        addonContext.init();
+    }
+
+    @Override
+    public void destroy() {
+        super.destroy();
+
+        addonContext.destroy();
+    }
+
+    /**
+     * Returns true if the servlet is running in production mode. Production
+     * mode disables all debug facilities.
+     * 
+     * @return true if in production mode, false if in debug mode
+     */
+    public boolean isProductionMode() {
+        return getDeploymentConfiguration().isProductionMode();
+    }
+
+    /**
+     * Returns the number of seconds the browser should cache a file. Default is
+     * 1 hour (3600 s).
+     * 
+     * @return The number of seconds files are cached in the browser
+     */
+    public int getResourceCacheTime() {
+        return getDeploymentConfiguration().getResourceCacheTime();
+    }
+
+    /**
+     * Receives standard HTTP requests from the public service method and
+     * dispatches them.
+     * 
+     * @param request
+     *            the object that contains the request the client made of the
+     *            servlet.
+     * @param response
+     *            the object that contains the response the servlet returns to
+     *            the client.
+     * @throws ServletException
+     *             if an input or output error occurs while the servlet is
+     *             handling the TRACE request.
+     * @throws IOException
+     *             if the request for the TRACE cannot be handled.
+     */
+
+    @Override
+    protected void service(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        service(createWrappedRequest(request), createWrappedResponse(response));
+    }
+
+    private void service(WrappedHttpServletRequest request,
+            WrappedHttpServletResponse response) throws ServletException,
+            IOException {
+        RequestTimer requestTimer = new RequestTimer();
+        requestTimer.start();
+
+        AbstractApplicationServletWrapper servletWrapper = new AbstractApplicationServletWrapper(
+                this);
+
+        RequestType requestType = getRequestType(request);
+        if (!ensureCookiesEnabled(requestType, request, response)) {
+            return;
+        }
+
+        if (requestType == RequestType.STATIC_FILE) {
+            serveStaticResources(request, response);
+            return;
+        }
+
+        Application application = null;
+        boolean transactionStarted = false;
+        boolean requestStarted = false;
+        boolean applicationRunning = false;
+
+        try {
+            // If a duplicate "close application" URL is received for an
+            // application that is not open, redirect to the application's main
+            // page.
+            // This is needed as e.g. Spring Security remembers the last
+            // URL from the application, which is the logout URL, and repeats
+            // it.
+            // We can tell apart a real onunload request from a repeated one
+            // based on the real one having content (at least the UIDL security
+            // key).
+            if (requestType == RequestType.UIDL
+                    && request.getParameterMap().containsKey(
+                            ApplicationConstants.PARAM_UNLOADBURST)
+                    && request.getContentLength() < 1
+                    && getExistingApplication(request, false) == null) {
+                redirectToApplication(request, response);
+                return;
+            }
+
+            // Find out which application this request is related to
+            application = findApplicationInstance(request, requestType);
+            if (application == null) {
+                return;
+            }
+            Application.setCurrent(application);
+
+            /*
+             * Get or create a WebApplicationContext and an ApplicationManager
+             * for the session
+             */
+            ServletApplicationContext webApplicationContext = getApplicationContext(request
+                    .getSession());
+            CommunicationManager applicationManager = webApplicationContext
+                    .getApplicationManager(application, this);
+
+            if (requestType == RequestType.CONNECTOR_RESOURCE) {
+                applicationManager.serveConnectorResource(request, response);
+                return;
+            } else if (requestType == RequestType.HEARTBEAT) {
+                applicationManager.handleHeartbeatRequest(request, response,
+                        application);
+                return;
+            }
+
+            /* Update browser information from the request */
+            webApplicationContext.getBrowser().updateRequestDetails(request);
+
+            /*
+             * Call application requestStart before Application.init() is called
+             * (bypasses the limitation in TransactionListener)
+             */
+            if (application instanceof HttpServletRequestListener) {
+                ((HttpServletRequestListener) application).onRequestStart(
+                        request, response);
+                requestStarted = true;
+            }
+
+            // Start the application if it's newly created
+            startApplication(request, application, webApplicationContext);
+            applicationRunning = true;
+
+            /*
+             * Transaction starts. Call transaction listeners. Transaction end
+             * is called in the finally block below.
+             */
+            webApplicationContext.startTransaction(application, request);
+            transactionStarted = true;
+
+            /* Handle the request */
+            if (requestType == RequestType.FILE_UPLOAD) {
+                // UI is resolved in communication manager
+                applicationManager.handleFileUpload(application, request,
+                        response);
+                return;
+            } else if (requestType == RequestType.UIDL) {
+                UI uI = application.getUIForRequest(request);
+                if (uI == null) {
+                    throw new ServletException(ERROR_NO_UI_FOUND);
+                }
+                // Handles AJAX UIDL requests
+                applicationManager.handleUidlRequest(request, response,
+                        servletWrapper, uI);
+                return;
+            } else if (requestType == RequestType.BROWSER_DETAILS) {
+                // Browser details - not related to a specific UI
+                applicationManager.handleBrowserDetailsRequest(request,
+                        response, application);
+                return;
+            }
+
+            // Removes application if it has stopped (maybe by thread or
+            // transactionlistener)
+            if (!application.isRunning()) {
+                endApplication(request, response, application);
+                return;
+            }
+
+            if (applicationManager.handleApplicationRequest(request, response)) {
+                return;
+            }
+            // TODO Should return 404 error here and not do anything more
+
+        } catch (final SessionExpiredException e) {
+            // Session has expired, notify user
+            handleServiceSessionExpired(request, response);
+        } catch (final GeneralSecurityException e) {
+            handleServiceSecurityException(request, response);
+        } catch (final Throwable e) {
+            handleServiceException(request, response, application, e);
+        } finally {
+
+            if (applicationRunning) {
+                application.closeInactiveUIs();
+            }
+
+            // Notifies transaction end
+            try {
+                if (transactionStarted) {
+                    ((ServletApplicationContext) application.getContext())
+                            .endTransaction(application, request);
+
+                }
+
+            } finally {
+                try {
+                    if (requestStarted) {
+                        ((HttpServletRequestListener) application)
+                                .onRequestEnd(request, response);
+                    }
+                } finally {
+                    UI.setCurrent(null);
+                    Application.setCurrent(null);
+
+                    HttpSession session = request.getSession(false);
+                    if (session != null) {
+                        requestTimer.stop(getApplicationContext(session));
+                    }
+                }
+            }
+
+        }
+    }
+
+    private WrappedHttpServletResponse createWrappedResponse(
+            HttpServletResponse response) {
+        WrappedHttpServletResponse wrappedResponse = new WrappedHttpServletResponse(
+                response, getDeploymentConfiguration());
+        return wrappedResponse;
+    }
+
+    /**
+     * Create a wrapped request for a http servlet request. This method can be
+     * overridden if the wrapped request should have special properties.
+     * 
+     * @param request
+     *            the original http servlet request
+     * @return a wrapped request for the original request
+     */
+    protected WrappedHttpServletRequest createWrappedRequest(
+            HttpServletRequest request) {
+        return new WrappedHttpServletRequest(request,
+                getDeploymentConfiguration());
+    }
+
+    /**
+     * Gets a the deployment configuration for this servlet.
+     * 
+     * @return the deployment configuration
+     */
+    protected DeploymentConfiguration getDeploymentConfiguration() {
+        return deploymentConfiguration;
+    }
+
+    /**
+     * Check that cookie support is enabled in the browser. Only checks UIDL
+     * requests.
+     * 
+     * @param requestType
+     *            Type of the request as returned by
+     *            {@link #getRequestType(HttpServletRequest)}
+     * @param request
+     *            The request from the browser
+     * @param response
+     *            The response to which an error can be written
+     * @return false if cookies are disabled, true otherwise
+     * @throws IOException
+     */
+    private boolean ensureCookiesEnabled(RequestType requestType,
+            WrappedHttpServletRequest request,
+            WrappedHttpServletResponse response) throws IOException {
+        if (requestType == RequestType.UIDL && !isRepaintAll(request)) {
+            // In all other but the first UIDL request a cookie should be
+            // returned by the browser.
+            // This can be removed if cookieless mode (#3228) is supported
+            if (request.getRequestedSessionId() == null) {
+                // User has cookies disabled
+                criticalNotification(request, response, getSystemMessages()
+                        .getCookiesDisabledCaption(), getSystemMessages()
+                        .getCookiesDisabledMessage(), null, getSystemMessages()
+                        .getCookiesDisabledURL());
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Send a notification to client's application. Used to notify client of
+     * critical errors, session expiration and more. Server has no knowledge of
+     * what application client refers to.
+     * 
+     * @param request
+     *            the HTTP request instance.
+     * @param response
+     *            the HTTP response to write to.
+     * @param caption
+     *            the notification caption
+     * @param message
+     *            to notification body
+     * @param details
+     *            a detail message to show in addition to the message. Currently
+     *            shown directly below the message but could be hidden behind a
+     *            details drop down in the future. Mainly used to give
+     *            additional information not necessarily useful to the end user.
+     * @param url
+     *            url to load when the message is dismissed. Null will reload
+     *            the current page.
+     * @throws IOException
+     *             if the writing failed due to input/output error.
+     */
+    protected void criticalNotification(WrappedHttpServletRequest request,
+            HttpServletResponse response, String caption, String message,
+            String details, String url) throws IOException {
+
+        if (ServletPortletHelper.isUIDLRequest(request)) {
+
+            if (caption != null) {
+                caption = "\"" + JsonPaintTarget.escapeJSON(caption) + "\"";
+            }
+            if (details != null) {
+                if (message == null) {
+                    message = details;
+                } else {
+                    message += "<br/><br/>" + details;
+                }
+            }
+
+            if (message != null) {
+                message = "\"" + JsonPaintTarget.escapeJSON(message) + "\"";
+            }
+            if (url != null) {
+                url = "\"" + JsonPaintTarget.escapeJSON(url) + "\"";
+            }
+
+            String output = "for(;;);[{\"changes\":[], \"meta\" : {"
+                    + "\"appError\": {" + "\"caption\":" + caption + ","
+                    + "\"message\" : " + message + "," + "\"url\" : " + url
+                    + "}}, \"resources\": {}, \"locales\":[]}]";
+            writeResponse(response, "application/json; charset=UTF-8", output);
+        } else {
+            // Create an HTML reponse with the error
+            String output = "";
+
+            if (url != null) {
+                output += "<a href=\"" + url + "\">";
+            }
+            if (caption != null) {
+                output += "<b>" + caption + "</b><br/>";
+            }
+            if (message != null) {
+                output += message;
+                output += "<br/><br/>";
+            }
+
+            if (details != null) {
+                output += details;
+                output += "<br/><br/>";
+            }
+            if (url != null) {
+                output += "</a>";
+            }
+            writeResponse(response, "text/html; charset=UTF-8", output);
+
+        }
+
+    }
+
+    /**
+     * Writes the response in {@code output} using the contentType given in
+     * {@code contentType} to the provided {@link HttpServletResponse}
+     * 
+     * @param response
+     * @param contentType
+     * @param output
+     *            Output to write (UTF-8 encoded)
+     * @throws IOException
+     */
+    private void writeResponse(HttpServletResponse response,
+            String contentType, String output) throws IOException {
+        response.setContentType(contentType);
+        final ServletOutputStream out = response.getOutputStream();
+        // Set the response type
+        final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
+                new OutputStreamWriter(out, "UTF-8")));
+        outWriter.print(output);
+        outWriter.flush();
+        outWriter.close();
+        out.flush();
+
+    }
+
+    /**
+     * Returns the application instance to be used for the request. If an
+     * existing instance is not found a new one is created or null is returned
+     * to indicate that the application is not available.
+     * 
+     * @param request
+     * @param requestType
+     * @return
+     * @throws MalformedURLException
+     * @throws IllegalAccessException
+     * @throws InstantiationException
+     * @throws ServletException
+     * @throws SessionExpiredException
+     */
+    private Application findApplicationInstance(HttpServletRequest request,
+            RequestType requestType) throws MalformedURLException,
+            ServletException, SessionExpiredException {
+
+        boolean requestCanCreateApplication = requestCanCreateApplication(
+                request, requestType);
+
+        /* Find an existing application for this request. */
+        Application application = getExistingApplication(request,
+                requestCanCreateApplication);
+
+        if (application != null) {
+            /*
+             * There is an existing application. We can use this as long as the
+             * user not specifically requested to close or restart it.
+             */
+
+            final boolean restartApplication = (request
+                    .getParameter(URL_PARAMETER_RESTART_APPLICATION) != null);
+            final boolean closeApplication = (request
+                    .getParameter(URL_PARAMETER_CLOSE_APPLICATION) != null);
+
+            if (restartApplication) {
+                closeApplication(application, request.getSession(false));
+                return createApplication(request);
+            } else if (closeApplication) {
+                closeApplication(application, request.getSession(false));
+                return null;
+            } else {
+                return application;
+            }
+        }
+
+        // No existing application was found
+
+        if (requestCanCreateApplication) {
+            /*
+             * If the request is such that it should create a new application if
+             * one as not found, we do that.
+             */
+            return createApplication(request);
+        } else {
+            /*
+             * The application was not found and a new one should not be
+             * created. Assume the session has expired.
+             */
+            throw new SessionExpiredException();
+        }
+
+    }
+
+    /**
+     * Check if the request should create an application if an existing
+     * application is not found.
+     * 
+     * @param request
+     * @param requestType
+     * @return true if an application should be created, false otherwise
+     */
+    boolean requestCanCreateApplication(HttpServletRequest request,
+            RequestType requestType) {
+        if (requestType == RequestType.UIDL && isRepaintAll(request)) {
+            /*
+             * UIDL request contains valid repaintAll=1 event, the user probably
+             * wants to initiate a new application through a custom index.html
+             * without using the bootstrap page.
+             */
+            return true;
+
+        } else if (requestType == RequestType.OTHER) {
+            /*
+             * I.e URIs that are not application resources or static (theme)
+             * files.
+             */
+            return true;
+
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets resource path using different implementations. Required to
+     * supporting different servlet container implementations (application
+     * servers).
+     * 
+     * @param servletContext
+     * @param path
+     *            the resource path.
+     * @return the resource path.
+     */
+    protected static String getResourcePath(ServletContext servletContext,
+            String path) {
+        String resultPath = null;
+        resultPath = servletContext.getRealPath(path);
+        if (resultPath != null) {
+            return resultPath;
+        } else {
+            try {
+                final URL url = servletContext.getResource(path);
+                resultPath = url.getFile();
+            } catch (final Exception e) {
+                // FIXME: Handle exception
+                getLogger().log(Level.INFO,
+                        "Could not find resource path " + path, e);
+            }
+        }
+        return resultPath;
+    }
+
+    /**
+     * Creates a new application and registers it into WebApplicationContext
+     * (aka session). This is not meant to be overridden. Override
+     * getNewApplication to create the application instance in a custom way.
+     * 
+     * @param request
+     * @return
+     * @throws ServletException
+     * @throws MalformedURLException
+     */
+    private Application createApplication(HttpServletRequest request)
+            throws ServletException, MalformedURLException {
+        Application newApplication = getNewApplication(request);
+
+        final ServletApplicationContext context = getApplicationContext(request
+                .getSession());
+        context.addApplication(newApplication);
+
+        return newApplication;
+    }
+
+    private void handleServiceException(WrappedHttpServletRequest request,
+            WrappedHttpServletResponse response, Application application,
+            Throwable e) throws IOException, ServletException {
+        // if this was an UIDL request, response UIDL back to client
+        if (getRequestType(request) == RequestType.UIDL) {
+            SystemMessages ci = getSystemMessages();
+            criticalNotification(request, response,
+                    ci.getInternalErrorCaption(), ci.getInternalErrorMessage(),
+                    null, ci.getInternalErrorURL());
+            if (application != null) {
+                application.getErrorHandler()
+                        .terminalError(new RequestError(e));
+            } else {
+                throw new ServletException(e);
+            }
+        } else {
+            // Re-throw other exceptions
+            throw new ServletException(e);
+        }
+
+    }
+
+    /**
+     * A helper method to strip away characters that might somehow be used for
+     * XSS attacs. Leaves at least alphanumeric characters intact. Also removes
+     * eg. ( and ), so values should be safe in javascript too.
+     * 
+     * @param themeName
+     * @return
+     */
+    protected static String stripSpecialChars(String themeName) {
+        StringBuilder sb = new StringBuilder();
+        char[] charArray = themeName.toCharArray();
+        for (int i = 0; i < charArray.length; i++) {
+            char c = charArray[i];
+            if (!CHAR_BLACKLIST.contains(c)) {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    private static final Collection<Character> CHAR_BLACKLIST = new HashSet<Character>(
+            Arrays.asList(new Character[] { '&', '"', '\'', '<', '>', '(', ')',
+                    ';' }));
+
+    /**
+     * Returns the default theme. Must never return null.
+     * 
+     * @return
+     */
+    public static String getDefaultTheme() {
+        return DEFAULT_THEME_NAME;
+    }
+
+    void handleServiceSessionExpired(WrappedHttpServletRequest request,
+            WrappedHttpServletResponse response) throws IOException,
+            ServletException {
+
+        if (isOnUnloadRequest(request)) {
+            /*
+             * Request was an unload request (e.g. window close event) and the
+             * client expects no response if it fails.
+             */
+            return;
+        }
+
+        try {
+            SystemMessages ci = getSystemMessages();
+            if (getRequestType(request) != RequestType.UIDL) {
+                // 'plain' http req - e.g. browser reload;
+                // just go ahead redirect the browser
+                response.sendRedirect(ci.getSessionExpiredURL());
+            } else {
+                /*
+                 * Invalidate session (weird to have session if we're saying
+                 * that it's expired, and worse: portal integration will fail
+                 * since the session is not created by the portal.
+                 * 
+                 * Session must be invalidated before criticalNotification as it
+                 * commits the response.
+                 */
+                request.getSession().invalidate();
+
+                // send uidl redirect
+                criticalNotification(request, response,
+                        ci.getSessionExpiredCaption(),
+                        ci.getSessionExpiredMessage(), null,
+                        ci.getSessionExpiredURL());
+
+            }
+        } catch (SystemMessageException ee) {
+            throw new ServletException(ee);
+        }
+
+    }
+
+    private void handleServiceSecurityException(
+            WrappedHttpServletRequest request,
+            WrappedHttpServletResponse response) throws IOException,
+            ServletException {
+        if (isOnUnloadRequest(request)) {
+            /*
+             * Request was an unload request (e.g. window close event) and the
+             * client expects no response if it fails.
+             */
+            return;
+        }
+
+        try {
+            SystemMessages ci = getSystemMessages();
+            if (getRequestType(request) != RequestType.UIDL) {
+                // 'plain' http req - e.g. browser reload;
+                // just go ahead redirect the browser
+                response.sendRedirect(ci.getCommunicationErrorURL());
+            } else {
+                // send uidl redirect
+                criticalNotification(request, response,
+                        ci.getCommunicationErrorCaption(),
+                        ci.getCommunicationErrorMessage(),
+                        INVALID_SECURITY_KEY_MSG, ci.getCommunicationErrorURL());
+                /*
+                 * Invalidate session. Portal integration will fail otherwise
+                 * since the session is not created by the portal.
+                 */
+                request.getSession().invalidate();
+            }
+        } catch (SystemMessageException ee) {
+            throw new ServletException(ee);
+        }
+
+        log("Invalid security key received from " + request.getRemoteHost());
+    }
+
+    /**
+     * Creates a new application for the given request.
+     * 
+     * @param request
+     *            the HTTP request.
+     * @return A new Application instance.
+     * @throws ServletException
+     */
+    protected Application getNewApplication(HttpServletRequest request)
+            throws ServletException {
+
+        // Creates a new application instance
+        try {
+            Class<? extends Application> applicationClass = ServletPortletHelper
+                    .getApplicationClass(getDeploymentConfiguration());
+
+            final Application application = applicationClass.newInstance();
+            application.addUIProvider(new DefaultUIProvider());
+
+            return application;
+        } catch (final IllegalAccessException e) {
+            throw new ServletException("getNewApplication failed", e);
+        } catch (final InstantiationException e) {
+            throw new ServletException("getNewApplication failed", e);
+        } catch (ApplicationClassException e) {
+            throw new ServletException("getNewApplication failed", e);
+        }
+    }
+
+    /**
+     * Starts the application if it is not already running.
+     * 
+     * @param request
+     * @param application
+     * @param webApplicationContext
+     * @throws ServletException
+     * @throws MalformedURLException
+     */
+    private void startApplication(HttpServletRequest request,
+            Application application,
+            ServletApplicationContext webApplicationContext)
+            throws ServletException, MalformedURLException {
+
+        if (!application.isRunning()) {
+            // Create application
+            final URL applicationUrl = getApplicationUrl(request);
+
+            // Initial locale comes from the request
+            Locale locale = request.getLocale();
+            application.setLocale(locale);
+            application.start(new ApplicationStartEvent(applicationUrl,
+                    getDeploymentConfiguration(), webApplicationContext));
+            addonContext.fireApplicationStarted(application);
+        }
+    }
+
+    /**
+     * Check if this is a request for a static resource and, if it is, serve the
+     * resource to the client.
+     * 
+     * @param request
+     * @param response
+     * @return true if a file was served and the request has been handled, false
+     *         otherwise.
+     * @throws IOException
+     * @throws ServletException
+     */
+    private boolean serveStaticResources(HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException {
+
+        // FIXME What does 10 refer to?
+        String pathInfo = request.getPathInfo();
+        if (pathInfo == null || pathInfo.length() <= 10) {
+            return false;
+        }
+
+        if ((request.getContextPath() != null)
+                && (request.getRequestURI().startsWith("/VAADIN/"))) {
+            serveStaticResourcesInVAADIN(request.getRequestURI(), request,
+                    response);
+            return true;
+        } else if (request.getRequestURI().startsWith(
+                request.getContextPath() + "/VAADIN/")) {
+            serveStaticResourcesInVAADIN(
+                    request.getRequestURI().substring(
+                            request.getContextPath().length()), request,
+                    response);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Serve resources from VAADIN directory.
+     * 
+     * @param filename
+     *            The filename to serve. Should always start with /VAADIN/.
+     * @param request
+     * @param response
+     * @throws IOException
+     * @throws ServletException
+     */
+    private void serveStaticResourcesInVAADIN(String filename,
+            HttpServletRequest request, HttpServletResponse response)
+            throws IOException, ServletException {
+
+        final ServletContext sc = getServletContext();
+        URL resourceUrl = sc.getResource(filename);
+        if (resourceUrl == null) {
+            // try if requested file is found from classloader
+
+            // strip leading "/" otherwise stream from JAR wont work
+            filename = filename.substring(1);
+            resourceUrl = getDeploymentConfiguration().getClassLoader()
+                    .getResource(filename);
+
+            if (resourceUrl == null) {
+                // cannot serve requested file
+                getLogger()
+                        .info("Requested resource ["
+                                + filename
+                                + "] not found from filesystem or through class loader."
+                                + " Add widgetset and/or theme JAR to your classpath or add files to WebContent/VAADIN folder.");
+                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                return;
+            }
+
+            // security check: do not permit navigation out of the VAADIN
+            // directory
+            if (!isAllowedVAADINResourceUrl(request, resourceUrl)) {
+                getLogger()
+                        .info("Requested resource ["
+                                + filename
+                                + "] not accessible in the VAADIN directory or access to it is forbidden.");
+                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+                return;
+            }
+        }
+
+        // Find the modification timestamp
+        long lastModifiedTime = 0;
+        URLConnection connection = null;
+        try {
+            connection = resourceUrl.openConnection();
+            lastModifiedTime = connection.getLastModified();
+            // Remove milliseconds to avoid comparison problems (milliseconds
+            // are not returned by the browser in the "If-Modified-Since"
+            // header).
+            lastModifiedTime = lastModifiedTime - lastModifiedTime % 1000;
+
+            if (browserHasNewestVersion(request, lastModifiedTime)) {
+                response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+                return;
+            }
+        } catch (Exception e) {
+            // Failed to find out last modified timestamp. Continue without it.
+            getLogger()
+                    .log(Level.FINEST,
+                            "Failed to find out last modified timestamp. Continuing without it.",
+                            e);
+        } finally {
+            if (connection instanceof URLConnection) {
+                try {
+                    // Explicitly close the input stream to prevent it
+                    // from remaining hanging
+                    // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4257700
+                    InputStream is = connection.getInputStream();
+                    if (is != null) {
+                        is.close();
+                    }
+                } catch (IOException e) {
+                    getLogger().log(Level.INFO,
+                            "Error closing URLConnection input stream", e);
+                }
+            }
+        }
+
+        // Set type mime type if we can determine it based on the filename
+        final String mimetype = sc.getMimeType(filename);
+        if (mimetype != null) {
+            response.setContentType(mimetype);
+        }
+
+        // Provide modification timestamp to the browser if it is known.
+        if (lastModifiedTime > 0) {
+            response.setDateHeader("Last-Modified", lastModifiedTime);
+            /*
+             * The browser is allowed to cache for 1 hour without checking if
+             * the file has changed. This forces browsers to fetch a new version
+             * when the Vaadin version is updated. This will cause more requests
+             * to the servlet than without this but for high volume sites the
+             * static files should never be served through the servlet. The
+             * cache timeout can be configured by setting the resourceCacheTime
+             * parameter in web.xml
+             */
+            response.setHeader("Cache-Control",
+                    "max-age= " + String.valueOf(getResourceCacheTime()));
+        }
+
+        // Write the resource to the client.
+        final OutputStream os = response.getOutputStream();
+        final byte buffer[] = new byte[DEFAULT_BUFFER_SIZE];
+        int bytes;
+        InputStream is = resourceUrl.openStream();
+        while ((bytes = is.read(buffer)) >= 0) {
+            os.write(buffer, 0, bytes);
+        }
+        is.close();
+    }
+
+    /**
+     * Check whether a URL obtained from a classloader refers to a valid static
+     * resource in the directory VAADIN.
+     * 
+     * Warning: Overriding of this method is not recommended, but is possible to
+     * support non-default classloaders or servers that may produce URLs
+     * different from the normal ones. The method prototype may change in the
+     * future. Care should be taken not to expose class files or other resources
+     * outside the VAADIN directory if the method is overridden.
+     * 
+     * @param request
+     * @param resourceUrl
+     * @return
+     * 
+     * @since 6.6.7
+     */
+    protected boolean isAllowedVAADINResourceUrl(HttpServletRequest request,
+            URL resourceUrl) {
+        if ("jar".equals(resourceUrl.getProtocol())) {
+            // This branch is used for accessing resources directly from the
+            // Vaadin JAR in development environments and in similar cases.
+
+            // Inside a JAR, a ".." would mean a real directory named ".." so
+            // using it in paths should just result in the file not being found.
+            // However, performing a check in case some servers or class loaders
+            // try to normalize the path by collapsing ".." before the class
+            // loader sees it.
+
+            if (!resourceUrl.getPath().contains("!/VAADIN/")) {
+                getLogger().info(
+                        "Blocked attempt to access a JAR entry not starting with /VAADIN/: "
+                                + resourceUrl);
+                return false;
+            }
+            getLogger().fine(
+                    "Accepted access to a JAR entry using a class loader: "
+                            + resourceUrl);
+            return true;
+        } else {
+            // Some servers such as GlassFish extract files from JARs (file:)
+            // and e.g. JBoss 5+ use protocols vsf: and vfsfile: .
+
+            // Check that the URL is in a VAADIN directory and does not contain
+            // "/../"
+            if (!resourceUrl.getPath().contains("/VAADIN/")
+                    || resourceUrl.getPath().contains("/../")) {
+                getLogger().info(
+                        "Blocked attempt to access file: " + resourceUrl);
+                return false;
+            }
+            getLogger().fine(
+                    "Accepted access to a file using a class loader: "
+                            + resourceUrl);
+            return true;
+        }
+    }
+
+    /**
+     * Checks if the browser has an up to date cached version of requested
+     * resource. Currently the check is performed using the "If-Modified-Since"
+     * header. Could be expanded if needed.
+     * 
+     * @param request
+     *            The HttpServletRequest from the browser.
+     * @param resourceLastModifiedTimestamp
+     *            The timestamp when the resource was last modified. 0 if the
+     *            last modification time is unknown.
+     * @return true if the If-Modified-Since header tells the cached version in
+     *         the browser is up to date, false otherwise
+     */
+    private boolean browserHasNewestVersion(HttpServletRequest request,
+            long resourceLastModifiedTimestamp) {
+        if (resourceLastModifiedTimestamp < 1) {
+            // We do not know when it was modified so the browser cannot have an
+            // up-to-date version
+            return false;
+        }
+        /*
+         * The browser can request the resource conditionally using an
+         * If-Modified-Since header. Check this against the last modification
+         * time.
+         */
+        try {
+            // If-Modified-Since represents the timestamp of the version cached
+            // in the browser
+            long headerIfModifiedSince = request
+                    .getDateHeader("If-Modified-Since");
+
+            if (headerIfModifiedSince >= resourceLastModifiedTimestamp) {
+                // Browser has this an up-to-date version of the resource
+                return true;
+            }
+        } catch (Exception e) {
+            // Failed to parse header. Fail silently - the browser does not have
+            // an up-to-date version in its cache.
+        }
+        return false;
+    }
+
+    protected enum RequestType {
+        FILE_UPLOAD, BROWSER_DETAILS, UIDL, OTHER, STATIC_FILE, APPLICATION_RESOURCE, CONNECTOR_RESOURCE, HEARTBEAT;
+    }
+
+    protected RequestType getRequestType(WrappedHttpServletRequest request) {
+        if (ServletPortletHelper.isFileUploadRequest(request)) {
+            return RequestType.FILE_UPLOAD;
+        } else if (ServletPortletHelper.isConnectorResourceRequest(request)) {
+            return RequestType.CONNECTOR_RESOURCE;
+        } else if (isBrowserDetailsRequest(request)) {
+            return RequestType.BROWSER_DETAILS;
+        } else if (ServletPortletHelper.isUIDLRequest(request)) {
+            return RequestType.UIDL;
+        } else if (isStaticResourceRequest(request)) {
+            return RequestType.STATIC_FILE;
+        } else if (ServletPortletHelper.isApplicationResourceRequest(request)) {
+            return RequestType.APPLICATION_RESOURCE;
+        } else if (ServletPortletHelper.isHeartbeatRequest(request)) {
+            return RequestType.HEARTBEAT;
+        }
+        return RequestType.OTHER;
+
+    }
+
+    private static boolean isBrowserDetailsRequest(HttpServletRequest request) {
+        return "POST".equals(request.getMethod())
+                && request.getParameter("browserDetails") != null;
+    }
+
+    private boolean isStaticResourceRequest(HttpServletRequest request) {
+        String pathInfo = request.getPathInfo();
+        if (pathInfo == null || pathInfo.length() <= 10) {
+            return false;
+        }
+
+        if ((request.getContextPath() != null)
+                && (request.getRequestURI().startsWith("/VAADIN/"))) {
+            return true;
+        } else if (request.getRequestURI().startsWith(
+                request.getContextPath() + "/VAADIN/")) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private boolean isOnUnloadRequest(HttpServletRequest request) {
+        return request.getParameter(ApplicationConstants.PARAM_UNLOADBURST) != null;
+    }
+
+    /**
+     * Get system messages
+     * 
+     * @return
+     */
+    protected SystemMessages getSystemMessages() {
+        return ServletPortletHelper.DEFAULT_SYSTEM_MESSAGES;
+    }
+
+    /**
+     * Return the URL from where static files, e.g. the widgetset and the theme,
+     * are served. In a standard configuration the VAADIN folder inside the
+     * returned folder is what is used for widgetsets and themes.
+     * 
+     * The returned folder is usually the same as the context path and
+     * independent of the application.
+     * 
+     * @param request
+     * @return The location of static resources (should contain the VAADIN
+     *         directory). Never ends with a slash (/).
+     */
+    protected String getStaticFilesLocation(HttpServletRequest request) {
+
+        return getWebApplicationsStaticFileLocation(request);
+    }
+
+    /**
+     * The default method to fetch static files location (URL). This method does
+     * not check for request attribute {@value #REQUEST_VAADIN_STATIC_FILE_PATH}
+     * 
+     * @param request
+     * @return
+     */
+    private String getWebApplicationsStaticFileLocation(
+            HttpServletRequest request) {
+        String staticFileLocation;
+        // if property is defined in configurations, use that
+        staticFileLocation = getDeploymentConfiguration()
+                .getApplicationOrSystemProperty(PARAMETER_VAADIN_RESOURCES,
+                        null);
+        if (staticFileLocation != null) {
+            return staticFileLocation;
+        }
+
+        // the last (but most common) option is to generate default location
+        // from request
+
+        // if context is specified add it to widgetsetUrl
+        String ctxPath = request.getContextPath();
+
+        // FIXME: ctxPath.length() == 0 condition is probably unnecessary and
+        // might even be wrong.
+
+        if (ctxPath.length() == 0
+                && request.getAttribute("javax.servlet.include.context_path") != null) {
+            // include request (e.g portlet), get context path from
+            // attribute
+            ctxPath = (String) request
+                    .getAttribute("javax.servlet.include.context_path");
+        }
+
+        // Remove heading and trailing slashes from the context path
+        ctxPath = removeHeadingOrTrailing(ctxPath, "/");
+
+        if (ctxPath.equals("")) {
+            return "";
+        } else {
+            return "/" + ctxPath;
+        }
+    }
+
+    /**
+     * Remove any heading or trailing "what" from the "string".
+     * 
+     * @param string
+     * @param what
+     * @return
+     */
+    private static String removeHeadingOrTrailing(String string, String what) {
+        while (string.startsWith(what)) {
+            string = string.substring(1);
+        }
+
+        while (string.endsWith(what)) {
+            string = string.substring(0, string.length() - 1);
+        }
+
+        return string;
+    }
+
+    /**
+     * Write a redirect response to the main page of the application.
+     * 
+     * @param request
+     * @param response
+     * @throws IOException
+     *             if sending the redirect fails due to an input/output error or
+     *             a bad application URL
+     */
+    private void redirectToApplication(HttpServletRequest request,
+            HttpServletResponse response) throws IOException {
+        String applicationUrl = getApplicationUrl(request).toExternalForm();
+        response.sendRedirect(response.encodeRedirectURL(applicationUrl));
+    }
+
+    /**
+     * Gets the current application URL from request.
+     * 
+     * @param request
+     *            the HTTP request.
+     * @throws MalformedURLException
+     *             if the application is denied access to the persistent data
+     *             store represented by the given URL.
+     */
+    protected URL getApplicationUrl(HttpServletRequest request)
+            throws MalformedURLException {
+        final URL reqURL = new URL(
+                (request.isSecure() ? "https://" : "http://")
+                        + request.getServerName()
+                        + ((request.isSecure() && request.getServerPort() == 443)
+                                || (!request.isSecure() && request
+                                        .getServerPort() == 80) ? "" : ":"
+                                + request.getServerPort())
+                        + request.getRequestURI());
+        String servletPath = "";
+        if (request.getAttribute("javax.servlet.include.servlet_path") != null) {
+            // this is an include request
+            servletPath = request.getAttribute(
+                    "javax.servlet.include.context_path").toString()
+                    + request
+                            .getAttribute("javax.servlet.include.servlet_path");
+
+        } else {
+            servletPath = request.getContextPath() + request.getServletPath();
+        }
+
+        if (servletPath.length() == 0
+                || servletPath.charAt(servletPath.length() - 1) != '/') {
+            servletPath = servletPath + "/";
+        }
+        URL u = new URL(reqURL, servletPath);
+        return u;
+    }
+
+    /**
+     * Gets the existing application for given request. Looks for application
+     * instance for given request based on the requested URL.
+     * 
+     * @param request
+     *            the HTTP request.
+     * @param allowSessionCreation
+     *            true if a session should be created if no session exists,
+     *            false if no session should be created
+     * @return Application instance, or null if the URL does not map to valid
+     *         application.
+     * @throws MalformedURLException
+     *             if the application is denied access to the persistent data
+     *             store represented by the given URL.
+     * @throws IllegalAccessException
+     * @throws InstantiationException
+     * @throws SessionExpiredException
+     */
+    protected Application getExistingApplication(HttpServletRequest request,
+            boolean allowSessionCreation) throws MalformedURLException,
+            SessionExpiredException {
+
+        // Ensures that the session is still valid
+        final HttpSession session = request.getSession(allowSessionCreation);
+        if (session == null) {
+            throw new SessionExpiredException();
+        }
+
+        ServletApplicationContext context = getApplicationContext(session);
+
+        // Gets application list for the session.
+        final Collection<Application> applications = context.getApplications();
+
+        // Search for the application (using the application URI) from the list
+        for (final Iterator<Application> i = applications.iterator(); i
+                .hasNext();) {
+            final Application sessionApplication = i.next();
+            final String sessionApplicationPath = sessionApplication.getURL()
+                    .getPath();
+            String requestApplicationPath = getApplicationUrl(request)
+                    .getPath();
+
+            if (requestApplicationPath.equals(sessionApplicationPath)) {
+                // Found a running application
+                if (sessionApplication.isRunning()) {
+                    return sessionApplication;
+                }
+                // Application has stopped, so remove it before creating a new
+                // application
+                getApplicationContext(session).removeApplication(
+                        sessionApplication);
+                break;
+            }
+        }
+
+        // Existing application not found
+        return null;
+    }
+
+    /**
+     * Ends the application.
+     * 
+     * @param request
+     *            the HTTP request.
+     * @param response
+     *            the HTTP response to write to.
+     * @param application
+     *            the application to end.
+     * @throws IOException
+     *             if the writing failed due to input/output error.
+     */
+    private void endApplication(HttpServletRequest request,
+            HttpServletResponse response, Application application)
+            throws IOException {
+
+        String logoutUrl = application.getLogoutURL();
+        if (logoutUrl == null) {
+            logoutUrl = application.getURL().toString();
+        }
+
+        final HttpSession session = request.getSession();
+        if (session != null) {
+            getApplicationContext(session).removeApplication(application);
+        }
+
+        response.sendRedirect(response.encodeRedirectURL(logoutUrl));
+    }
+
+    /**
+     * Returns the path info; note that this _can_ be different than
+     * request.getPathInfo(). Examples where this might be useful:
+     * <ul>
+     * <li>An application runner servlet that runs different Vaadin applications
+     * based on an identifier.</li>
+     * <li>Providing a REST interface in the context root, while serving a
+     * Vaadin UI on a sub-URI using only one servlet (e.g. REST on
+     * http://example.com/foo, UI on http://example.com/foo/vaadin)</li>
+     * 
+     * @param request
+     * @return
+     */
+    protected String getRequestPathInfo(HttpServletRequest request) {
+        return request.getPathInfo();
+    }
+
+    /**
+     * Gets relative location of a theme resource.
+     * 
+     * @param theme
+     *            the Theme name.
+     * @param resource
+     *            the Theme resource.
+     * @return External URI specifying the resource
+     */
+    public String getResourceLocation(String theme, ThemeResource resource) {
+
+        if (resourcePath == null) {
+            return resource.getResourceId();
+        }
+        return resourcePath + theme + "/" + resource.getResourceId();
+    }
+
+    private boolean isRepaintAll(HttpServletRequest request) {
+        return (request.getParameter(URL_PARAMETER_REPAINT_ALL) != null)
+                && (request.getParameter(URL_PARAMETER_REPAINT_ALL).equals("1"));
+    }
+
+    private void closeApplication(Application application, HttpSession session) {
+        if (application == null) {
+            return;
+        }
+
+        application.close();
+        if (session != null) {
+            ServletApplicationContext context = getApplicationContext(session);
+            context.removeApplication(application);
+        }
+    }
+
+    /**
+     * 
+     * Gets the application context from an HttpSession. If no context is
+     * currently stored in a session a new context is created and stored in the
+     * session.
+     * 
+     * @param session
+     *            the HTTP session.
+     * @return the application context for HttpSession.
+     */
+    protected ServletApplicationContext getApplicationContext(
+            HttpSession session) {
+        /*
+         * TODO the ApplicationContext.getApplicationContext() should be removed
+         * and logic moved here. Now overriding context type is possible, but
+         * the whole creation logic should be here. MT 1101
+         */
+        return ServletApplicationContext.getApplicationContext(session);
+    }
+
+    public class RequestError implements Terminal.ErrorEvent, Serializable {
+
+        private final Throwable throwable;
+
+        public RequestError(Throwable throwable) {
+            this.throwable = throwable;
+        }
+
+        @Override
+        public Throwable getThrowable() {
+            return throwable;
+        }
+
+    }
+
+    /**
+     * Override this method if you need to use a specialized communicaiton
+     * mananger implementation.
+     * 
+     * @deprecated Instead of overriding this method, override
+     *             {@link ServletApplicationContext} implementation via
+     *             {@link VaadinServlet#getApplicationContext(HttpSession)}
+     *             method and in that customized implementation return your
+     *             CommunicationManager in
+     *             {@link ServletApplicationContext#getApplicationManager(Application, VaadinServlet)}
+     *             method.
+     * 
+     * @param application
+     * @return
+     */
+    @Deprecated
+    public CommunicationManager createCommunicationManager(
+            Application application) {
+        return new CommunicationManager(application);
+    }
+
+    /**
+     * Escapes characters to html entities. An exception is made for some
+     * "safe characters" to keep the text somewhat readable.
+     * 
+     * @param unsafe
+     * @return a safe string to be added inside an html tag
+     */
+    public static final String safeEscapeForHtml(String unsafe) {
+        if (null == unsafe) {
+            return null;
+        }
+        StringBuilder safe = new StringBuilder();
+        char[] charArray = unsafe.toCharArray();
+        for (int i = 0; i < charArray.length; i++) {
+            char c = charArray[i];
+            if (isSafe(c)) {
+                safe.append(c);
+            } else {
+                safe.append("&#");
+                safe.append((int) c);
+                safe.append(";");
+            }
+        }
+
+        return safe.toString();
+    }
+
+    private static boolean isSafe(char c) {
+        return //
+        c > 47 && c < 58 || // alphanum
+                c > 64 && c < 91 || // A-Z
+                c > 96 && c < 123 // a-z
+        ;
+    }
+
+    private static final Logger getLogger() {
+        return Logger.getLogger(VaadinServlet.class.getName());
+    }
+}
index 92090da14a8205d266415639700318a882d892e7..96cab99e4057a41aeaf28b8336f1d211d008e27a 100644 (file)
@@ -335,9 +335,8 @@ public class WebBrowser {
     }
 
     /**
-     * For internal use by AbstractApplicationServlet/AbstractApplicationPortlet
-     * only. Updates all properties in the class according to the given
-     * information.
+     * For internal use by VaadinServlet/VaadinPortlet only. Updates all
+     * properties in the class according to the given information.
      * 
      * @param sw
      *            Screen width
@@ -406,9 +405,8 @@ public class WebBrowser {
     }
 
     /**
-     * For internal use by AbstractApplicationServlet/AbstractApplicationPortlet
-     * only. Updates all properties in the class according to the given
-     * information.
+     * For internal use by VaadinServlet/VaadinPortlet only. Updates all
+     * properties in the class according to the given information.
      * 
      * @param request
      *            the wrapped request to read the information from
index d1ccaacde3797cc063c8253d7d108dbed1c543ed..0f914d3947e2c5a4bd7f1927f58554cd992cbdfb 100644 (file)
@@ -33,7 +33,6 @@ import com.vaadin.event.Action.Handler;
 import com.vaadin.event.ActionManager;
 import com.vaadin.event.MouseEvents.ClickEvent;
 import com.vaadin.event.MouseEvents.ClickListener;
-import com.vaadin.server.AbstractApplicationServlet;
 import com.vaadin.server.LegacyComponent;
 import com.vaadin.server.Page;
 import com.vaadin.server.Page.BrowserWindowResizeEvent;
@@ -41,6 +40,7 @@ import com.vaadin.server.Page.BrowserWindowResizeListener;
 import com.vaadin.server.PaintException;
 import com.vaadin.server.PaintTarget;
 import com.vaadin.server.Resource;
+import com.vaadin.server.VaadinServlet;
 import com.vaadin.server.WrappedRequest;
 import com.vaadin.server.WrappedRequest.BrowserDetails;
 import com.vaadin.shared.EventId;
@@ -63,7 +63,7 @@ import com.vaadin.tools.ReflectTools;
  * </p>
  * <p>
  * When a new UI instance is needed, typically because the user opens a URL in a
- * browser window which points to {@link AbstractApplicationServlet},
+ * browser window which points to {@link VaadinServlet},
  * {@link Application#getUIForRequest(WrappedRequest)} is invoked to get a UI.
  * That method does by default create a UI according to the
  * {@value Application#UI_PARAMETER} parameter from web.xml.
index df16e98bba448057466c93c19f8cf0cd41c12627..3ae41610fa7039dddd0694f41e99cd1ad4a777d2 100644 (file)
@@ -15,14 +15,11 @@ import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 
-import com.vaadin.server.AbstractApplicationServlet;
-import com.vaadin.server.ApplicationServlet;
-
 import junit.framework.TestCase;
 
 public class TestAbstractApplicationServletStaticFilesLocation extends TestCase {
 
-    ApplicationServlet servlet;
+    VaadinServlet servlet;
 
     private Method getStaticFilesLocationMethod;
 
@@ -30,18 +27,16 @@ public class TestAbstractApplicationServletStaticFilesLocation extends TestCase
     protected void setUp() throws Exception {
         super.setUp();
 
-        servlet = new ApplicationServlet();
+        servlet = new VaadinServlet();
 
         // Workaround to avoid calling init and creating servlet config
-        Field f = AbstractApplicationServlet.class
-                .getDeclaredField("applicationProperties");
+        Field f = VaadinServlet.class.getDeclaredField("applicationProperties");
         f.setAccessible(true);
         f.set(servlet, new Properties());
 
-        getStaticFilesLocationMethod = AbstractApplicationServlet.class
-                .getDeclaredMethod(
-                        "getStaticFilesLocation",
-                        new Class[] { javax.servlet.http.HttpServletRequest.class });
+        getStaticFilesLocationMethod = VaadinServlet.class.getDeclaredMethod(
+                "getStaticFilesLocation",
+                new Class[] { javax.servlet.http.HttpServletRequest.class });
         getStaticFilesLocationMethod.setAccessible(true);
 
     }
index e2fe5df4c75da0ea6b763d80990f619a3f3974c4..bbe6e061fb6ffd92e2236d50a96dc8ad20045f7f 100644 (file)
@@ -30,7 +30,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import com.vaadin.Application;
-import com.vaadin.server.AbstractApplicationServlet;
+import com.vaadin.server.VaadinServlet;
 import com.vaadin.server.AbstractUIProvider;
 import com.vaadin.server.WrappedHttpServletRequest;
 import com.vaadin.server.WrappedRequest;
@@ -38,7 +38,7 @@ import com.vaadin.tests.components.TestBase;
 import com.vaadin.ui.UI;
 
 @SuppressWarnings("serial")
-public class ApplicationRunnerServlet extends AbstractApplicationServlet {
+public class ApplicationRunnerServlet extends VaadinServlet {
 
     /**
      * The name of the application class currently used. Only valid within one