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

org.dellroad.stuff.vaadin7.VaadinUtil Maven / Gradle / Ivy

The newest version!

/*
 * Copyright (C) 2022 Archie L. Cobbs. All rights reserved.
 */

package org.dellroad.stuff.vaadin7;

import com.vaadin.server.SessionDestroyEvent;
import com.vaadin.server.SessionDestroyListener;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinSession;

import java.util.concurrent.Future;

/**
 * Miscellaneous utility methods.
 */
public final class VaadinUtil {

    private VaadinUtil() {
    }

    /**
     * Verify that we are running in the context of the given session and holding the session's lock.
     * This method can be used by any code that manipulates Vaadin state to assert that the proper Vaadin
     * locking has been performed.
     *
     * @param session session we are supposed to be running with
     * @throws IllegalArgumentException if {@code session} is null
     * @throws IllegalStateException if there is no {@link VaadinSession} associated with the current thread
     * @throws IllegalStateException if the {@link VaadinSession} associated with the current thread is not {@code session}
     * @throws IllegalStateException if the {@link VaadinSession} associated with the current thread is not locked
     * @throws IllegalStateException if the {@link VaadinSession} associated with the current thread is locked by another thread
     */
    public static void assertSession(VaadinSession session) {
        if (session == null)
            throw new IllegalArgumentException("null session");
        final VaadinSession currentSession = VaadinSession.getCurrent();
        if (currentSession == null)
            throw new IllegalStateException("there is no VaadinSession associated with the current thread");
        if (currentSession != session) {
            throw new IllegalStateException("the VaadinSession associated with the current thread " + currentSession
              + " is not the same session as the given one " + session);
        }
        if (!session.hasLock()) {
            throw new IllegalStateException("the VaadinSession associated with the current thread " + currentSession
              + " is not locked by this thread");
        }
    }

    /**
     * Get the {@link VaadinSession} associated with the current thread.
     * This is just a wrapper around {@link VaadinSession#getCurrent} that throws an exception instead
     * of returning null when there is no session associated with the current thread.
     *
     * @return current {@link VaadinSession}, never null
     *
     * @throws IllegalStateException if there is no {@link VaadinSession} associated with the current thread
     */
    public static VaadinSession getCurrentSession() {
        VaadinSession session = VaadinSession.getCurrent();
        if (session == null) {
            throw new IllegalStateException("there is no VaadinSession associated with the current thread;"
              + " are we executing within a Vaadin HTTP request or VaadinUtil.invoke()?");
        }
        return session;
    }

    /**
     * Get the {@link VaadinRequest} associated with the current thread.
     * This is just a wrapper around {@link VaadinService#getCurrentRequest} that throws an exception instead
     * of returning null when there is no request associated with the current thread.
     *
     * @return current {@link VaadinRequest}, never null
     *
     * @throws IllegalStateException if there is no {@link VaadinRequest} associated with the current thread
     */
    public static VaadinRequest getCurrentRequest() {
        VaadinRequest request = VaadinService.getCurrentRequest();
        if (request == null) {
            throw new IllegalStateException("there is no VaadinRequest associated with the current thread;"
              + " are we executing within a Vaadin HTTP request?");
        }
        return request;
    }

    /**
     * Peform some action while holding the given {@link VaadinSession}'s lock.
     *
     * 

* This method now just invokes {@link VaadinSession#accessSynchronously}, a method which didn't exist in earlier * versions of Vaadin. * *

* All back-end threads that interact with Vaadin components must use this method (or {@link #invokeLater invokeLater()}) * to avoid race conditions. Since session locks are re-entrant, it will not cause problems if this method is also * used by a "front-end" (i.e., Vaadin HTTP request) thread. * *

* Note: when executing within a Vaadin HTTP request, the current thread's {@link VaadinSession} is available * via {@link VaadinSession#getCurrent}; consider also using {@link VaadinApplication#invoke} instead of this method. * *

* Warning: background threads should be careful when invoking this method to ensure they * are not already holding an application-specific lock that a separate HTTP request thread could * attempt to acquire during its normal processing: because the HTTP request thread will probably * already be holding the session lock when it attempts to acquire the application-specific lock, * this creates the potential for a lock-ordering reversal deadlock. * * @param session Vaadin session * @param action action to perform * @throws IllegalArgumentException if either parameter is null * @see VaadinApplication#invoke */ public static void invoke(VaadinSession session, Runnable action) { if (session == null) throw new IllegalArgumentException("null session"); if (action == null) throw new IllegalArgumentException("null action"); session.accessSynchronously(action); } /** * Peform some action while holding the given {@link VaadinSession}'s lock, but do so asynchronously. * *

* Here the term "asynchronously" means: *

    *
  • If any thread holds the session lock (including the current thread), this method will return * immediately and the action will be performed later, when the session is eventually unlocked.
  • *
  • If no thread holds the session lock, the session will be locked and the action performed * synchronously by the current thread.
  • *
* *

* This method now just invokes {@link VaadinSession#access}, a method which didn't exist in earlier * versions of Vaadin. * *

* Note: when executing within a Vaadin HTTP request, the current thread's {@link VaadinSession} is available * via {@link VaadinSession#getCurrent}; consider also using {@link VaadinApplication#invokeLater} instead of this method. * * @param session Vaadin session * @param action action to perform * @return a corresponding {@link Future} * @throws IllegalArgumentException if either parameter is null * @see #invoke * @see VaadinApplication#invokeLater */ public static Future invokeLater(VaadinSession session, Runnable action) { if (session == null) throw new IllegalArgumentException("null session"); if (action == null) throw new IllegalArgumentException("null action"); return session.access(action); } /** * Register for a notification when the {@link VaadinSession} is closed, without creating a memory leak. * This method is intended to be used by listeners that are themselves part of a Vaadin application. * *

* Explanation: the {@link VaadinSession} class does not provide a listener API directly; instead, you must * use the {@link com.vaadin.server.VaadinService} class. However, registering as a listener on the * {@link com.vaadin.server.VaadinService} when you are part of a Vaadin application sets you up for a memory leak * if you forget to unregister yourself when the notification arrives, because the {@link com.vaadin.server.VaadinService} * lifetime is longer than the {@link VaadinSession} lifetime. This method handles that de-registration for * you automatically. * * @param session Vaadin session * @param listener listener for notifications * @throws IllegalArgumentException if either parameter is null * @see VaadinApplication#addSessionDestroyListener */ public static void addSessionDestroyListener(VaadinSession session, SessionDestroyListener listener) { session.getService().addSessionDestroyListener(new LeakAvoidingDestroyListener(session, listener)); } /** * Remove a listener added via {@link #addSessionDestroyListener addSessionDestroyListener()}. * * @param session Vaadin session * @param listener listener for notifications * @throws IllegalArgumentException if either parameter is null * @see VaadinApplication#removeSessionDestroyListener */ public static void removeSessionDestroyListener(VaadinSession session, SessionDestroyListener listener) { session.getService().removeSessionDestroyListener(new LeakAvoidingDestroyListener(session, listener)); } // LeakAvoidingDestroyListener @SuppressWarnings("serial") private static class LeakAvoidingDestroyListener implements SessionDestroyListener { private final VaadinSession session; private final SessionDestroyListener listener; LeakAvoidingDestroyListener(VaadinSession session, SessionDestroyListener listener) { if (session == null) throw new IllegalArgumentException("null session"); if (listener == null) throw new IllegalArgumentException("null listener"); this.session = session; this.listener = listener; } @Override public void sessionDestroy(SessionDestroyEvent event) { final VaadinSession closedSession = event.getSession(); if (closedSession == this.session) { final VaadinService service = this.session.getService(); VaadinUtil.invokeLater(this.session, () -> service.removeSessionDestroyListener(this)); // avoid mem leak this.listener.sessionDestroy(event); } } @Override public boolean equals(Object obj) { if (obj == null || obj.getClass() != this.getClass()) return false; LeakAvoidingDestroyListener that = (LeakAvoidingDestroyListener)obj; return this.session == that.session && this.listener.equals(that.listener); } @Override public int hashCode() { return this.session.hashCode() ^ this.listener.hashCode(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy