Skip to content

Commit

Permalink
feat: Fire an event when all hotswap operations have completed (#20364)
Browse files Browse the repository at this point in the history
  • Loading branch information
Artur- authored Oct 29, 2024
1 parent 1ea3bc5 commit 0e70cb1
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2000-2024 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.vaadin.flow.hotswap;

import java.util.Set;

import com.vaadin.flow.server.VaadinService;

/*
* Event fired when hotswap has been completed.
*/
public class HotswapCompleteEvent {

private final Set<Class<?>> classes;
private final VaadinService vaadinService;
private final boolean redefined;

public HotswapCompleteEvent(VaadinService vaadinService,
Set<Class<?>> classes, boolean redefined) {
this.classes = classes;
this.vaadinService = vaadinService;
this.redefined = redefined;
}

/**
* Gets the classes that were updated.
*
* @return the updated classes
*/
public Set<Class<?>> getClasses() {
return classes;
}

/**
* Checks if the classes were redefined (as opposed to being new classes).
*
* @return {@literal true} if the classes have been redefined by hotswap
*/
public boolean isRedefined() {
return redefined;
}

/**
* Gets the Vaadin service.
*
* @return the vaadin service
*/
public VaadinService getService() {
return vaadinService;
}

}
11 changes: 11 additions & 0 deletions flow-server/src/main/java/com/vaadin/flow/hotswap/Hotswapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,17 @@ private void onHotswapInternal(HashSet<Class<?>> classes,
if (forceBrowserReload || uiTreeNeedsRefresh) {
triggerClientUpdate(refreshActions, forceBrowserReload);
}

HotswapCompleteEvent event = new HotswapCompleteEvent(vaadinService,
classes, redefined);
for (VaadinHotswapper hotSwapper : hotSwappers) {
try {
hotSwapper.onHotswapComplete(event);
} catch (Exception ex) {
LOGGER.debug("Hotswap complete event handling failed for {}",
hotSwapper, ex);
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,16 @@ default boolean onClassLoadEvent(VaadinSession vaadinSession,
return false;
}

/**
* Called by Vaadin hotswap entry point after all hotswap related operations
* have been completed.
*
* @param event
* an event containing information about the hotswap operation.
*/
default void onHotswapComplete(HotswapCompleteEvent event) {
// no-op by default
return;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;

import com.vaadin.flow.component.Component;
Expand Down Expand Up @@ -157,6 +158,10 @@ public void onHotswap_noActiveSession_onlyGlobalHookCalled() {
Mockito.verify(hillaHotswapper, never()).onClassLoadEvent(
isA(VaadinSession.class), anySet(), anyBoolean());

HotswapCompleteEvent hotswapCompleteEvent = new HotswapCompleteEvent(
service, classes, false);
assertOnHotswapCompleteInvoked(flowHotswapper, hotswapCompleteEvent);
assertOnHotswapCompleteInvoked(hillaHotswapper, hotswapCompleteEvent);
}

@Test
Expand Down Expand Up @@ -222,6 +227,10 @@ public void onHotswap_sessionHookCalledOnlyForActiveSessions()
Mockito.verify(hillaHotswapper, never()).onClassLoadEvent(
isA(VaadinSession.class), anySet(), anyBoolean());

HotswapCompleteEvent hotswapCompleteEvent = new HotswapCompleteEvent(
service, classes, true);
assertOnHotswapCompleteInvoked(flowHotswapper, hotswapCompleteEvent);
assertOnHotswapCompleteInvoked(hillaHotswapper, hotswapCompleteEvent);
}

@Test
Expand Down Expand Up @@ -788,6 +797,18 @@ public Registration addServiceDestroyListener(
uiInitInstalled.get());
}

private void assertOnHotswapCompleteInvoked(VaadinHotswapper hotswapper,
HotswapCompleteEvent event) {
var eventArgumentCaptor = ArgumentCaptor
.forClass(HotswapCompleteEvent.class);
Mockito.verify(hotswapper)
.onHotswapComplete(eventArgumentCaptor.capture());
HotswapCompleteEvent capturedEvent = eventArgumentCaptor.getValue();
Assert.assertEquals(event.getService(), capturedEvent.getService());
Assert.assertEquals(event.getClasses(), capturedEvent.getClasses());
Assert.assertEquals(event.isRedefined(), capturedEvent.isRedefined());
}

@Tag("my-route")
public static class MyRoute extends Component {

Expand All @@ -796,13 +817,15 @@ public static class MyRoute extends Component {
@Tag("my-route-with-child")
public static class MyRouteWithChild extends Component
implements HasComponents {

public MyRouteWithChild() {
add(new MyComponent());
}
}

@Tag("my-layout")
public static class MyLayout extends Component implements RouterLayout {

}

@Tag("my-layout-with-child")
Expand All @@ -819,11 +842,13 @@ public void showRouterLayoutContent(HasElement content) {
@Tag("my-nested-layout")
public static class MyNestedLayout extends Component
implements RouterLayout {

}

@Tag("my-nested-layout-with-child")
public static class MyNestedLayoutWithChild extends Component
implements HasComponents, RouterLayout {

@Override
public void showRouterLayoutContent(HasElement content) {
RouterLayout.super.showRouterLayoutContent(content);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ protected Stream<String> getExcludedPatterns() {
"com\\.vaadin\\.flow\\.component\\.dnd\\.osgi\\.DndConnectorResource",
"com\\.vaadin\\.flow\\.component\\.internal\\.DeadlockDetectingCompletableFuture",
"com\\.vaadin\\.flow\\.function\\.VaadinApplicationInitializationBootstrap",
"com\\.vaadin\\.flow\\.hotswap\\.HotswapCompleteEvent",
"com\\.vaadin\\.flow\\.hotswap\\.Hotswapper",
"com\\.vaadin\\.flow\\.hotswap\\.VaadinHotswapper",
"com\\.vaadin\\.flow\\.internal\\.BrowserLiveReloadAccessor",
Expand Down

0 comments on commit 0e70cb1

Please sign in to comment.