All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.server.VaadinService Maven / Gradle / Ivy

There is a newer version: 8.27.3
Show newest version
/*
 * Vaadin Framework 7
 *
 * Copyright (C) 2000-2024 Vaadin Ltd
 *
 * This program is available under Vaadin Commercial License and Service Terms.
 *
 * See  for the full
 * 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.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;

import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.event.EventRouter;
import com.vaadin.server.VaadinSession.FutureAccess;
import com.vaadin.server.VaadinSession.State;
import com.vaadin.server.communication.AtmospherePushConnection;
import com.vaadin.server.communication.FileUploadHandler;
import com.vaadin.server.communication.HeartbeatHandler;
import com.vaadin.server.communication.PublishedFileHandler;
import com.vaadin.server.communication.SessionRequestHandler;
import com.vaadin.server.communication.UidlRequestHandler;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.JsonConstants;
import com.vaadin.shared.ui.ui.UIConstants;
import com.vaadin.ui.UI;
import com.vaadin.util.CurrentInstance;
import com.vaadin.util.ReflectTools;

import elemental.json.Json;
import elemental.json.JsonException;
import elemental.json.JsonObject;
import elemental.json.impl.JsonUtil;

/**
 * Provide deployment specific settings that are required outside terminal
 * specific code.
 *
 * @author Vaadin Ltd.
 *
 * @since 7.0
 */
public abstract class VaadinService implements Serializable {
    /**
     * Attribute name for telling
     * {@link VaadinSession#valueUnbound(javax.servlet.http.HttpSessionBindingEvent)}
     * that it should not close a {@link VaadinSession} even though it gets
     * unbound. If a {@code VaadinSession} has an attribute with this name and
     * the attribute value is {@link Boolean#TRUE}, that session will not be
     * closed when it is unbound from the underlying session.
     */
    // Use the old name.reinitializing value for backwards compatibility
    static final String PRESERVE_UNBOUND_SESSION_ATTRIBUTE = VaadinService.class
            .getName() + ".reinitializing";

    /**
     * @deprecated As of 7.1.1, use {@link #PRESERVE_UNBOUND_SESSION_ATTRIBUTE}
     *             instead
     */
    @Deprecated
    static final String REINITIALIZING_SESSION_MARKER = PRESERVE_UNBOUND_SESSION_ATTRIBUTE;

    private static final Method SESSION_INIT_METHOD = ReflectTools.findMethod(
            SessionInitListener.class, "sessionInit", SessionInitEvent.class);

    private static final Method SESSION_DESTROY_METHOD = ReflectTools
            .findMethod(SessionDestroyListener.class, "sessionDestroy",
                    SessionDestroyEvent.class);

    private static final Method SERVICE_DESTROY_METHOD = ReflectTools
            .findMethod(ServiceDestroyListener.class, "serviceDestroy",
                    ServiceDestroyEvent.class);

    /**
     * @deprecated As of 7.0. Only supported for {@link LegacyApplication}.
     */
    @Deprecated
    public static final String URL_PARAMETER_RESTART_APPLICATION = "restartApplication";

    /**
     * @deprecated As of 7.0. Only supported for {@link LegacyApplication}.
     */
    @Deprecated
    public static final String URL_PARAMETER_CLOSE_APPLICATION = "closeApplication";

    private static final String REQUEST_START_TIME_ATTRIBUTE = "requestStartTime";

    private final DeploymentConfiguration deploymentConfiguration;

    private final EventRouter eventRouter = new EventRouter();

    private SystemMessagesProvider systemMessagesProvider = DefaultSystemMessagesProvider
            .get();

    private ClassLoader classLoader;

    private Iterable requestHandlers;

    private Boolean atmosphereAvailable = null;

    /**
     * Keeps track of whether a warning about missing push support has already
     * been logged. This is used to avoid spamming the log with the same message
     * every time a new UI is bootstrapped.
     */
    private boolean pushWarningEmitted = false;

    /**
     * Has {@link #init()} been run?
     */
    private boolean initialized = false;

    /**
     * Creates a new vaadin service based on a deployment configuration
     *
     * @param deploymentConfiguration
     *            the deployment configuration for the service
     */
    public VaadinService(DeploymentConfiguration deploymentConfiguration) {
        this.deploymentConfiguration = deploymentConfiguration;

        final String classLoaderName = getDeploymentConfiguration()
                .getClassLoaderName();
        if (classLoaderName != null) {
            try {
                final Class classLoaderClass = getClass().getClassLoader()
                        .loadClass(classLoaderName);
                final Constructor c = classLoaderClass
                        .getConstructor(new Class[] { ClassLoader.class });
                setClassLoader((ClassLoader) c.newInstance(
                        new Object[] { getClass().getClassLoader() }));
            } catch (final Exception e) {
                throw new RuntimeException(
                        "Could not find specified class loader: "
                                + classLoaderName,
                        e);
            }
        }

        if (getClassLoader() == null) {
            setDefaultClassLoader();
        }
    }

    /**
     * Initializes this service. The service should be initialized before it is
     * used.
     *
     * @since 7.1
     * @throws ServiceException
     *             if a problem occurs when creating the service
     */
    public void init() throws ServiceException {
        List handlers = createRequestHandlers();
        Collections.reverse(handlers);
        requestHandlers = Collections.unmodifiableCollection(handlers);

        initialized = true;
    }

    /**
     * Called during initialization to add the request handlers for the service.
     * Note that the returned list will be reversed so the last handler will be
     * called first. This enables overriding this method and using add on the
     * returned list to add a custom request handler which overrides any
     * predefined handler.
     *
     * @return The list of request handlers used by this service.
     * @throws ServiceException
     *             if a problem occurs when creating the request handlers
     */
    protected List createRequestHandlers()
            throws ServiceException {
        ArrayList handlers = new ArrayList();
        handlers.add(new SessionRequestHandler());
        handlers.add(new PublishedFileHandler());
        handlers.add(new HeartbeatHandler());
        handlers.add(new FileUploadHandler());
        handlers.add(new UidlRequestHandler());
        handlers.add(new UnsupportedBrowserHandler());
        handlers.add(new ConnectorResourceHandler());

        return handlers;
    }

    /**
     * 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 e.g. the servlet mapping.
     *
     * @param request
     *            the request for which the location should be determined
     *
     * @return The location of static resources (should contain the VAADIN
     *         directory). Never ends with a slash (/).
     */
    public abstract String getStaticFileLocation(VaadinRequest request);

    /**
     * Gets the widgetset that is configured for this deployment, e.g. from a
     * parameter in web.xml.
     *
     * @param request
     *            the request for which a widgetset is required
     * @return the name of the widgetset
     */
    public abstract String getConfiguredWidgetset(VaadinRequest request);

    /**
     * Gets the theme that is configured for this deployment, e.g. from a portal
     * parameter or just some sensible default value.
     *
     * @param request
     *            the request for which a theme is required
     * @return the name of the theme
     */
    public abstract String getConfiguredTheme(VaadinRequest request);

    /**
     * Checks whether the UI will be rendered on its own in the browser or
     * whether it will be included into some other context. A standalone UI may
     * do things that might interfere with other parts of a page, e.g. changing
     * the page title and requesting focus upon loading.
     *
     * @param request
     *            the request for which the UI is loaded
     * @return a boolean indicating whether the UI should be standalone
     */
    public abstract boolean isStandalone(VaadinRequest request);

    /**
     * Gets the class loader to use for loading classes loaded by name, e.g.
     * custom UI classes. This is by default the class loader that was used to
     * load the Servlet or Portlet class to which this service belongs.
     *
     * @return the class loader to use, or null
     *
     * @see #setClassLoader(ClassLoader)
     */
    public ClassLoader getClassLoader() {
        return classLoader;
    }

    /**
     * Sets the class loader to use for loading classes loaded by name, e.g.
     * custom UI classes. Invokers of this method should be careful to not break
     * any existing class loader hierarchy, e.g. by ensuring that a class loader
     * set for this service delegates to the previously set class loader if the
     * class is not found.
     *
     * @param classLoader
     *            the new class loader to set, not null.
     *
     * @see #getClassLoader()
     */
    public void setClassLoader(ClassLoader classLoader) {
        if (classLoader == null) {
            throw new IllegalArgumentException(
                    "Can not set class loader to null");
        }
        this.classLoader = classLoader;
    }

    /**
     * Returns the MIME type of the specified file, or null if the MIME type is
     * not known. The MIME type is determined by the configuration of the
     * container, and may be specified in a deployment descriptor. Common MIME
     * types are "text/html" and "image/gif".
     *
     * @param resourceName
     *            a String specifying the name of a file
     * @return a String specifying the file's MIME type
     *
     * @see ServletContext#getMimeType(String)
     */
    public abstract String getMimeType(String resourceName);

    /**
     * Gets the deployment configuration.
     *
     * @return the deployment configuration
     */
    public DeploymentConfiguration getDeploymentConfiguration() {
        return deploymentConfiguration;
    }

    /**
     * Sets the system messages provider to use for getting system messages to
     * display to users of this service.
     *
     * @see #getSystemMessagesProvider()
     *
     * @param systemMessagesProvider
     *            the system messages provider; null is not
     *            allowed.
     */
    public void setSystemMessagesProvider(
            SystemMessagesProvider systemMessagesProvider) {
        if (systemMessagesProvider == null) {
            throw new IllegalArgumentException(
                    "SystemMessagesProvider can not be null.");
        }
        this.systemMessagesProvider = systemMessagesProvider;
    }

    /**
     * Gets the system messages provider currently defined for this service.
     * 

* By default, the {@link DefaultSystemMessagesProvider} which always * provides the built-in default {@link SystemMessages} is used. *

* * @see #setSystemMessagesProvider(SystemMessagesProvider) * @see SystemMessagesProvider * @see SystemMessages * * @return the system messages provider; not null */ public SystemMessagesProvider getSystemMessagesProvider() { return systemMessagesProvider; } /** * Gets the system message to use for a specific locale. This method may * also be implemented to use information from current instances of various * objects, which means that this method might return different values for * the same locale under different circumstances. * * @param locale * the desired locale for the system messages * @param request * @return the system messages to use */ public SystemMessages getSystemMessages(Locale locale, VaadinRequest request) { SystemMessagesInfo systemMessagesInfo = new SystemMessagesInfo(); systemMessagesInfo.setLocale(locale); systemMessagesInfo.setService(this); systemMessagesInfo.setRequest(request); return getSystemMessagesProvider() .getSystemMessages(systemMessagesInfo); } /** * Returns the context base directory. * * Typically an application is deployed in a such way that is has an * application directory. For web applications this directory is the root * directory of the web applications. In some cases applications might not * have an application directory (for example web applications running * inside a war). * * @return The application base directory or null if the application has no * base directory. */ public abstract File getBaseDirectory(); /** * Adds a listener that gets notified when a new Vaadin service session is * initialized for this service. *

* Because of the way different service instances share the same session, * the listener is not necessarily notified immediately when the session is * created but only when the first request for that session is handled by * this service. * * @see #removeSessionInitListener(SessionInitListener) * @see SessionInitListener * * @param listener * the Vaadin service session initialization listener */ public void addSessionInitListener(SessionInitListener listener) { eventRouter.addListener(SessionInitEvent.class, listener, SESSION_INIT_METHOD); } /** * Removes a Vaadin service session initialization listener from this * service. * * @see #addSessionInitListener(SessionInitListener) * * @param listener * the Vaadin service session initialization listener to remove. */ public void removeSessionInitListener(SessionInitListener listener) { eventRouter.removeListener(SessionInitEvent.class, listener, SESSION_INIT_METHOD); } /** * Adds a listener that gets notified when a Vaadin service session that has * been initialized for this service is destroyed. *

* The session being destroyed is locked and its UIs have been removed when * the listeners are called. * * @see #addSessionInitListener(SessionInitListener) * * @param listener * the vaadin service session destroy listener */ public void addSessionDestroyListener(SessionDestroyListener listener) { eventRouter.addListener(SessionDestroyEvent.class, listener, SESSION_DESTROY_METHOD); } /** * Handles destruction of the given session. Internally ensures proper * locking is done. * * @param vaadinSession * The session to destroy */ public void fireSessionDestroy(VaadinSession vaadinSession) { final VaadinSession session = vaadinSession; session.access(new Runnable() { @Override public void run() { if (session.getState() == State.CLOSED) { return; } if (session.getState() == State.OPEN) { closeSession(session); } ArrayList uis = new ArrayList(session.getUIs()); for (final UI ui : uis) { ui.accessSynchronously(new Runnable() { @Override public void run() { /* * close() called here for consistency so that it is * always called before a UI is removed. * UI.isClosing() is thus always true in UI.detach() * and associated detach listeners. */ if (!ui.isClosing()) { ui.close(); } session.removeUI(ui); } }); } // for now, use the session error handler; in the future, could // have an API for using some other handler for session init and // destroy listeners eventRouter.fireEvent( new SessionDestroyEvent(VaadinService.this, session), session.getErrorHandler()); session.setState(State.CLOSED); } }); } /** * Removes a Vaadin service session destroy listener from this service. * * @see #addSessionDestroyListener(SessionDestroyListener) * * @param listener * the vaadin service session destroy listener */ public void removeSessionDestroyListener(SessionDestroyListener listener) { eventRouter.removeListener(SessionDestroyEvent.class, listener, SESSION_DESTROY_METHOD); } /** * Attempts to find a Vaadin service session associated with this request. *

* Handles locking of the session internally to avoid creation of duplicate * sessions by two threads simultaneously. *

* * @param request * the request to get a vaadin service session for. * * @see VaadinSession * * @return the vaadin service session for the request, or null * if no session is found and this is a request for which a new * session shouldn't be created. */ public VaadinSession findVaadinSession(VaadinRequest request) throws ServiceException, SessionExpiredException { VaadinSession vaadinSession = findOrCreateVaadinSession(request); if (vaadinSession == null) { return null; } VaadinSession.setCurrent(vaadinSession); request.setAttribute(VaadinSession.class.getName(), vaadinSession); return vaadinSession; } /** * Associates the given lock with this service and the given wrapped * session. This method should not be called more than once when the lock is * initialized for the session. * * @see #getSessionLock(WrappedSession) * @param wrappedSession * The wrapped session the lock is associated with * @param lock * The lock object */ protected void setSessionLock(WrappedSession wrappedSession, Lock lock) { if (wrappedSession == null) { throw new IllegalArgumentException( "Can't set a lock for a null session"); } Object currentSessionLock = wrappedSession .getAttribute(getLockAttributeName()); assert (currentSessionLock == null || currentSessionLock == lock) : "Changing the lock for a session is not allowed"; wrappedSession.setAttribute(getLockAttributeName(), lock); } /** * Returns the name used to store the lock in the HTTP session. * * @return The attribute name for the lock */ protected String getLockAttributeName() { return getServiceName() + ".lock"; } /** * Gets the lock instance used to lock the VaadinSession associated with the * given wrapped session. *

* This method uses the wrapped session instead of VaadinSession to be able * to lock even before the VaadinSession has been initialized. *

* * @param wrappedSession * The wrapped session * @return A lock instance used for locking access to the wrapped session */ protected Lock getSessionLock(WrappedSession wrappedSession) { Object lock = wrappedSession.getAttribute(getLockAttributeName()); if (lock instanceof ReentrantLock) { return (ReentrantLock) lock; } if (lock == null) { return null; } throw new RuntimeException( "Something else than a ReentrantLock was stored in the " + getLockAttributeName() + " in the session"); } /** * Locks the given session for this service instance. Typically you want to * call {@link VaadinSession#lock()} instead of this method. *

* Note: The method and its signature has been changed to return lock * instance in Vaadin 7.7.27. If you have overriden this method, you need * to update your implementation. *

* Note: Overriding this method is not recommended, for custom lock storage * strategy override {@link #getSessionLock(WrappedSession)} and * {@link #setSessionLock(WrappedSession,Lock)} instead. * * @param wrappedSession * The session to lock * @return Lock instance * * @throws IllegalStateException * if the session is invalidated before it can be locked */ protected Lock lockSession(WrappedSession wrappedSession) { Lock lock = getSessionLock(wrappedSession); if (lock == null) { /* * No lock found in the session attribute. Ensure only one lock is * created and used by everybody by doing double checked locking. * Assumes there is a memory barrier for the attribute (i.e. that * the CPU flushes its caches and reads the value directly from main * memory). */ synchronized (VaadinService.class) { lock = getSessionLock(wrappedSession); if (lock == null) { lock = new ReentrantLock(); setSessionLock(wrappedSession, lock); } } } lock.lock(); try { // Someone might have invalidated the session between fetching the // lock and acquiring it. Guard for this by calling a method that's // specified to throw IllegalStateException if invalidated // (#12282) wrappedSession.getAttribute(getLockAttributeName()); } catch (IllegalStateException e) { lock.unlock(); throw e; } return lock; } /** * Releases the lock for the given session for this service instance. * Typically you want to call {@link VaadinSession#unlock()} instead of this * method. *

* Note: The method and its signature has been changed to get lock instance * as parameter in Vaadin 7.7.27. If you have overriden this method, you need * to update your implementation. *

* Note: Overriding this method is not recommended, for custom lock storage * strategy override {@link #getSessionLock(WrappedSession)} and * {@link #setSessionLock(WrappedSession,Lock)} instead. * * @param wrappedSession * The session to unlock, used only with assert * @param lock * Lock instance to unlock */ protected void unlockSession(WrappedSession wrappedSession, Lock lock) { assert ((ReentrantLock) lock).isHeldByCurrentThread() : "Trying to unlock the session but it has not been locked by this thread"; lock.unlock(); } private VaadinSession findOrCreateVaadinSession(VaadinRequest request) throws SessionExpiredException, ServiceException { boolean requestCanCreateSession = requestCanCreateSession(request); WrappedSession wrappedSession = getWrappedSession(request, requestCanCreateSession); final Lock lock; try { lock = lockSession(wrappedSession); } catch (IllegalStateException e) { throw new SessionExpiredException(); } try { return doFindOrCreateVaadinSession(request, requestCanCreateSession); } finally { unlockSession(wrappedSession, lock); } } /** * Finds or creates a Vaadin session. Assumes necessary synchronization has * been done by the caller to ensure this is not called simultaneously by * several threads. * * @param request * @param requestCanCreateSession * @return * @throws SessionExpiredException * @throws ServiceException */ private VaadinSession doFindOrCreateVaadinSession(VaadinRequest request, boolean requestCanCreateSession) throws SessionExpiredException, ServiceException { assert ((ReentrantLock) getSessionLock(request.getWrappedSession())) .isHeldByCurrentThread() : "Session has not been locked by this thread"; /* Find an existing session for this request. */ VaadinSession session = getExistingSession(request, requestCanCreateSession); if (session != null) { /* * There is an existing session. We can use this as long as the user * not specifically requested to close or restart it. */ final boolean restartApplication = hasParameter(request, URL_PARAMETER_RESTART_APPLICATION) && !hasParameter(request, BootstrapHandler.IGNORE_RESTART_PARAM); final boolean closeApplication = hasParameter(request, URL_PARAMETER_CLOSE_APPLICATION); if (closeApplication) { closeSession(session, request.getWrappedSession(false)); return null; } else if (restartApplication) { closeSession(session, request.getWrappedSession(false)); return createAndRegisterSession(request); } else { return session; } } // No existing session was found if (requestCanCreateSession) { /* * If the request is such that it should create a new session if one * as not found, we do that. */ return createAndRegisterSession(request); } else { /* * The session was not found and a new one should not be created. * Assume the session has expired. */ throw new SessionExpiredException(); } } private static boolean hasParameter(VaadinRequest request, String parameterName) { return request.getParameter(parameterName) != null; } /** * Creates and registers a new VaadinSession for this service. Assumes * proper locking has been taken care of by the caller. * * * @param request * The request which triggered session creation. * @return A new VaadinSession instance * @throws ServiceException */ private VaadinSession createAndRegisterSession(VaadinRequest request) throws ServiceException { assert ((ReentrantLock) getSessionLock(request.getWrappedSession())) .isHeldByCurrentThread() : "Session has not been locked by this thread"; VaadinSession session = createVaadinSession(request); VaadinSession.setCurrent(session); storeSession(session, request.getWrappedSession()); // Initial WebBrowser data comes from the request session.getBrowser().updateRequestDetails(request); // Initial locale comes from the request Locale locale = request.getLocale(); session.setLocale(locale); session.setConfiguration(getDeploymentConfiguration()); session.setCommunicationManager( new LegacyCommunicationManager(session)); ServletPortletHelper.initDefaultUIProvider(session, this); onVaadinSessionStarted(request, session); return session; } /** * Get the base URL that should be used for sending requests back to this * service. *

* This is only used to support legacy cases. * * @param request * @return * @throws MalformedURLException * * @deprecated As of 7.0. Only used to support {@link LegacyApplication}. */ @Deprecated protected URL getApplicationUrl(VaadinRequest request) throws MalformedURLException { return null; } /** * Creates a new Vaadin session for this service and request * * @param request * The request for which to create a VaadinSession * @return A new VaadinSession * @throws ServiceException * */ protected VaadinSession createVaadinSession(VaadinRequest request) throws ServiceException { return new VaadinSession(this); } private void onVaadinSessionStarted(VaadinRequest request, VaadinSession session) throws ServiceException { // for now, use the session error handler; in the future, could have an // API for using some other handler for session init and destroy // listeners eventRouter.fireEvent(new SessionInitEvent(this, session, request), session.getErrorHandler()); ServletPortletHelper.checkUiProviders(session, this); } private void closeSession(VaadinSession vaadinSession, WrappedSession session) { if (vaadinSession == null) { return; } if (session != null) { removeSession(session); } } protected VaadinSession getExistingSession(VaadinRequest request, boolean allowSessionCreation) throws SessionExpiredException { final WrappedSession session = getWrappedSession(request, allowSessionCreation); VaadinSession vaadinSession = loadSession(session); if (vaadinSession == null) { return null; } return vaadinSession; } /** * Retrieves the wrapped session for the request. * * @param request * The request for which to retrieve a session * @param requestCanCreateSession * true to create a new session if one currently does not exist * @return The retrieved (or created) wrapped session * @throws SessionExpiredException * If the request is not associated to a session and new session * creation is not allowed */ private WrappedSession getWrappedSession(VaadinRequest request, boolean requestCanCreateSession) throws SessionExpiredException { final WrappedSession session = request .getWrappedSession(requestCanCreateSession); if (session == null) { throw new SessionExpiredException(); } return session; } /** * Checks whether it's valid to create a new service session as a result of * the given request. * * @param request * the request * @return true if it's valid to create a new service session * for the request; else false */ protected abstract boolean requestCanCreateSession(VaadinRequest request); /** * Gets the currently used Vaadin service. The current service is * automatically defined when processing requests related to the service and * in threads started at a point when the current service is defined (see * {@link InheritableThreadLocal}). In other cases, (e.g. from background * threads started in some other way), the current service is not * automatically defined. * * @return the current Vaadin service instance if available, otherwise * null * * @see #setCurrentInstances(VaadinRequest, VaadinResponse) */ public static VaadinService getCurrent() { return CurrentInstance.get(VaadinService.class); } /** * Sets the this Vaadin service as the current service and also sets the * current Vaadin request and Vaadin response. This method is used by the * framework to set the current instances when a request related to the * service is processed and they are cleared when the request has been * processed. *

* The application developer can also use this method to define the current * instances outside the normal request handling, e.g. when initiating * custom background threads. *

* * @param request * the Vaadin request to set as the current request, or * null if no request should be set. * @param response * the Vaadin response to set as the current response, or * null if no response should be set. * * @see #getCurrent() * @see #getCurrentRequest() * @see #getCurrentResponse() */ public void setCurrentInstances(VaadinRequest request, VaadinResponse response) { setCurrent(this); CurrentInstance.set(VaadinRequest.class, request); CurrentInstance.set(VaadinResponse.class, response); } /** * Sets the given Vaadin service as the current service. * * @param service */ public static void setCurrent(VaadinService service) { CurrentInstance.setInheritable(VaadinService.class, service); } /** * Gets the currently processed Vaadin request. The current request is * automatically defined when the request is started. The current request * can not be used in e.g. background threads because of the way server * implementations reuse request instances. * * @return the current Vaadin request instance if available, otherwise * null * * @see #setCurrentInstances(VaadinRequest, VaadinResponse) */ public static VaadinRequest getCurrentRequest() { return CurrentInstance.get(VaadinRequest.class); } /** * Gets the currently processed Vaadin response. The current response is * automatically defined when the request is started. The current response * can not be used in e.g. background threads because of the way server * implementations reuse response instances. * * @return the current Vaadin response instance if available, otherwise * null * * @see #setCurrentInstances(VaadinRequest, VaadinResponse) */ public static VaadinResponse getCurrentResponse() { return CurrentInstance.get(VaadinResponse.class); } /** * Gets a unique name for this service. The name should be unique among * different services of the same type but the same for corresponding * instances running in different JVMs in a cluster. This is typically based * on e.g. the configured servlet's or portlet's name. * * @return the unique name of this service instance. */ public abstract String getServiceName(); /** * Finds the {@link UI} that belongs to the provided request. This is * generally only supported for UIDL requests as other request types are not * related to any particular UI or have the UI information encoded in a * non-standard way. The returned UI is also set as the current UI ( * {@link UI#setCurrent(UI)}). * * @param request * the request for which a UI is desired * @return the UI belonging to the request or null if no UI is found * */ public UI findUI(VaadinRequest request) { // getForSession asserts that the lock is held VaadinSession session = loadSession(request.getWrappedSession()); // Get UI id from the request String uiIdString = request.getParameter(UIConstants.UI_ID_PARAMETER); UI ui = null; if (uiIdString != null && session != null) { int uiId = Integer.parseInt(uiIdString); ui = session.getUIById(uiId); } UI.setCurrent(ui); return ui; } /** * Check if the given UI should be associated with the * window.name so that it can be re-used if the browser window * is reloaded. This is typically determined by the UI provider which * typically checks the @{@link PreserveOnRefresh} annotation but UI * providers and ultimately VaadinService implementations may choose to * override the defaults. * * @param provider * the UI provider responsible for the UI * @param event * the UI create event with details about the UI * * @return true if the UI should be preserved on refresh; * false if a new UI instance should be initialized on * refreshed. */ public boolean preserveUIOnRefresh(UIProvider provider, UICreateEvent event) { return provider.isPreservedOnRefresh(event); } /** * Discards the current session and creates a new session with the same * contents. The purpose of this is to introduce a new session key in order * to avoid session fixation attacks. *

* Please note that this method makes certain assumptions about how data is * stored in the underlying session and may thus not be compatible with some * environments. * * @param request * The Vaadin request for which the session should be * reinitialized */ public static void reinitializeSession(VaadinRequest request) { WrappedSession oldSession = request.getWrappedSession(); // Stores all attributes (security key, reference to this context // instance) so they can be added to the new session Set attributeNames = oldSession.getAttributeNames(); HashMap attrs = new HashMap( attributeNames.size() * 2); for (String name : attributeNames) { Object value = oldSession.getAttribute(name); if (value instanceof VaadinSession) { // set flag to avoid cleanup VaadinSession serviceSession = (VaadinSession) value; serviceSession.lock(); try { serviceSession.setAttribute( PRESERVE_UNBOUND_SESSION_ATTRIBUTE, Boolean.TRUE); } finally { serviceSession.unlock(); } } attrs.put(name, value); } // Invalidate the current session oldSession.invalidate(); // Create a new session WrappedSession newSession = request.getWrappedSession(); // Restores all attributes (security key, reference to this context // instance) for (String name : attrs.keySet()) { Object value = attrs.get(name); newSession.setAttribute(name, value); // Ensure VaadinServiceSession knows where it's stored if (value instanceof VaadinSession) { VaadinSession serviceSession = (VaadinSession) value; VaadinService service = serviceSession.getService(); // Use the same lock instance in the new session service.setSessionLock(newSession, serviceSession.getLockInstance()); service.storeSession(serviceSession, newSession); serviceSession.lock(); try { serviceSession.setAttribute( PRESERVE_UNBOUND_SESSION_ATTRIBUTE, null); } finally { serviceSession.unlock(); } } } } /** * * Finds the given theme resource from the web content folder or using the * class loader and returns a stream for it * * @param ui * The ui for which to find the resource * @param themeName * The name of the theme * @param resource * The name of the resource, e.g. "layouts/mycustomlayout.html" * @return A stream for the resource or null if the resource was not found * @throws IOException * if a problem occurred while finding or opening the resource */ public abstract InputStream getThemeResourceAsStream(UI ui, String themeName, String resource) throws IOException; /** * Creates and returns a unique ID for the DIV where the UI is to be * rendered. * * @param session * The service session to which the bootstrapped UI will belong. * @param request * The request for which a div id is needed * @param uiClass * The class of the UI that will be bootstrapped * * @return the id to use in the DOM */ public abstract String getMainDivId(VaadinSession session, VaadinRequest request, Class uiClass); /** * Sets the given session to be closed and all its UI state to be discarded * at the end of the current request, or at the end of the next request if * there is no ongoing one. *

* After the session has been discarded, any UIs that have been left open * will give a Session Expired error and a new session will be created for * serving new UIs. *

* To avoid causing out of sync errors, you should typically redirect to * some other page using {@link Page#setLocation(String)} to make the * browser unload the invalidated UI. * * @see SystemMessages#getSessionExpiredCaption() * * @param session * the session to close */ public void closeSession(VaadinSession session) { session.close(); } /** * Called at the end of a request, after sending the response. Closes * inactive UIs in the given session, removes closed UIs from the session, * and closes the session if it is itself inactive. * * @param session */ void cleanupSession(VaadinSession session) { if (isSessionActive(session)) { closeInactiveUIs(session); removeClosedUIs(session); } else { if (session.getState() == State.OPEN) { closeSession(session); if (session.getSession() != null) { getLogger().log(Level.FINE, "Closing inactive session {0}", session.getSession().getId()); } } if (session.getSession() != null) { /* * If the VaadinSession has no WrappedSession then it has * already been removed from the HttpSession and we do not have * to do it again */ removeSession(session.getSession()); } /* * The session was destroyed during this request and therefore no * destroy event has yet been sent */ fireSessionDestroy(session); } } /** * Removes those UIs from the given session for which {@link UI#isClosing() * isClosing} yields true. * * @param session */ private void removeClosedUIs(final VaadinSession session) { ArrayList uis = new ArrayList(session.getUIs()); for (final UI ui : uis) { if (ui.isClosing()) { ui.accessSynchronously(new Runnable() { @Override public void run() { getLogger().log(Level.FINER, "Removing closed UI {0}", ui.getUIId()); session.removeUI(ui); } }); } } } /** * Closes those UIs in the given session for which {@link #isUIActive} * yields false. * * @since 7.0.0 */ private void closeInactiveUIs(VaadinSession session) { final String sessionId = session.getSession().getId(); for (final UI ui : session.getUIs()) { if (!isUIActive(ui) && !ui.isClosing()) { ui.accessSynchronously(new Runnable() { @Override public void run() { getLogger().log(Level.FINE, "Closing inactive UI #{0} in session {1}", new Object[] { ui.getUIId(), sessionId }); ui.close(); } }); } } } /** * Returns the number of seconds that must pass without a valid heartbeat or * UIDL request being received from a UI before that UI is removed from its * session. This is a lower bound; it might take longer to close an inactive * UI. Returns a negative number if heartbeat is disabled and timeout never * occurs. * * @see DeploymentConfiguration#getHeartbeatInterval() * * @since 7.0.0 * * @return The heartbeat timeout in seconds or a negative number if timeout * never occurs. */ private int getHeartbeatTimeout() { // Permit three missed heartbeats before closing the UI return (int) (getDeploymentConfiguration().getHeartbeatInterval() * (3.1)); } /** * Returns the number of seconds that must pass without a valid UIDL request * being received for the given session before the session is closed, even * though heartbeat requests are received. This is a lower bound; it might * take longer to close an inactive session. *

* Returns a negative number if there is no timeout. In this case heartbeat * requests suffice to keep the session alive, but it will still eventually * expire in the regular manner if there are no requests at all (see * {@link WrappedSession#getMaxInactiveInterval()}). * * @see DeploymentConfiguration#isCloseIdleSessions() * @see #getHeartbeatTimeout() * * @since 7.0.0 * * @return The UIDL request timeout in seconds, or a negative number if * timeout never occurs. */ private int getUidlRequestTimeout(VaadinSession session) { return getDeploymentConfiguration().isCloseIdleSessions() ? session.getSession().getMaxInactiveInterval() : -1; } /** * Returns whether the given UI is active (the client-side actively * communicates with the server) or whether it can be removed from the * session and eventually collected. *

* A UI is active if and only if its {@link UI#isClosing() isClosing} * returns false and {@link #getHeartbeatTimeout() getHeartbeatTimeout} is * negative or has not yet expired. * * @since 7.0.0 * * @param ui * The UI whose status to check * * @return true if the UI is active, false if it could be removed. */ private boolean isUIActive(UI ui) { if (ui.isClosing()) { return false; } // Check for long running tasks Lock lockInstance = ui.getSession().getLockInstance(); if (lockInstance instanceof ReentrantLock) { if (((ReentrantLock) lockInstance).hasQueuedThreads()) { /* * Someone is trying to access the session. Leaving all UIs * alive for now. A possible kill decision will be made at a * later time when the session access has ended. */ return true; } } // Check timeout long now = System.currentTimeMillis(); int timeout = 1000 * getHeartbeatTimeout(); return timeout < 0 || now - ui.getLastHeartbeatTimestamp() < timeout; } /** * Returns whether the given session is active or whether it can be closed. *

* A session is active if and only if its {@link #isClosing} returns false * and {@link #getUidlRequestTimeout(VaadinSession) getUidlRequestTimeout} * is negative or has not yet expired. * * @param session * The session whose status to check * * @return true if the session is active, false if it could be closed. */ private boolean isSessionActive(VaadinSession session) { if (session.getState() != State.OPEN || session.getSession() == null) { return false; } else { long now = System.currentTimeMillis(); int timeout = 1000 * getUidlRequestTimeout(session); return timeout < 0 || now - session.getLastRequestTimestamp() < timeout; } } private static final Logger getLogger() { return Logger.getLogger(VaadinService.class.getName()); } /** * Called before the framework starts handling a request * * @param request * The request * @param response * The response */ public void requestStart(VaadinRequest request, VaadinResponse response) { if (!initialized) { throw new IllegalStateException( "Can not process requests before init() has been called"); } setCurrentInstances(request, response); request.setAttribute(REQUEST_START_TIME_ATTRIBUTE, System.nanoTime()); } /** * Called after the framework has handled a request and the response has * been written. * * @param request * The request object * @param response * The response object * @param session * The session which was used during the request or null if the * request did not use a session */ public void requestEnd(VaadinRequest request, VaadinResponse response, VaadinSession session) { if (session != null) { assert VaadinSession.getCurrent() == session; session.lock(); try { cleanupSession(session); final long duration = (System.nanoTime() - (Long) request .getAttribute(REQUEST_START_TIME_ATTRIBUTE)) / 1000000; session.setLastRequestDuration(duration); } finally { session.unlock(); } } CurrentInstance.clearAll(); } /** * Returns the request handlers that are registered with this service. The * iteration order of the returned collection is the same as the order in * which the request handlers will be invoked when a request is handled. * * @return a collection of request handlers in the order they are invoked * * @see #createRequestHandlers() * * @since 7.1 */ public Iterable getRequestHandlers() { return requestHandlers; } /** * Handles the incoming request and writes the response into the response * object. Uses {@link #getRequestHandlers()} for handling the request. *

* If a session expiration is detected during request handling then each * {@link RequestHandler request handler} has an opportunity to handle the * expiration event if it implements {@link SessionExpiredHandler}. If no * request handler handles session expiration a default expiration message * will be written. *

* * @param request * The incoming request * @param response * The outgoing response * @throws ServiceException * Any exception that occurs during response handling will be * wrapped in a ServiceException */ public void handleRequest(VaadinRequest request, VaadinResponse response) throws ServiceException { requestStart(request, response); VaadinSession vaadinSession = null; try { // Find out the service session this request is related to vaadinSession = findVaadinSession(request); if (vaadinSession == null) { return; } for (RequestHandler handler : getRequestHandlers()) { if (handler.handleRequest(vaadinSession, request, response)) { return; } } // Request not handled by any RequestHandler response.sendError(HttpServletResponse.SC_NOT_FOUND, "Request was not handled by any registered handler."); } catch (final SessionExpiredException e) { handleSessionExpired(request, response); } catch (final Throwable e) { handleExceptionDuringRequest(request, response, vaadinSession, e); } finally { requestEnd(request, response, vaadinSession); } } private void handleExceptionDuringRequest(VaadinRequest request, VaadinResponse response, VaadinSession vaadinSession, Throwable t) throws ServiceException { if (vaadinSession != null) { vaadinSession.lock(); } try { ErrorHandler errorHandler = ErrorEvent .findErrorHandler(vaadinSession); if (errorHandler != null) { errorHandler.error(new ErrorEvent(t)); } // if this was an UIDL request, send UIDL back to the client if (ServletPortletHelper.isUIDLRequest(request)) { SystemMessages ci = getSystemMessages(ServletPortletHelper .findLocale(null, vaadinSession, request), request); try { writeStringResponse(response, JsonConstants.JSON_CONTENT_TYPE, createCriticalNotificationJSON( ci.getInternalErrorCaption(), ci.getInternalErrorMessage(), null, ci.getInternalErrorURL())); } catch (IOException e) { // An exception occured while writing the response. Log // it and continue handling only the original error. getLogger().log(Level.WARNING, "Failed to write critical notification response to the client", e); } } else { // Re-throw other exceptions throw new ServiceException(t); } } finally { if (vaadinSession != null) { vaadinSession.unlock(); } } } /** * Writes the given string as a response using the given content type. * * @param response * The response reference * @param contentType * The content type of the response * @param reponseString * The actual response * @throws IOException * If an error occured while writing the response */ public void writeStringResponse(VaadinResponse response, String contentType, String reponseString) throws IOException { response.setContentType(contentType); final OutputStream out = response.getOutputStream(); final PrintWriter outWriter = new PrintWriter( new BufferedWriter(new OutputStreamWriter(out, "UTF-8"))); outWriter.print(reponseString); outWriter.close(); } /** * Called when the session has expired and the request handling is therefore * aborted. * * @param request * The request * @param response * The response * @throws ServiceException * Thrown if there was any problem handling the expiration of * the session */ protected void handleSessionExpired(VaadinRequest request, VaadinResponse response) throws ServiceException { for (RequestHandler handler : getRequestHandlers()) { if (handler instanceof SessionExpiredHandler) { try { if (((SessionExpiredHandler) handler) .handleSessionExpired(request, response)) { return; } } catch (IOException e) { throw new ServiceException( "Handling of session expired failed", e); } } } // No request handlers handled the request. Write a normal HTTP response try { // If there is a URL, try to redirect there SystemMessages systemMessages = getSystemMessages( ServletPortletHelper.findLocale(null, null, request), request); String sessionExpiredURL = systemMessages.getSessionExpiredURL(); if (sessionExpiredURL != null && (response instanceof VaadinServletResponse)) { ((VaadinServletResponse) response) .sendRedirect(sessionExpiredURL); } else { /* * Session expired as a result of a standard http request and we * have nowhere to redirect. Reloading would likely cause an * endless loop. This can at least happen if refreshing a * resource when the session has expired. */ response.sendError(HttpServletResponse.SC_GONE, "Session expired"); } } catch (IOException e) { throw new ServiceException(e); } } /** * Creates a JSON message which, when sent to client as-is, will cause a * critical error to be shown with the given details. * * @param caption * The caption of the error or null to omit * @param message * The error message or null to omit * @param details * Additional error details or null to omit * @param url * A url to redirect to. If no other details are given then the * user will be immediately redirected to this URL. Otherwise the * message will be shown and the browser will redirect to the * given URL only after the user acknowledges the message. If * null then the browser will refresh the current page. * @return A JSON string to be sent to the client */ public static String createCriticalNotificationJSON(String caption, String message, String details, String url) { String returnString = ""; try { JsonObject appError = Json.createObject(); putValueOrJsonNull(appError, "caption", caption); putValueOrJsonNull(appError, "url", url); putValueOrJsonNull(appError, "message", message); putValueOrJsonNull(appError, "details", details); JsonObject meta = Json.createObject(); meta.put("appError", appError); JsonObject json = Json.createObject(); json.put("changes", Json.createObject()); json.put("resources", Json.createObject()); json.put("locales", Json.createObject()); json.put("meta", meta); json.put(ApplicationConstants.SERVER_SYNC_ID, -1); returnString = JsonUtil.stringify(json); } catch (JsonException e) { getLogger().log(Level.WARNING, "Error creating critical notification JSON message", e); } return "for(;;);[" + returnString + "]"; } private static void putValueOrJsonNull(JsonObject json, String key, String value) { if (value == null) { json.put(key, Json.createNull()); } else { json.put(key, value); } } /** * @deprecated As of 7.0. Will likely change or be removed in a future * version */ @Deprecated public void criticalNotification(VaadinRequest request, VaadinResponse response, String caption, String message, String details, String url) throws IOException { writeStringResponse(response, JsonConstants.JSON_CONTENT_TYPE, createCriticalNotificationJSON(caption, message, details, url)); } /** * Enables push if push support is available and push has not yet been * enabled. * * If push support is not available, a warning explaining the situation will * be logged at least the first time this method is invoked. * * @return true if push can be used; false if push * is not available. */ public boolean ensurePushAvailable() { if (isAtmosphereAvailable()) { return true; } else { if (!pushWarningEmitted) { pushWarningEmitted = true; getLogger().log(Level.WARNING, Constants.ATMOSPHERE_MISSING_ERROR); } return false; } } private boolean checkAtmosphereSupport() { String rawVersion = AtmospherePushConnection.getAtmosphereVersion(); if (rawVersion == null) { return false; } if (!Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION.equals(rawVersion)) { getLogger().log(Level.WARNING, Constants.INVALID_ATMOSPHERE_VERSION_WARNING, new Object[] { Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION, rawVersion }); } return true; } /** * Checks whether Atmosphere is avilable for use * * @since 7.6 * @return true if Atmosphere is available, false otherwise */ protected boolean isAtmosphereAvailable() { if (atmosphereAvailable == null) { atmosphereAvailable = checkAtmosphereSupport(); } return atmosphereAvailable; } /** * Checks that another {@link VaadinSession} instance is not locked. This is * internally used by {@link VaadinSession#accessSynchronously(Runnable)} * and {@link UI#accessSynchronously(Runnable)} to help avoid causing * deadlocks. * * @since 7.1 * @param session * the session that is being locked * @throws IllegalStateException * if the current thread holds the lock for another session */ public static void verifyNoOtherSessionLocked(VaadinSession session) { if (isOtherSessionLocked(session)) { throw new IllegalStateException( "Can't access session while another session is locked by the same thread. This restriction is intended to help avoid deadlocks."); } } /** * Checks whether there might be some {@link VaadinSession} other than the * provided one for which the current thread holds a lock. This method might * not detect all cases where some other session is locked, but it should * cover the most typical situations. * * @since 7.2 * @param session * the session that is expected to be locked * @return true if another session is also locked by the * current thread; false if no such session was found */ public static boolean isOtherSessionLocked(VaadinSession session) { VaadinSession otherSession = VaadinSession.getCurrent(); if (otherSession == null || otherSession == session) { return false; } return otherSession.hasLock(); } /** * Verifies that the given CSRF token (aka double submit cookie) is valid * for the given session. This is used to protect against Cross Site Request * Forgery attacks. *

* This protection is enabled by default, but it might need to be disabled * to allow a certain type of testing. For these cases, the check can be * disabled by setting the init parameter * disable-xsrf-protection to true. * * @see DeploymentConfiguration#isXsrfProtectionEnabled() * * @since 7.1 * * @param session * the vaadin session for which the check should be done * @param requestToken * the CSRF token provided in the request * @return true if the token is valid or if the protection is * disabled; false if protection is enabled and the * token is invalid */ public static boolean isCsrfTokenValid(VaadinSession session, String requestToken) { if (session.getService().getDeploymentConfiguration() .isXsrfProtectionEnabled()) { String sessionToken = session.getCsrfToken(); try { if (sessionToken == null || !MessageDigest.isEqual( sessionToken.getBytes("UTF-8"), requestToken.getBytes("UTF-8"))) { return false; } } catch (UnsupportedEncodingException e) { getLogger().log(Level.WARNING, "Session token was not UTF-8, this should never happen."); return false; } } return true; } /** * Implementation for {@link VaadinSession#access(Runnable)}. This method is * implemented here instead of in {@link VaadinSession} to enable overriding * the implementation without using a custom subclass of VaadinSession. * * @since 7.1 * @see VaadinSession#access(Runnable) * * @param session * the vaadin session to access * @param runnable * the runnable to run with the session locked * * @return a future that can be used to check for task completion and to * cancel the task */ public Future accessSession(VaadinSession session, Runnable runnable) { FutureAccess future = new FutureAccess(session, runnable); session.getPendingAccessQueue().add(future); ensureAccessQueuePurged(session); return future; } /** * Makes sure the pending access queue is purged for the provided session. * If the session is currently locked by the current thread or some other * thread, the queue will be purged when the session is unlocked. If the * lock is not held by any thread, it is acquired and the queue is purged * right away. * * @since 7.1.2 * @param session * the session for which the access queue should be purged */ public void ensureAccessQueuePurged(VaadinSession session) { /* * If no thread is currently holding the lock, pending changes for UIs * with automatic push would not be processed and pushed until the next * time there is a request or someone does an explicit push call. * * To remedy this, we try to get the lock at this point. If the lock is * currently held by another thread, we just back out as the queue will * get purged once it is released. If the lock is held by the current * thread, we just release it knowing that the queue gets purged once * the lock is ultimately released. If the lock is not held by any * thread and we acquire it, we just release it again to purge the queue * right away. */ try { // tryLock() would be shorter, but it does not guarantee fairness if (session.getLockInstance().tryLock(0, TimeUnit.SECONDS)) { // unlock triggers runPendingAccessTasks session.unlock(); } } catch (InterruptedException e) { // Just ignore } } /** * Purges the queue of pending access invocations enqueued with * {@link VaadinSession#access(Runnable)}. *

* This method is automatically run by the framework at appropriate * situations and is not intended to be used by application developers. * * @param session * the vaadin session to purge the queue for * @since 7.1 */ public void runPendingAccessTasks(VaadinSession session) { assert session.hasLock(); if (session.getPendingAccessQueue().isEmpty()) { return; } Map, CurrentInstance> oldInstances = CurrentInstance .getInstances(false); FutureAccess pendingAccess; try { while ((pendingAccess = session.getPendingAccessQueue() .poll()) != null) { if (!pendingAccess.isCancelled()) { CurrentInstance.clearAll(); CurrentInstance.restoreInstances( pendingAccess.getCurrentInstances()); CurrentInstance.setCurrent(session); pendingAccess.run(); try { pendingAccess.get(); } catch (Exception exception) { pendingAccess.handleError(exception); } } } } finally { CurrentInstance.clearAll(); CurrentInstance.restoreInstances(oldInstances); } } /** * Adds a service destroy listener that gets notified when this service is * destroyed. * * @since 7.2 * @param listener * the service destroy listener to add * * @see #destroy() * @see #removeServiceDestroyListener(ServiceDestroyListener) * @see ServiceDestroyListener */ public void addServiceDestroyListener(ServiceDestroyListener listener) { eventRouter.addListener(ServiceDestroyEvent.class, listener, SERVICE_DESTROY_METHOD); } /** * Removes a service destroy listener that was previously added with * {@link #addServiceDestroyListener(ServiceDestroyListener)}. * * @since 7.2 * @param listener * the service destroy listener to remove */ public void removeServiceDestroyListener(ServiceDestroyListener listener) { eventRouter.removeListener(ServiceDestroyEvent.class, listener, SERVICE_DESTROY_METHOD); } /** * Called when the servlet, portlet or similar for this service is being * destroyed. After this method has been called, no more requests will be * handled by this service. * * @see #addServiceDestroyListener(ServiceDestroyListener) * @see Servlet#destroy() * * @since 7.2 */ public void destroy() { eventRouter.fireEvent(new ServiceDestroyEvent(this)); } /** * Tries to acquire default class loader and sets it as a class loader for * this {@link VaadinService} if found. If current security policy disallows * acquiring class loader instance it will log a message and re-throw * {@link SecurityException} * * @throws SecurityException * If current security policy forbids acquiring class loader * * @since 7.3.5 */ protected void setDefaultClassLoader() { try { setClassLoader( VaadinServiceClassLoaderUtil.findDefaultClassLoader()); } catch (SecurityException e) { getLogger().log(Level.SEVERE, Constants.CANNOT_ACQUIRE_CLASSLOADER_SEVERE, e); throw e; } } /** * Called when the VaadinSession should be stored. *

* By default stores the VaadinSession in the underlying HTTP session. * * @since 7.6 * @param session * the VaadinSession to store * @param wrappedSession * the underlying HTTP session */ protected void storeSession(VaadinSession session, WrappedSession wrappedSession) { assert VaadinSession.hasLock(this, wrappedSession); writeToHttpSession(wrappedSession, session); session.refreshTransients(wrappedSession, this); } /** * Performs the actual write of the VaadinSession to the underlying HTTP * session after sanity checks have been performed. *

* Called by {@link #storeSession(VaadinSession, WrappedSession)} * * @since 7.6 * @param wrappedSession * the underlying HTTP session * @param session * the VaadinSession to store */ protected void writeToHttpSession(WrappedSession wrappedSession, VaadinSession session) { wrappedSession.setAttribute(getSessionAttributeName(), session); } /** * Called when the VaadinSession should be loaded from the underlying HTTP * session * * @since 7.6 * @param wrappedSession * the underlying HTTP session * @return the VaadinSession in the HTTP session or null if not found */ protected VaadinSession loadSession(WrappedSession wrappedSession) { assert VaadinSession.hasLock(this, wrappedSession); VaadinSession vaadinSession = readFromHttpSession(wrappedSession); if (vaadinSession == null) { return null; } vaadinSession.refreshTransients(wrappedSession, this); return vaadinSession; } /** * Performs the actual read of the VaadinSession from the underlying HTTP * session after sanity checks have been performed. *

* Called by {@link #loadSession(WrappedSession)}. * * @param wrappedSession * the underlying HTTP session * @since 7.6 * @return the VaadinSession or null if no session was found */ protected VaadinSession readFromHttpSession(WrappedSession wrappedSession) { return (VaadinSession) wrappedSession .getAttribute(getSessionAttributeName()); } /** * Called when the VaadinSession should be removed from the underlying HTTP * session * * @since 7.6 * @param wrappedSession * the underlying HTTP session */ public void removeSession(WrappedSession wrappedSession) { assert VaadinSession.hasLock(this, wrappedSession); removeFromHttpSession(wrappedSession); } /** * Performs the actual removal of the VaadinSession from the underlying HTTP * session after sanity checks have been performed * * @since 7.6 * @param wrappedSession * the underlying HTTP session */ protected void removeFromHttpSession(WrappedSession wrappedSession) { wrappedSession.removeAttribute(getSessionAttributeName()); } /** * Returns the name used for storing the VaadinSession in the underlying * HTTP session * * @since 7.6 * @return the attribute name used for storing the VaadinSession */ protected String getSessionAttributeName() { return VaadinSession.class.getName() + "." + getServiceName(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy