]> source.dussan.org Git - vaadin-framework.git/commitdiff
Prevent killing UI if heartbeats are pending (#10371) (#10450)
authorOlli Tietäväinen <ollit@vaadin.com>
Wed, 20 Dec 2017 14:48:56 +0000 (16:48 +0200)
committerGitHub <noreply@github.com>
Wed, 20 Dec 2017 14:48:56 +0000 (16:48 +0200)
* Prevent killing UI if heartbeats are pending (#10371)

Fixes #9663

* fixed Java 1.8 syntax -> 1.6

server/src/main/java/com/vaadin/server/VaadinService.java
uitest/src/main/java/com/vaadin/tests/core/LockingUI.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/core/LockingUITest.java [new file with mode: 0644]

index e453332f026042d864ad14d067c89d559f4f61c9..50dd55ee7ace2d28b759ef09884cda52093d6531 100644 (file)
@@ -1291,12 +1291,25 @@ public abstract class VaadinService implements Serializable {
     private boolean isUIActive(UI ui) {
         if (ui.isClosing()) {
             return false;
-        } else {
-            long now = System.currentTimeMillis();
-            int timeout = 1000 * getHeartbeatTimeout();
-            return timeout < 0
-                    || now - ui.getLastHeartbeatTimestamp() < timeout;
         }
+
+        // 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;
     }
 
     /**
diff --git a/uitest/src/main/java/com/vaadin/tests/core/LockingUI.java b/uitest/src/main/java/com/vaadin/tests/core/LockingUI.java
new file mode 100644 (file)
index 0000000..10f503b
--- /dev/null
@@ -0,0 +1,62 @@
+package com.vaadin.tests.core;
+
+import com.vaadin.launcher.CustomDeploymentConfiguration;
+import com.vaadin.launcher.CustomDeploymentConfiguration.Conf;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.VaadinService;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.Notification;
+import com.vaadin.ui.Notification.Type;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.VerticalLayout;
+
+@CustomDeploymentConfiguration({
+        @Conf(name = "heartbeatInterval", value = "2") })
+public class LockingUI extends UI {
+
+    public static final String LOCKING_ENDED = "Locking has ended";
+    public static final String ALL_OK = "All is fine";
+
+    @Override
+    protected void init(VaadinRequest request) {
+        Button lockButton = new Button("Lock UI for too long",
+                new ClickListener() {
+
+                    @Override
+                    public void buttonClick(ClickEvent event) {
+                        int heartbeatInterval = VaadinService.getCurrent()
+                                .getDeploymentConfiguration()
+                                .getHeartbeatInterval();
+                        try {
+                            // Wait for 4 heartbeats
+                            long timeout = heartbeatInterval * 1000;
+                            for (int i = 0; i < 4; ++i) {
+                                Thread.sleep(timeout);
+                            }
+
+                        } catch (InterruptedException e1) {
+                            throw new RuntimeException(
+                                    "Timeout should not get interrupted.");
+                        }
+                        Notification.show(LOCKING_ENDED,
+                                Type.TRAY_NOTIFICATION);
+                    }
+                });
+        Button checkButton = new Button("Test communication",
+                new ClickListener() {
+
+                    @Override
+                    public void buttonClick(ClickEvent event) {
+                        Notification.show(ALL_OK, Type.TRAY_NOTIFICATION);
+                    }
+                });
+
+        lockButton.setId("lock");
+        checkButton.setId("check");
+
+        setContent(new VerticalLayout(lockButton, checkButton));
+    }
+
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/core/LockingUITest.java b/uitest/src/test/java/com/vaadin/tests/core/LockingUITest.java
new file mode 100644 (file)
index 0000000..214adfc
--- /dev/null
@@ -0,0 +1,47 @@
+package com.vaadin.tests.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.testbench.elements.NotificationElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+public class LockingUITest extends SingleBrowserTest {
+
+    @Test
+    public void testLockingTheUIFor4HeartBeats() {
+        openTestURL();
+
+        clickButtonAndCheckNotification("check", LockingUI.ALL_OK);
+        clickButtonAndCheckNotification("lock", LockingUI.LOCKING_ENDED);
+        clickButtonAndCheckNotification("check", LockingUI.ALL_OK);
+    }
+
+    private void clickButtonAndCheckNotification(String buttonId, String text) {
+        checkNoInitialNotification();
+
+        $(ButtonElement.class).id(buttonId).click();
+        testBench().waitForVaadin();
+
+        checkNotification(text);
+    }
+
+    private void checkNotification(String text) {
+        assertTrue("Notification should be displayed",
+                $(NotificationElement.class).exists());
+
+        NotificationElement notification = $(NotificationElement.class).first();
+        assertEquals("Unexpected text content in Notification", text,
+                notification.getText());
+        notification.close();
+    }
+
+    private void checkNoInitialNotification() {
+        assertFalse("Extra notification displayed",
+                $(NotificationElement.class).exists());
+    }
+}