|
16 | 16 |
|
17 | 17 | package com.vaadin.flow.hotswap;
|
18 | 18 |
|
| 19 | +import java.io.Serializable; |
19 | 20 | import java.net.URI;
|
20 | 21 | import java.util.ArrayList;
|
21 | 22 | import java.util.Arrays;
|
|
29 | 30 | import java.util.ResourceBundle;
|
30 | 31 | import java.util.Set;
|
31 | 32 | import java.util.concurrent.ConcurrentHashMap;
|
| 33 | +import java.util.concurrent.atomic.AtomicBoolean; |
32 | 34 | import java.util.function.Consumer;
|
33 | 35 | import java.util.stream.Collectors;
|
34 | 36 |
|
|
84 | 86 | * {@link VaadinHotswapper} interface.
|
85 | 87 | * <p>
|
86 | 88 | * </p>
|
| 89 | + * By default, Hotswapper determines the best browser page refresh strategy, but |
| 90 | + * a full page reload can be forced by setting the |
| 91 | + * {@code vaadin.hotswap.forcePageReload} system property. Hotswap tools can |
| 92 | + * alter the behavior at runtime by calling |
| 93 | + * {@link #forcePageReload(VaadinService, boolean)} |
| 94 | + * <p> |
| 95 | + * </p> |
87 | 96 | * For internal use only. May be renamed or removed in a future release.
|
88 | 97 | *
|
89 | 98 | * @author Vaadin Ltd
|
|
92 | 101 | */
|
93 | 102 | public class Hotswapper implements ServiceDestroyListener, SessionInitListener,
|
94 | 103 | SessionDestroyListener, UIInitListener {
|
| 104 | + |
| 105 | + /** |
| 106 | + * Configuration name for the system parameter that determines whether |
| 107 | + * Hotswapper should always trigger a full page reload instead of computing |
| 108 | + * an appropriate UI refresh strategy. |
| 109 | + */ |
| 110 | + public static final String FORCE_RELOAD_PROPERTY = "vaadin.hotswap.forcePageReload"; |
| 111 | + |
95 | 112 | private static final Logger LOGGER = LoggerFactory
|
96 | 113 | .getLogger(Hotswapper.class);
|
97 | 114 | private final Set<VaadinSession> sessions = ConcurrentHashMap.newKeySet();
|
@@ -259,9 +276,18 @@ private void onHotswapInternal(HashSet<Class<?>> classes,
|
259 | 276 | vaadinSession.getLockInstance().unlock();
|
260 | 277 | }
|
261 | 278 | }
|
262 |
| - EnumMap<UIRefreshStrategy, List<UI>> refreshActions = computeRefreshStrategies( |
263 |
| - classes, redefined); |
264 |
| - boolean uiTreeNeedsRefresh = !refreshActions.isEmpty(); |
| 279 | + forceBrowserReload = forceBrowserReload |
| 280 | + || getForceReloadHolder(vaadinService).shouldReloadPage(); |
| 281 | + |
| 282 | + boolean uiTreeNeedsRefresh = false; |
| 283 | + EnumMap<UIRefreshStrategy, List<UI>> refreshActions = null; |
| 284 | + |
| 285 | + // When a full page reload is requested it does not make sense to |
| 286 | + // compute refresh strategy |
| 287 | + if (!forceBrowserReload) { |
| 288 | + refreshActions = computeRefreshStrategies(classes, redefined); |
| 289 | + uiTreeNeedsRefresh = !refreshActions.isEmpty(); |
| 290 | + } |
265 | 291 | if (forceBrowserReload || uiTreeNeedsRefresh) {
|
266 | 292 | triggerClientUpdate(refreshActions, forceBrowserReload);
|
267 | 293 | }
|
@@ -447,8 +473,8 @@ private void triggerClientUpdate(
|
447 | 473 | EnumMap<UIRefreshStrategy, List<UI>> uisToRefresh,
|
448 | 474 | boolean forceReload) {
|
449 | 475 |
|
450 |
| - boolean refreshRequested = uisToRefresh |
451 |
| - .containsKey(UIRefreshStrategy.REFRESH); |
| 476 | + boolean refreshRequested = !forceReload |
| 477 | + && uisToRefresh.containsKey(UIRefreshStrategy.REFRESH); |
452 | 478 |
|
453 | 479 | // If some UI has push not enabled, BrowserLiveReload should be used to
|
454 | 480 | // trigger a client update. However, BrowserLiveReload broadcasts the
|
@@ -548,4 +574,57 @@ public static Optional<Hotswapper> register(VaadinService vaadinService) {
|
548 | 574 | return Optional.empty();
|
549 | 575 | }
|
550 | 576 |
|
| 577 | + /** |
| 578 | + * Instructs the {@link Hotswapper} if a full page reload should always be |
| 579 | + * triggered instead of detecting the best UI refresh strategy. |
| 580 | + * |
| 581 | + * @param vaadinService |
| 582 | + * the {@link VaadinService} instance. |
| 583 | + * @param forceReload |
| 584 | + * {@literal true} to always force a page reload, |
| 585 | + * {@literal false} to let the {@link Hotswapper} decide the |
| 586 | + * refresh strategy. |
| 587 | + */ |
| 588 | + public static void forcePageReload(VaadinService vaadinService, |
| 589 | + boolean forceReload) { |
| 590 | + Objects.requireNonNull(vaadinService, "VaadinService cannot be null"); |
| 591 | + getForceReloadHolder(vaadinService).activate(forceReload); |
| 592 | + } |
| 593 | + |
| 594 | + /** |
| 595 | + * Gets whether a forced full page reload is triggered on class changes. |
| 596 | + * |
| 597 | + * @param vaadinService |
| 598 | + * the {@link VaadinService} instance. |
| 599 | + * @return {@literal true} if full page reload if forced, otherwise |
| 600 | + * {@literal false}. |
| 601 | + */ |
| 602 | + public static boolean isForcedPageReload(VaadinService vaadinService) { |
| 603 | + return getForceReloadHolder(vaadinService).isActive(); |
| 604 | + } |
| 605 | + |
| 606 | + private static ForcePageReloadHolder getForceReloadHolder( |
| 607 | + VaadinService vaadinService) { |
| 608 | + return vaadinService.getContext().getAttribute( |
| 609 | + ForcePageReloadHolder.class, ForcePageReloadHolder::new); |
| 610 | + } |
| 611 | + |
| 612 | + private static class ForcePageReloadHolder implements Serializable { |
| 613 | + |
| 614 | + private final AtomicBoolean forceReload = new AtomicBoolean(false); |
| 615 | + |
| 616 | + void activate(boolean active) { |
| 617 | + forceReload.set(active); |
| 618 | + } |
| 619 | + |
| 620 | + boolean isActive() { |
| 621 | + return forceReload.get(); |
| 622 | + } |
| 623 | + |
| 624 | + boolean shouldReloadPage() { |
| 625 | + return forceReload.get() |
| 626 | + || Boolean.getBoolean(FORCE_RELOAD_PROPERTY); |
| 627 | + } |
| 628 | + } |
| 629 | + |
551 | 630 | }
|
0 commit comments