]> source.dussan.org Git - vaadin-framework.git/commitdiff
Convenience methods for running code in the UI or VaadinSession context (#11219)
authorArtur Signell <artur@vaadin.com>
Fri, 22 Mar 2013 14:15:22 +0000 (16:15 +0200)
committerVaadin Code Review <review@vaadin.com>
Thu, 28 Mar 2013 13:50:05 +0000 (13:50 +0000)
Change-Id: If31a965f925ca2bedb25c712b83ccb070a9e71a0

server/src/com/vaadin/server/VaadinService.java
server/src/com/vaadin/server/VaadinSession.java
server/src/com/vaadin/ui/UI.java
server/src/com/vaadin/util/CurrentInstance.java

index 9be1c64880b631ff07bde2b65bfdfeee1bb463eb..e5d72969c6621de26b2ffd41a548a40f9c9d48ba 100644 (file)
@@ -579,11 +579,20 @@ public abstract class VaadinService implements Serializable {
      */
     public void setCurrentInstances(VaadinRequest request,
             VaadinResponse response) {
-        CurrentInstance.setInheritable(VaadinService.class, this);
+        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
index 92a3399bb5017f9551e8ea7f2f444e19cdf0d5b1..322bfff92448e02aef082c30d26a4e3eb1ea91bc 100644 (file)
@@ -927,4 +927,37 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
     private static final Logger getLogger() {
         return Logger.getLogger(VaadinSession.class.getName());
     }
+
+    /**
+     * Performs a safe update of this VaadinSession.
+     * <p>
+     * This method runs the runnable code so that it is safe to update session
+     * variables. It also ensures that all thread locals are set correctly when
+     * executing the runnable.
+     * </p>
+     * <p>
+     * Note that using this method for a VaadinSession which has been detached
+     * from its underlying HTTP session is not necessarily safe. Exclusive
+     * access is provided through locking which is done using the underlying
+     * session.
+     * </p>
+     * 
+     * @param runnable
+     *            The runnable which updates the session
+     */
+    public void runSafely(Runnable runnable) {
+        Map<Class<?>, CurrentInstance> old = null;
+        lock();
+        try {
+            old = CurrentInstance.setThreadLocals(this);
+            runnable.run();
+        } finally {
+            unlock();
+            if (old != null) {
+                CurrentInstance.restoreThreadLocals(old);
+            }
+        }
+
+    }
+
 }
index 796d1f08ea6288982bd19388d0d0cccb23c659af..6a21ba3357b77bba8f56c0975a6bb7aec7d9d052 100644 (file)
@@ -1054,4 +1054,44 @@ public abstract class UI extends AbstractSingleComponentContainer implements
     public int getTabIndex() {
         return getState(false).tabIndex;
     }
+
+    /**
+     * Performs a safe update of this UI.
+     * <p>
+     * This method runs the runnable code so that it is safe to update UI and
+     * session variables. It also ensures that all thread locals are set
+     * correctly when executing the runnable.
+     * </p>
+     * <p>
+     * Note that exclusive access is only guaranteed as long as the UI is
+     * attached to a VaadinSession. If the UI is not attached to a session, this
+     * method makes no guarantees. If the UI is detached then the current
+     * session will also be null.
+     * </p>
+     * 
+     * @param runnable
+     *            The runnable which updates the UI
+     */
+    public void runSafely(Runnable runnable) {
+        Map<Class<?>, CurrentInstance> old = null;
+
+        VaadinSession session = getSession();
+
+        if (session != null) {
+            session.lock();
+        }
+        try {
+            old = CurrentInstance.setThreadLocals(this);
+            runnable.run();
+        } finally {
+            if (session != null) {
+                session.unlock();
+            }
+            if (old != null) {
+                CurrentInstance.restoreThreadLocals(old);
+            }
+        }
+
+    }
+
 }
index adf6d963c3652cf5fd5c38531b6461fba64a00be..a2cfd4fff7379fd84a3d9bcb3d3abc6b3e02468f 100644 (file)
@@ -21,8 +21,28 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import com.vaadin.server.VaadinPortlet;
+import com.vaadin.server.VaadinPortletService;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.VaadinResponse;
+import com.vaadin.server.VaadinService;
+import com.vaadin.server.VaadinServlet;
+import com.vaadin.server.VaadinServletService;
+import com.vaadin.server.VaadinSession;
+import com.vaadin.ui.UI;
+
 /**
  * Keeps track of various thread local instances used by the framework.
+ * <p>
+ * Currently the framework uses the following instances:
+ * </p>
+ * <p>
+ * Inheritable: {@link UI}, {@link VaadinPortlet}, {@link VaadinService},
+ * {@link VaadinServlet}, {@link VaadinSession}.
+ * </p>
+ * <p>
+ * Non-inheritable: {@link VaadinRequest}, {@link VaadinResponse}.
+ * </p>
  * 
  * @author Vaadin Ltd
  * @version @VERSION@
@@ -32,6 +52,18 @@ public class CurrentInstance implements Serializable {
     private final Object instance;
     private final boolean inheritable;
 
+    private static boolean portletAvailable = false;
+    {
+        try {
+            /*
+             * VaadinPortlet depends on portlet API which is available only if
+             * running in a portal.
+             */
+            portletAvailable = (VaadinPortlet.class.getName() != null);
+        } catch (Throwable t) {
+        }
+    }
+
     private static InheritableThreadLocal<Map<Class<?>, CurrentInstance>> instances = new InheritableThreadLocal<Map<Class<?>, CurrentInstance>>() {
         @Override
         protected Map<Class<?>, CurrentInstance> childValue(
@@ -118,7 +150,11 @@ public class CurrentInstance implements Serializable {
                     new CurrentInstance(instance, inheritable));
             if (previousInstance != null) {
                 assert previousInstance.inheritable == inheritable : "Inheritable status mismatch for "
-                        + type;
+                        + type
+                        + " (previous was "
+                        + previousInstance.inheritable
+                        + ", new is "
+                        + inheritable + ")";
             }
         }
     }
@@ -129,4 +165,72 @@ public class CurrentInstance implements Serializable {
     public static void clearAll() {
         instances.get().clear();
     }
+
+    /**
+     * Restores the given thread locals to the given values. Note that this
+     * should only be used internally to restore Vaadin classes.
+     * 
+     * @param old
+     *            A Class -> Object map to set as thread locals
+     */
+    public static void restoreThreadLocals(Map<Class<?>, CurrentInstance> old) {
+        for (Class c : old.keySet()) {
+            CurrentInstance ci = old.get(c);
+            set(c, ci.instance, ci.inheritable);
+        }
+    }
+
+    /**
+     * Sets thread locals for the UI and all related classes
+     * 
+     * @param ui
+     *            The UI
+     * @return A map containing the old values of the thread locals this method
+     *         updated.
+     */
+    public static Map<Class<?>, CurrentInstance> setThreadLocals(UI ui) {
+        Map<Class<?>, CurrentInstance> old = new HashMap<Class<?>, CurrentInstance>();
+        old.put(UI.class, new CurrentInstance(UI.getCurrent(), true));
+        UI.setCurrent(ui);
+        old.putAll(setThreadLocals(ui.getSession()));
+        return old;
+    }
+
+    /**
+     * Sets thread locals for the {@link VaadinSession} and all related classes
+     * 
+     * @param session
+     *            The VaadinSession
+     * @return A map containing the old values of the thread locals this method
+     *         updated.
+     */
+    public static Map<Class<?>, CurrentInstance> setThreadLocals(
+            VaadinSession session) {
+        Map<Class<?>, CurrentInstance> old = new HashMap<Class<?>, CurrentInstance>();
+        old.put(VaadinSession.class,
+                new CurrentInstance(VaadinSession.getCurrent(), true));
+        old.put(VaadinService.class,
+                new CurrentInstance(VaadinService.getCurrent(), true));
+        VaadinService service = null;
+        if (session != null) {
+            service = session.getService();
+        }
+
+        VaadinSession.setCurrent(session);
+        VaadinService.setCurrent(service);
+
+        if (service instanceof VaadinServletService) {
+            old.put(VaadinServlet.class,
+                    new CurrentInstance(VaadinServlet.getCurrent(), true));
+            VaadinServlet.setCurrent(((VaadinServletService) service)
+                    .getServlet());
+        } else if (portletAvailable && service instanceof VaadinPortletService) {
+            old.put(VaadinPortlet.class,
+                    new CurrentInstance(VaadinPortlet.getCurrent(), true));
+            VaadinPortlet.setCurrent(((VaadinPortletService) service)
+                    .getPortlet());
+        }
+
+        return old;
+    }
 }