Skip to content

Commit

Permalink
fix: ensure session is registered with Hotswapper (#19979)
Browse files Browse the repository at this point in the history
VaadinSession references are registered in Hotswapper in a session init
listener. However, the listener might not always been invoked, for example
if the HTTP session is serialized and deserialized on server restart.
This change registers a UI init listener to make sure VaadinSession reference
is also registered for existing sessions.

Fixes #19973
  • Loading branch information
mcollovati authored Sep 19, 2024
1 parent ef6e04e commit e7ac185
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
import com.vaadin.flow.server.SessionDestroyListener;
import com.vaadin.flow.server.SessionInitEvent;
import com.vaadin.flow.server.SessionInitListener;
import com.vaadin.flow.server.UIInitEvent;
import com.vaadin.flow.server.UIInitListener;
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.VaadinSession;

Expand Down Expand Up @@ -83,7 +85,7 @@
* @since 24.5
*/
public class Hotswapper implements ServiceDestroyListener, SessionInitListener,
SessionDestroyListener {
SessionDestroyListener, UIInitListener {
private static final Logger LOGGER = LoggerFactory
.getLogger(Hotswapper.class);
private final Set<VaadinSession> sessions = ConcurrentHashMap.newKeySet();
Expand Down Expand Up @@ -413,6 +415,11 @@ public void serviceDestroy(ServiceDestroyEvent event) {
sessions.clear();
}

@Override
public void uiInit(UIInitEvent event) {
sessions.add(event.getUI().getSession());
}

/**
* Register the hotwsapper entry point for the given {@link VaadinService}.
* <p>
Expand All @@ -428,6 +435,7 @@ public void serviceDestroy(ServiceDestroyEvent event) {
public static Optional<Hotswapper> register(VaadinService vaadinService) {
if (!vaadinService.getDeploymentConfiguration().isProductionMode()) {
Hotswapper hotswapper = new Hotswapper(vaadinService);
vaadinService.addUIInitListener(hotswapper);
vaadinService.addSessionInitListener(hotswapper);
vaadinService.addSessionDestroyListener(hotswapper);
vaadinService.addServiceDestroyListener(hotswapper);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import com.vaadin.flow.server.SessionDestroyListener;
import com.vaadin.flow.server.SessionInitEvent;
import com.vaadin.flow.server.SessionInitListener;
import com.vaadin.flow.server.UIInitListener;
import com.vaadin.flow.server.VaadinRequest;
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.VaadinSession;
Expand Down Expand Up @@ -504,6 +505,7 @@ public void register_developmentMode_trackingListenerInstalled() {
AtomicBoolean sessionInitInstalled = new AtomicBoolean();
AtomicBoolean sessionDestroyInstalled = new AtomicBoolean();
AtomicBoolean serviceDestroyInstalled = new AtomicBoolean();
AtomicBoolean uiInitInstalled = new AtomicBoolean();
MockDeploymentConfiguration configuration = new MockDeploymentConfiguration();
configuration.setProductionMode(false);
VaadinService service = new MockVaadinServletService(configuration) {
Expand All @@ -527,6 +529,12 @@ public Registration addServiceDestroyListener(
serviceDestroyInstalled.set(true);
return super.addServiceDestroyListener(listener);
}

@Override
public Registration addUIInitListener(UIInitListener listener) {
uiInitInstalled.set(true);
return super.addUIInitListener(listener);
}
};
ApplicationConfiguration appConfig = Mockito
.mock(ApplicationConfiguration.class);
Expand All @@ -546,13 +554,17 @@ public Registration addServiceDestroyListener(
Assert.assertTrue(
"Expected hotswapper ServiceDestroyListener to be registered in development mode, but was not",
serviceDestroyInstalled.get());
Assert.assertTrue(
"Expected hotswapper UIInitListener to be registered in development mode, but was not",
uiInitInstalled.get());
}

@Test
public void register_productionMode_trackingListenerNotInstalled() {
AtomicBoolean sessionInitInstalled = new AtomicBoolean();
AtomicBoolean sessionDestroyInstalled = new AtomicBoolean();
AtomicBoolean serviceDestroyInstalled = new AtomicBoolean();
AtomicBoolean uiInitInstalled = new AtomicBoolean();
MockDeploymentConfiguration configuration = new MockDeploymentConfiguration();
configuration.setProductionMode(true);
VaadinService service = new MockVaadinServletService(configuration) {
Expand Down Expand Up @@ -588,6 +600,9 @@ public Registration addServiceDestroyListener(
Assert.assertFalse(
"Expected hotswapper ServiceDestroyListener not to be registered in production mode, but it was",
serviceDestroyInstalled.get());
Assert.assertFalse(
"Expected hotswapper UIInitListener not to be registered in production mode, but it was",
uiInitInstalled.get());
}

@Tag("my-route")
Expand Down

0 comments on commit e7ac185

Please sign in to comment.