<commit_before>/*
 * 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.File;
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.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.DefaultDeploymentConfiguration;
import com.vaadin.sass.ScssStylesheet;
import com.vaadin.server.AbstractCommunicationManager.Callback;
import com.vaadin.server.ServletPortletHelper.ApplicationClassException;
import com.vaadin.server.VaadinSession.SessionStartEvent;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.ui.UI;
import com.vaadin.util.CurrentInstance;

@SuppressWarnings("serial")
public class VaadinServlet extends HttpServlet implements Constants {

    public static class ServletService extends AbstractVaadinService {
        private final VaadinServlet servlet;

        public ServletService(VaadinServlet servlet,
                DeploymentConfiguration deploymentConfiguration) {
            super(deploymentConfiguration);
            this.servlet = servlet;
        }

        protected VaadinServlet getServlet() {
            return servlet;
        }

        @Override
        public String getStaticFileLocation(WrappedRequest request) {
            HttpServletRequest servletRequest = WrappedHttpServletRequest
                    .cast(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 = servletRequest.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;
            }
        }

        @Override
        public String getConfiguredWidgetset(WrappedRequest request) {
            return getDeploymentConfiguration().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 getServlet().getServletContext().getMimeType(resourceName);
        }

        @Override
        public SystemMessages getSystemMessages() {
            return ServletPortletHelper.DEFAULT_SYSTEM_MESSAGES;
        }

        @Override
        public File getBaseDirectory() {
            final String realPath = VaadinServlet.getResourcePath(
                    servlet.getServletContext(), "/");
            if (realPath == null) {
                return null;
            }
            return new File(realPath);
        }
    }

    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 ServletService servletService;

    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 initParameters = 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();
            initParameters.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();
            initParameters.setProperty(name,
                    servletConfig.getInitParameter(name));
        }

        DeploymentConfiguration deploymentConfiguration = createDeploymentConfiguration(initParameters);
        servletService = createServletService(deploymentConfiguration);

        addonContext = new AddonContext(servletService);
        addonContext.init();
    }

    protected DeploymentConfiguration createDeploymentConfiguration(
            Properties initParameters) {
        return new DefaultDeploymentConfiguration(getClass(), initParameters);
    }

    protected ServletService createServletService(
            DeploymentConfiguration deploymentConfiguration) {
        return new ServletService(this, deploymentConfiguration);
    }

    @Override
    public void destroy() {
        super.destroy();

        addonContext.destroy();
    }

    /**
     * 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();

        CurrentInstance.set(WrappedResponse.class, response);
        CurrentInstance.set(WrappedRequest.class, request);

        AbstractApplicationServletWrapper servletWrapper = new AbstractApplicationServletWrapper(
                this);

        RequestType requestType = getRequestType(request);
        if (!ensureCookiesEnabled(requestType, request, response)) {
            return;
        }

        if (requestType == RequestType.STATIC_FILE) {
            serveStaticResources(request, response);
            return;
        }

        VaadinSession application = null;
        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;
            }
            request.setAttribute(VaadinSession.class.getName(), application);
            VaadinSession.setCurrent(application);

            /*
             * Get or create a WebApplicationContext and an ApplicationManager
             * for the session
             */
            VaadinServletSession webApplicationContext = (VaadinServletSession) application;
            CommunicationManager applicationManager = (CommunicationManager) webApplicationContext
                    .getApplicationManager();

            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);

            applicationRunning = 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();
            }

            CurrentInstance.clearAll();

            if (application != null) {
                requestTimer.stop(application);
            }
        }
    }

    private WrappedHttpServletResponse createWrappedResponse(
            HttpServletResponse response) {
        WrappedHttpServletResponse wrappedResponse = new WrappedHttpServletResponse(
                response, getVaadinService());
        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, getVaadinService());
    }

    /**
     * Gets a the vaadin service for this servlet.
     * 
     * @return the vaadin service
     */
    protected ServletService getVaadinService() {
        return servletService;
    }

    /**
     * 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
                SystemMessages systemMessages = getVaadinService()
                        .getSystemMessages();
                criticalNotification(request, response,
                        systemMessages.getCookiesDisabledCaption(),
                        systemMessages.getCookiesDisabledMessage(), null,
                        systemMessages.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.
     * 
     * @deprecated might be refactored or removed before 7.0.0
     */
    @Deprecated
    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 VaadinSession findApplicationInstance(
            WrappedHttpServletRequest request, RequestType requestType)
            throws MalformedURLException, ServletException,
            SessionExpiredException {

        boolean requestCanCreateApplication = requestCanCreateApplication(
                request, requestType);

        /* Find an existing application for this request. */
        VaadinSession 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 createAndRegisterApplication(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 createAndRegisterApplication(request);
        } else {
            /*
             * The application was not found and a new one should not be
             * created. Assume the session has expired.
             */
            throw new SessionExpiredException();
        }

    }

    private VaadinSession createAndRegisterApplication(
            WrappedHttpServletRequest request) throws ServletException,
            MalformedURLException {
        VaadinServletSession session = createVaadinSession(request);

        session.storeInSession(new WrappedHttpSession(request.getSession()));

        final URL applicationUrl = getApplicationUrl(request);

        // Initial locale comes from the request
        Locale locale = request.getLocale();
        session.setLocale(locale);
        session.start(new SessionStartEvent(applicationUrl, getVaadinService()
                .getDeploymentConfiguration(),
                createCommunicationManager(session)));

        onVaadinSessionStarted(request, session);

        return session;
    }

    protected void onVaadinSessionStarted(WrappedHttpServletRequest request,
            VaadinServletSession session) throws ServletException {
        addonContext.fireApplicationStarted(session);

        try {
            ServletPortletHelper.checkUiProviders(session);
        } catch (ApplicationClassException e) {
            throw new ServletException(e);
        }
    }

    /**
     * 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
     * 
     * @deprecated might be refactored or removed before 7.0.0
     */
    @Deprecated
    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.<commit_msg>Allow browser details request to initialize a new session (#9652)<commit_after>/*
 * 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.File;
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.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.DefaultDeploymentConfiguration;
import com.vaadin.sass.ScssStylesheet;
import com.vaadin.server.AbstractCommunicationManager.Callback;
import com.vaadin.server.ServletPortletHelper.ApplicationClassException;
import com.vaadin.server.VaadinSession.SessionStartEvent;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.ui.UI;
import com.vaadin.util.CurrentInstance;

@SuppressWarnings("serial")
public class VaadinServlet extends HttpServlet implements Constants {

    public static class ServletService extends AbstractVaadinService {
        private final VaadinServlet servlet;

        public ServletService(VaadinServlet servlet,
                DeploymentConfiguration deploymentConfiguration) {
            super(deploymentConfiguration);
            this.servlet = servlet;
        }

        protected VaadinServlet getServlet() {
            return servlet;
        }

        @Override
        public String getStaticFileLocation(WrappedRequest request) {
            HttpServletRequest servletRequest = WrappedHttpServletRequest
                    .cast(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 = servletRequest.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;
            }
        }

        @Override
        public String getConfiguredWidgetset(WrappedRequest request) {
            return getDeploymentConfiguration().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 getServlet().getServletContext().getMimeType(resourceName);
        }

        @Override
        public SystemMessages getSystemMessages() {
            return ServletPortletHelper.DEFAULT_SYSTEM_MESSAGES;
        }

        @Override
        public File getBaseDirectory() {
            final String realPath = VaadinServlet.getResourcePath(
                    servlet.getServletContext(), "/");
            if (realPath == null) {
                return null;
            }
            return new File(realPath);
        }
    }

    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 ServletService servletService;

    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 initParameters = 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();
            initParameters.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();
            initParameters.setProperty(name,
                    servletConfig.getInitParameter(name));
        }

        DeploymentConfiguration deploymentConfiguration = createDeploymentConfiguration(initParameters);
        servletService = createServletService(deploymentConfiguration);

        addonContext = new AddonContext(servletService);
        addonContext.init();
    }

    protected DeploymentConfiguration createDeploymentConfiguration(
            Properties initParameters) {
        return new DefaultDeploymentConfiguration(getClass(), initParameters);
    }

    protected ServletService createServletService(
            DeploymentConfiguration deploymentConfiguration) {
        return new ServletService(this, deploymentConfiguration);
    }

    @Override
    public void destroy() {
        super.destroy();

        addonContext.destroy();
    }

    /**
     * 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();

        CurrentInstance.set(WrappedResponse.class, response);
        CurrentInstance.set(WrappedRequest.class, request);

        AbstractApplicationServletWrapper servletWrapper = new AbstractApplicationServletWrapper(
                this);

        RequestType requestType = getRequestType(request);
        if (!ensureCookiesEnabled(requestType, request, response)) {
            return;
        }

        if (requestType == RequestType.STATIC_FILE) {
            serveStaticResources(request, response);
            return;
        }

        VaadinSession application = null;
        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;
            }
            request.setAttribute(VaadinSession.class.getName(), application);
            VaadinSession.setCurrent(application);

            /*
             * Get or create a WebApplicationContext and an ApplicationManager
             * for the session
             */
            VaadinServletSession webApplicationContext = (VaadinServletSession) application;
            CommunicationManager applicationManager = (CommunicationManager) webApplicationContext
                    .getApplicationManager();

            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);

            applicationRunning = 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();
            }

            CurrentInstance.clearAll();

            if (application != null) {
                requestTimer.stop(application);
            }
        }
    }

    private WrappedHttpServletResponse createWrappedResponse(
            HttpServletResponse response) {
        WrappedHttpServletResponse wrappedResponse = new WrappedHttpServletResponse(
                response, getVaadinService());
        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, getVaadinService());
    }

    /**
     * Gets a the vaadin service for this servlet.
     * 
     * @return the vaadin service
     */
    protected ServletService getVaadinService() {
        return servletService;
    }

    /**
     * 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
                SystemMessages systemMessages = getVaadinService()
                        .getSystemMessages();
                criticalNotification(request, response,
                        systemMessages.getCookiesDisabledCaption(),
                        systemMessages.getCookiesDisabledMessage(), null,
                        systemMessages.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.
     * 
     * @deprecated might be refactored or removed before 7.0.0
     */
    @Deprecated
    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 VaadinSession findApplicationInstance(
            WrappedHttpServletRequest request, RequestType requestType)
            throws MalformedURLException, ServletException,
            SessionExpiredException {

        boolean requestCanCreateApplication = requestCanCreateApplication(
                request, requestType);

        /* Find an existing application for this request. */
        VaadinSession 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 createAndRegisterApplication(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 createAndRegisterApplication(request);
        } else {
            /*
             * The application was not found and a new one should not be
             * created. Assume the session has expired.
             */
            throw new SessionExpiredException();
        }

    }

    private VaadinSession createAndRegisterApplication(
            WrappedHttpServletRequest request) throws ServletException,
            MalformedURLException {
        VaadinServletSession session = createVaadinSession(request);

        session.storeInSession(new WrappedHttpSession(request.getSession()));

        final URL applicationUrl = getApplicationUrl(request);

        // Initial locale comes from the request
        Locale locale = request.getLocale();
        session.setLocale(locale);
        session.start(new SessionStartEvent(applicationUrl, getVaadinService()
                .getDeploymentConfiguration(),
                createCommunicationManager(session)));

        onVaadinSessionStarted(request, session);

        return session;
    }

    protected void onVaadinSessionStarted(WrappedHttpServletRequest request,
            VaadinServletSession session) throws ServletException {
        addonContext.fireApplicationStarted(session);

        try {
            ServletPortletHelper.checkUiProviders(session);
        } catch (ApplicationClassException e) {
            throw new ServletException(e);
        }
    }

    /**
     * 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
     * 
     * @deprecated might be refactored or removed before 7.0.0
     */
    @Deprecated
    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.BROWSER_DETAILS) {
            // This is the first request if you are embedding by writing the
            // embedding code yourself
            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.
{'fraction_non_alphanumeric': 0.07408764049276088, 'fraction_numerical': 0.0011952728885118852, 'mean_word_length': 1.963436928702011, 'pattern_counts': {'":': 10, '<': 31, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4422', 'n_tokens_mistral': 12057, 'n_tokens_neox': 11495, 'n_words': 3559}