You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/07/07 05:56:55 UTC
[isis] branch master updated: ISIS-2787: adds support for
runtime/dynamic reloading of menubars.xml
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 7caa1be ISIS-2787: adds support for runtime/dynamic reloading of menubars.xml
7caa1be is described below
commit 7caa1be0ce6222c65c02fa7fb3faf21bdd8c0ced
Author: andi-huber <ah...@apache.org>
AuthorDate: Wed Jul 7 07:54:57 2021 +0200
ISIS-2787: adds support for runtime/dynamic reloading of menubars.xml
---
.../isis/core/metamodel/facetapi/FacetRanking.java | 42 ++++++++++++++++++----
.../isis/core/metamodel/facetapi/FacetUtil.java | 14 ++++++++
.../menubars/bootstrap3/MenuBarsServiceBS3.java | 11 ++++++
.../bootstrap3/MenuBarsServiceBS3Test.java | 24 +++++++++++++
4 files changed, 85 insertions(+), 6 deletions(-)
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetRanking.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetRanking.java
index f0c8e13..eda3d69 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetRanking.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetRanking.java
@@ -23,10 +23,12 @@ import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
+import java.util.function.Predicate;
import org.apache.isis.commons.collections.Can;
import org.apache.isis.commons.internal.assertions._Assert;
import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.commons.internal.base._Reduction;
import org.apache.isis.commons.internal.collections._Lists;
import org.apache.isis.commons.internal.collections._Multimaps;
import org.apache.isis.commons.internal.exceptions._Exceptions;
@@ -186,14 +188,12 @@ public final class FacetRanking {
* @param facetType - for convenience, so the caller does not need to cast the result
* @param precedenceUpper - upper bound
*/
- public <F extends Facet> Can<F> getRankLowerOrEqualTo(final @NonNull Class<F> facetType, final @NonNull Precedence precedenceUpper) {
+ public <F extends Facet> Can<F> getRankLowerOrEqualTo(
+ final @NonNull Class<F> facetType,
+ final @NonNull Precedence precedenceUpper) {
_Assert.assertEquals(this.facetType, facetType);
- val precedenceSelected = facetsByPrecedence
- .keySet()
- .stream()
- .filter(precedence->precedence.ordinal()<=precedenceUpper.ordinal())
- .max(Comparator.comparing(Precedence::ordinal));
+ val precedenceSelected = getHighestPrecedenceLowerOrEqualTo(precedenceUpper);
return precedenceSelected
.map(facetsByPrecedence::get)
@@ -217,6 +217,36 @@ public final class FacetRanking {
return Optional.ofNullable(topPrecedenceRef.get());
}
+ // -- DYNAMIC UPDATE SUPPORT
+
+ /**
+ * Removes any facet of {@code facetType} from facetHolder if it passes the given {@code filter}.
+ * @param facetType - to ensure the filter is properly generic-type-constraint
+ * @param filter
+ */
+ public <F extends Facet> void purgeIf(
+ final @NonNull Class<F> facetType,
+ final @NonNull Predicate<? extends F> filter) {
+
+ // reassess the top precedence
+ final _Reduction<Facet.Precedence> top = _Reduction.of(null, (a, b)->a==null?b:a.ordinal()>b.ordinal()?a:b);
+ val markedForRemoval = _Lists.newArrayList(facetsByPrecedence.size());
+
+ facetsByPrecedence.forEach((precedence, facets)->{
+ facets.removeIf(_Casts.uncheckedCast(filter));
+ if(!facets.isEmpty()) {
+ top.accept(precedence);
+ } else {
+ markedForRemoval.add(precedence);
+ }
+ });
+
+ topPrecedenceRef.set(top.getResult().orElse(null));
+
+ // remove keys that associate empty lists, so finding highest used precedence by key is simple
+ markedForRemoval.forEach(facetsByPrecedence::remove);
+ }
+
// -- VALIDATION SUPPORT
public <F extends Facet> void visitTopRankPairsSemanticDiffering(
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetUtil.java
index 92f2449..139d295 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetUtil.java
@@ -21,6 +21,7 @@ package org.apache.isis.core.metamodel.facetapi;
import java.util.Optional;
import java.util.function.BiConsumer;
+import java.util.function.Predicate;
import javax.annotation.Nullable;
@@ -97,4 +98,17 @@ public final class FacetUtil {
.forEach(target::addFacet);
}
+ /**
+ * removes any facet of facet-type from facetHolder if it passes the given filter
+ */
+ public static <F extends Facet> void purgeIf(
+ final Class<F> facetType,
+ final Predicate<? extends F> filter,
+ final FacetHolder facetHolder) {
+
+ facetHolder.getFacetRanking(facetType)
+ .ifPresent(ranking->ranking.purgeIf(facetType, filter));
+ }
+
+
}
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java
index e68eeaa..4f31074 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java
@@ -58,6 +58,7 @@ import org.apache.isis.core.metamodel.facetapi.FacetUtil;
import org.apache.isis.core.metamodel.facets.actions.layout.MemberDescribedFacetForMenuBarXml;
import org.apache.isis.core.metamodel.facets.actions.layout.MemberNamedFacetForMenuBarXml;
import org.apache.isis.core.metamodel.facets.actions.notinservicemenu.NotInServiceMenuFacet;
+import org.apache.isis.core.metamodel.facets.all.described.MemberDescribedFacet;
import org.apache.isis.core.metamodel.facets.all.i8n.staatic.HasStaticText;
import org.apache.isis.core.metamodel.facets.all.named.MemberNamedFacet;
import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacet;
@@ -194,6 +195,16 @@ implements MenuBarsService {
val layoutData = serviceActionLayoutDataByActionId.get(actionId);
+ FacetUtil.purgeIf(
+ MemberNamedFacet.class,
+ facet->facet instanceof MemberNamedFacetForMenuBarXml,
+ objectAction);
+
+ FacetUtil.purgeIf(
+ MemberDescribedFacet.class,
+ facet->facet instanceof MemberDescribedFacetForMenuBarXml,
+ objectAction);
+
FacetUtil.addFacetIfPresent(
MemberNamedFacetForMenuBarXml
.create(layoutData, objectAction));
diff --git a/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3Test.java b/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3Test.java
index a6e7512..e8093f1 100644
--- a/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3Test.java
+++ b/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3Test.java
@@ -27,11 +27,14 @@ import org.apache.isis.applib.services.layout.LayoutService;
import org.apache.isis.applib.services.menu.MenuBarsLoaderService;
import org.apache.isis.applib.services.menu.MenuBarsService;
import org.apache.isis.core.metamodel._testing.MetaModelContext_forTesting.MetaModelContext_forTestingBuilder;
+import org.apache.isis.core.metamodel.facetapi.Facet.Precedence;
+import org.apache.isis.core.metamodel.facets.all.named.MemberNamedFacet;
import org.apache.isis.core.runtimeservices.RuntimeServicesTestAbstract;
import org.apache.isis.core.runtimeservices.menubars.MenuBarsLoaderServiceDefault;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import lombok.val;
@@ -126,6 +129,27 @@ extends RuntimeServicesTestAbstract {
assertNotNull(objectAction);
assertEquals(customNamed, objectAction.getStaticFriendlyName().orElse(null));
+
+ // trigger a menubars.xml reload, which installs more facets while purging old ones
+ // verify however, that the number of facets stays constant
+
+ menuBarsService.menuBars(); // trigger reload (MenuBarsService for testing is setup the support reloading)
+
+ assertEquals(customNamed, objectAction.getStaticFriendlyName().orElse(null));
+
+ val facetRanking = objectAction.getFacetRanking(MemberNamedFacet.class).orElse(null);
+ assertNotNull(facetRanking);
+
+ // XML layout facets are installed at precedence HIGH
+ val xmlFacetRank = facetRanking.getRankLowerOrEqualTo(MemberNamedFacet.class, Precedence.HIGH);
+
+ // verify rank did not grow with latest menubars.xml reload
+ assertEquals(1, xmlFacetRank.size());
+
+ // verify winning facet is the same object as the last one added from latest menubars.xml reload,
+ // to make sure we are not feed the winner from an outdated cache
+ assertSame(facetRanking.getWinnerNonEvent(MemberNamedFacet.class).get(), xmlFacetRank.getLastOrFail());
+
}
// -- HELPER