]> source.dussan.org Git - vaadin-framework.git/commitdiff
Recreate transient pendingAccessQueue in readObject() (#12456)
authorJohannes Dahlström <johannesd@vaadin.com>
Tue, 24 Sep 2013 13:08:08 +0000 (16:08 +0300)
committerVaadin Code Review <review@vaadin.com>
Tue, 8 Oct 2013 12:33:28 +0000 (12:33 +0000)
This prevents a race condition in getPendingAccessQueue().

Change-Id: I1b8d013119e5963ed6083b7dd17afccd3a915e42

server/src/com/vaadin/server/VaadinSession.java
server/tests/src/com/vaadin/tests/server/TestSerialization.java

index 1f7a577a42f3b25c9a111672c8a372bcc6567b17..c73ac8b686a721c114434008635d34400d209481 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.vaadin.server;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.util.Collection;
@@ -203,10 +205,10 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
      * session is serialized as long as it doesn't happen while some other
      * thread has the lock.
      */
-    private transient ConcurrentLinkedQueue<FutureAccess> pendingAccessQueue;
+    private transient ConcurrentLinkedQueue<FutureAccess> pendingAccessQueue = new ConcurrentLinkedQueue<FutureAccess>();
 
     /**
-     * Create a new service session tied to a Vaadin service
+     * Creates a new VaadinSession tied to a VaadinService.
      * 
      * @param service
      *            the Vaadin service for the new session
@@ -1272,18 +1274,15 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
     }
 
     /**
-     * Gets the queue of tasks submitted using {@link #access(Runnable)}.
+     * Gets the queue of tasks submitted using {@link #access(Runnable)}. It is
+     * safe to call this method and access the returned queue without holding
+     * the {@link #lock() session lock}.
      * 
      * @since 7.1
      * 
-     * @return the pending access queue
+     * @return the queue of pending access tasks
      */
     public Queue<FutureAccess> getPendingAccessQueue() {
-        if (pendingAccessQueue == null) {
-            // pendingAccessQueue is transient, so will be null after
-            // deserialization
-            pendingAccessQueue = new ConcurrentLinkedQueue<FutureAccess>();
-        }
         return pendingAccessQueue;
     }
 
@@ -1299,4 +1298,13 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
         return csrfToken;
     }
 
+    /**
+     * Override default deserialization logic to account for transient
+     * {@link #pendingAccessQueue}.
+     */
+    private void readObject(ObjectInputStream stream) throws IOException,
+            ClassNotFoundException {
+        stream.defaultReadObject();
+        pendingAccessQueue = new ConcurrentLinkedQueue<FutureAccess>();
+    }
 }
index 84ff5ad6faa6bca176db0b5480465c2683b3abed..a52821a91991f1465af8b50b61546f7489c53353 100644 (file)
@@ -14,6 +14,7 @@ import com.vaadin.data.Property;
 import com.vaadin.data.util.IndexedContainer;
 import com.vaadin.data.util.MethodProperty;
 import com.vaadin.data.validator.RegexpValidator;
+import com.vaadin.server.VaadinSession;
 import com.vaadin.ui.Form;
 
 public class TestSerialization extends TestCase {
@@ -21,7 +22,7 @@ public class TestSerialization extends TestCase {
     public void testValidators() throws Exception {
         RegexpValidator validator = new RegexpValidator(".*", "Error");
         validator.validate("aaa");
-        RegexpValidator validator2 = (RegexpValidator) serializeAndDeserialize(validator);
+        RegexpValidator validator2 = serializeAndDeserialize(validator);
         validator2.validate("aaa");
     }
 
@@ -67,7 +68,17 @@ public class TestSerialization extends TestCase {
         serializeAndDeserialize(mp);
     }
 
-    private static Serializable serializeAndDeserialize(Serializable s)
+    public void testVaadinSession() throws Exception {
+        VaadinSession session = new VaadinSession(null);
+
+        session = serializeAndDeserialize(session);
+
+        assertNotNull(
+                "Pending access queue was not recreated after deserialization",
+                session.getPendingAccessQueue());
+    }
+
+    private static <S extends Serializable> S serializeAndDeserialize(S s)
             throws IOException, ClassNotFoundException {
         // Serialize and deserialize
 
@@ -77,10 +88,12 @@ public class TestSerialization extends TestCase {
         byte[] data = bs.toByteArray();
         ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(
                 data));
-        Serializable s2 = (Serializable) in.readObject();
+        @SuppressWarnings("unchecked")
+        S s2 = (S) in.readObject();
 
         // using special toString(Object) method to avoid calling
         // Property.toString(), which will be temporarily disabled
+        // TODO This is hilariously broken (#12723)
         if (s.equals(s2)) {
             System.out.println(toString(s) + " equals " + toString(s2));
         } else {