Browse Source

Prevent killing UI if heartbeats are pending (#10371)

Fixes #9663
tags/8.3.0.alpha1
Teemu Suo-Anttila 6 years ago
parent
commit
d936003c92

+ 18
- 5
server/src/main/java/com/vaadin/server/VaadinService.java View File

@@ -1363,12 +1363,25 @@ public abstract class VaadinService implements Serializable {
public 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;
}

/**

+ 47
- 0
uitest/src/main/java/com/vaadin/tests/core/LockingUI.java View File

@@ -0,0 +1,47 @@
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.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", e -> {
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",
e -> Notification.show(ALL_OK, Type.TRAY_NOTIFICATION));

lockButton.setId("lock");
checkButton.setId("check");

setContent(new VerticalLayout(lockButton, checkButton));
}

}

+ 47
- 0
uitest/src/test/java/com/vaadin/tests/core/LockingUITest.java View File

@@ -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());
}
}

Loading…
Cancel
Save