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/06 16:24:53 UTC

[isis] branch master updated: ISIS-2787: fixes action names from menubars.layout.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 364a6d6  ISIS-2787: fixes action names from menubars.layout.xml
364a6d6 is described below

commit 364a6d6dfaa9b62d165c08f13a72b6e32c409e16
Author: andi-huber <ah...@apache.org>
AuthorDate: Tue Jul 6 18:24:38 2021 +0200

    ISIS-2787: fixes action names from menubars.layout.xml
    
    however, we don't support dynamic menubars.layout.xml reloading
---
 .../isis/applib/services/menu/MenuBarsService.java | 14 ++++++++
 .../actions/layout/ActionLayoutFacetFactory.java   |  2 +-
 ...erDescribedFacetForActionLayoutAnnotation.java} |  6 ++--
 ....java => MemberDescribedFacetForActionXml.java} |  8 ++---
 ...java => MemberDescribedFacetForMenuBarXml.java} | 16 +++++----
 ...try.java => MemberNamedFacetForMenuBarXml.java} | 12 ++++---
 .../all/i18n/ServiceActionNamingPostProcessor.java | 35 ++++++++++++++++---
 .../services/grid/GridSystemServiceAbstract.java   |  4 +--
 .../menubars/MenuBarsLoaderServiceDefault.java     | 34 ++++++++++++++----
 .../menubars/bootstrap3/MenuBarsServiceBS3.java    | 40 +++++++++++++++++++---
 .../RuntimeServicesTestAbstract.java               | 24 ++++++-------
 .../bootstrap3/MenuBarsServiceBS3Test.java         | 23 ++++++++-----
 12 files changed, 159 insertions(+), 59 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/menu/MenuBarsService.java b/api/applib/src/main/java/org/apache/isis/applib/services/menu/MenuBarsService.java
index 8a2c3e3..f364251 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/menu/MenuBarsService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/menu/MenuBarsService.java
@@ -18,8 +18,12 @@
  */
 package org.apache.isis.applib.services.menu;
 
+import java.util.Optional;
+
+import org.apache.isis.applib.Identifier;
 import org.apache.isis.applib.IsisModuleApplib;
 import org.apache.isis.applib.annotation.Value;
+import org.apache.isis.applib.layout.component.ServiceActionLayoutData;
 import org.apache.isis.applib.layout.menubars.MenuBars;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 
@@ -68,6 +72,11 @@ public interface MenuBarsService {
      */
     MenuBars menuBars(final Type type);
 
+    /**
+     * @since 2.0
+     */
+    Optional<ServiceActionLayoutData> lookupLayout(Identifier serviceActionIdentifier);
+
     // -- JUNIT SUPPORT
 
     static MenuBarsService forTesting() {
@@ -78,6 +87,11 @@ public interface MenuBarsService {
                 throw _Exceptions.unsupportedOperation();
             }
 
+            @Override
+            public Optional<ServiceActionLayoutData> lookupLayout(Identifier serviceActionIdentifier) {
+                throw _Exceptions.unsupportedOperation();
+            }
+
         };
     }
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java
index c709e52..cc33a71 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java
@@ -67,7 +67,7 @@ extends FacetFactoryAbstract {
 
         // describedAs
         addFacetIfPresent(
-                DescribedAsFacetForActionLayoutAnnotation
+                MemberDescribedFacetForActionLayoutAnnotation
                 .create(actionLayoutIfAny, facetHolder));
 
         // hidden
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/DescribedAsFacetForActionLayoutAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberDescribedFacetForActionLayoutAnnotation.java
similarity index 89%
rename from core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/DescribedAsFacetForActionLayoutAnnotation.java
rename to core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberDescribedFacetForActionLayoutAnnotation.java
index 79e6d90..f8dd84b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/DescribedAsFacetForActionLayoutAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberDescribedFacetForActionLayoutAnnotation.java
@@ -27,7 +27,7 @@ import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.all.described.MemberDescribedFacet;
 import org.apache.isis.core.metamodel.facets.all.described.MemberDescribedFacetWithStaticTextAbstract;
 
-public class DescribedAsFacetForActionLayoutAnnotation
+public class MemberDescribedFacetForActionLayoutAnnotation
 extends MemberDescribedFacetWithStaticTextAbstract {
 
     public static Optional<MemberDescribedFacet> create(
@@ -38,10 +38,10 @@ extends MemberDescribedFacetWithStaticTextAbstract {
                 .map(ActionLayout::describedAs)
                 .filter(_Strings::isNotEmpty)
                 .map(describedAs ->
-                    new DescribedAsFacetForActionLayoutAnnotation(describedAs, holder));
+                    new MemberDescribedFacetForActionLayoutAnnotation(describedAs, holder));
     }
 
-    private DescribedAsFacetForActionLayoutAnnotation(
+    private MemberDescribedFacetForActionLayoutAnnotation(
             final String described,
             final FacetHolder holder) {
         super(described, holder);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/DescribedAsFacetForActionXml.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberDescribedFacetForActionXml.java
similarity index 84%
copy from core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/DescribedAsFacetForActionXml.java
copy to core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberDescribedFacetForActionXml.java
index c9e2fdf..73ca60c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/DescribedAsFacetForActionXml.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberDescribedFacetForActionXml.java
@@ -27,7 +27,7 @@ import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.all.described.MemberDescribedFacet;
 import org.apache.isis.core.metamodel.facets.all.described.MemberDescribedFacetWithStaticTextAbstract;
 
-public class DescribedAsFacetForActionXml
+public class MemberDescribedFacetForActionXml
 extends MemberDescribedFacetWithStaticTextAbstract {
 
     public static Optional<MemberDescribedFacet> create(
@@ -38,12 +38,12 @@ extends MemberDescribedFacetWithStaticTextAbstract {
         }
         final String describedAs = _Strings.emptyToNull(actionLayout.getDescribedAs());
         return describedAs != null
-                ? Optional.of(new DescribedAsFacetForActionXml(describedAs, holder))
+                ? Optional.of(new MemberDescribedFacetForActionXml(describedAs, holder))
                 : Optional.empty();
     }
 
-    private DescribedAsFacetForActionXml(final String described, final FacetHolder holder) {
-        super(described, holder);
+    private MemberDescribedFacetForActionXml(final String described, final FacetHolder holder) {
+        super(described, holder, Precedence.HIGH); // XML layout overrules layout from annotations
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/DescribedAsFacetForActionXml.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberDescribedFacetForMenuBarXml.java
similarity index 74%
rename from core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/DescribedAsFacetForActionXml.java
rename to core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberDescribedFacetForMenuBarXml.java
index c9e2fdf..24ab1bd 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/DescribedAsFacetForActionXml.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberDescribedFacetForMenuBarXml.java
@@ -21,29 +21,31 @@ package org.apache.isis.core.metamodel.facets.actions.layout;
 
 import java.util.Optional;
 
-import org.apache.isis.applib.layout.component.ActionLayoutData;
+import javax.annotation.Nullable;
+
+import org.apache.isis.applib.layout.component.ServiceActionLayoutData;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.all.described.MemberDescribedFacet;
 import org.apache.isis.core.metamodel.facets.all.described.MemberDescribedFacetWithStaticTextAbstract;
 
-public class DescribedAsFacetForActionXml
+public class MemberDescribedFacetForMenuBarXml
 extends MemberDescribedFacetWithStaticTextAbstract {
 
     public static Optional<MemberDescribedFacet> create(
-            final ActionLayoutData actionLayout,
+            final @Nullable ServiceActionLayoutData actionLayout,
             final FacetHolder holder) {
         if(actionLayout == null) {
             return Optional.empty();
         }
         final String describedAs = _Strings.emptyToNull(actionLayout.getDescribedAs());
-        return describedAs != null
-                ? Optional.of(new DescribedAsFacetForActionXml(describedAs, holder))
+        return _Strings.isNotEmpty(describedAs)
+                ? Optional.of(new MemberDescribedFacetForMenuBarXml(describedAs, holder))
                 : Optional.empty();
     }
 
-    private DescribedAsFacetForActionXml(final String described, final FacetHolder holder) {
-        super(described, holder);
+    private MemberDescribedFacetForMenuBarXml(final String named, final FacetHolder holder) {
+        super(named, holder, Precedence.HIGH); // XML menu-bar entries overrule layout from annotations
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberNamedFacetForMenuBarEntry.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberNamedFacetForMenuBarXml.java
similarity index 79%
rename from core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberNamedFacetForMenuBarEntry.java
rename to core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberNamedFacetForMenuBarXml.java
index 129aa9d..3129a0f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberNamedFacetForMenuBarEntry.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/MemberNamedFacetForMenuBarXml.java
@@ -21,29 +21,31 @@ package org.apache.isis.core.metamodel.facets.actions.layout;
 
 import java.util.Optional;
 
+import javax.annotation.Nullable;
+
 import org.apache.isis.applib.layout.component.ServiceActionLayoutData;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.all.named.MemberNamedFacet;
 import org.apache.isis.core.metamodel.facets.all.named.MemberNamedFacetWithStaticTextAbstract;
 
-public class MemberNamedFacetForMenuBarEntry
+public class MemberNamedFacetForMenuBarXml
 extends MemberNamedFacetWithStaticTextAbstract {
 
     public static Optional<MemberNamedFacet> create(
-            final ServiceActionLayoutData actionLayout,
+            final @Nullable ServiceActionLayoutData actionLayout,
             final FacetHolder holder) {
         if(actionLayout == null) {
             return Optional.empty();
         }
         final String named = _Strings.emptyToNull(actionLayout.getNamed());
         return named != null
-                ? Optional.of(new MemberNamedFacetForMenuBarEntry(named, holder))
+                ? Optional.of(new MemberNamedFacetForMenuBarXml(named, holder))
                 : Optional.empty();
     }
 
-    private MemberNamedFacetForMenuBarEntry(final String named, final FacetHolder holder) {
-        super(named, holder);
+    private MemberNamedFacetForMenuBarXml(final String named, final FacetHolder holder) {
+        super(named, holder, Precedence.HIGH); // XML menu-bar entries overrule layout from annotations
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/all/i18n/ServiceActionNamingPostProcessor.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/all/i18n/ServiceActionNamingPostProcessor.java
index 086e1a0..5e9f685 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/all/i18n/ServiceActionNamingPostProcessor.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/all/i18n/ServiceActionNamingPostProcessor.java
@@ -18,10 +18,13 @@
  */
 package org.apache.isis.core.metamodel.postprocessors.all.i18n;
 
-
 import javax.inject.Inject;
 
+import org.apache.isis.applib.services.menu.MenuBarsService;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
+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.postprocessors.ObjectSpecificationPostProcessorAbstract;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
@@ -29,6 +32,9 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 
+import lombok.Getter;
+import lombok.val;
+
 public class ServiceActionNamingPostProcessor
 extends ObjectSpecificationPostProcessorAbstract {
 
@@ -39,17 +45,27 @@ extends ObjectSpecificationPostProcessorAbstract {
 
     @Override
     protected void doPostProcess(final ObjectSpecification objectSpecification) {
+    }
+
+    @Override
+    protected void doPostProcess(final ObjectSpecification objectSpecification, final ObjectAction objectAction) {
 
         if(!(objectSpecification.getBeanSort().isManagedBeanContributing())) {
             return;
         }
 
-        //TODO[ISIS-2787] load menubar, then install MemberNamedFacets
+        // installs MemberNamedFacet(s) and MemberDescribedFacet(s) for MenuBar entries
 
-    }
+        val layoutData = getMenuBarsService().lookupLayout(objectAction.getFeatureIdentifier()).orElse(null);
+
+        FacetUtil.addFacetIfPresent(
+                MemberNamedFacetForMenuBarXml
+                .create(layoutData, objectAction));
+
+        FacetUtil.addFacetIfPresent(
+                MemberDescribedFacetForMenuBarXml
+                .create(layoutData, objectAction));
 
-    @Override
-    protected void doPostProcess(final ObjectSpecification objectSpecification, final ObjectAction act) {
     }
 
     @Override
@@ -66,5 +82,14 @@ extends ObjectSpecificationPostProcessorAbstract {
 
     // -- HELEPR
 
+    @Getter(lazy = true)
+    private final MenuBarsService menuBarsService = getMenuBarsServiceAndReloadXml();
+
+    private final MenuBarsService getMenuBarsServiceAndReloadXml() {
+        val menuBarsService = getServiceRegistry()
+                .lookupServiceElseFail(MenuBarsService.class);
+        menuBarsService.menuBars(); // as a side-effect reloads XML resource if supported
+        return menuBarsService;
+    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java
index a83e2c0..bb7fc35 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java
@@ -44,7 +44,7 @@ import org.apache.isis.core.metamodel.facets.actions.layout.ActionPositionFacetF
 import org.apache.isis.core.metamodel.facets.actions.layout.BookmarkPolicyFacetForActionXml;
 import org.apache.isis.core.metamodel.facets.actions.layout.CssClassFaFacetForActionXml;
 import org.apache.isis.core.metamodel.facets.actions.layout.CssClassFacetForActionXml;
-import org.apache.isis.core.metamodel.facets.actions.layout.DescribedAsFacetForActionXml;
+import org.apache.isis.core.metamodel.facets.actions.layout.MemberDescribedFacetForActionXml;
 import org.apache.isis.core.metamodel.facets.actions.layout.HiddenFacetForActionXml;
 import org.apache.isis.core.metamodel.facets.actions.layout.MemberNamedFacetForActionXml;
 import org.apache.isis.core.metamodel.facets.actions.layout.PromptStyleFacetForActionXml;
@@ -229,7 +229,7 @@ implements GridSystemService<G> {
                 addFacetIfPresent(BookmarkPolicyFacetForActionXml.create(actionLayoutData, objectAction));
                 addFacetIfPresent(CssClassFacetForActionXml.create(actionLayoutData, objectAction));
                 addFacetIfPresent(CssClassFaFacetForActionXml.create(actionLayoutData, objectAction));
-                addFacetIfPresent(DescribedAsFacetForActionXml.create(actionLayoutData, objectAction));
+                addFacetIfPresent(MemberDescribedFacetForActionXml.create(actionLayoutData, objectAction));
                 addFacetIfPresent(HiddenFacetForActionXml.create(actionLayoutData, objectAction));
                 addFacetIfPresent(MemberNamedFacetForActionXml.create(actionLayoutData, objectAction));
                 addFacetIfPresent(PromptStyleFacetForActionXml.create(actionLayoutData, objectAction));
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/MenuBarsLoaderServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/MenuBarsLoaderServiceDefault.java
index 8367f43..48c17ca 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/MenuBarsLoaderServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/MenuBarsLoaderServiceDefault.java
@@ -18,7 +18,10 @@
  */
 package org.apache.isis.core.runtimeservices.menubars;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.annotation.Priority;
 import javax.inject.Inject;
@@ -49,25 +52,42 @@ import lombok.extern.log4j.Log4j2;
 public class MenuBarsLoaderServiceDefault
 implements MenuBarsLoaderService {
 
-    private final IsisSystemEnvironment isisSystemEnvironment;
     private final JaxbService jaxbService;
-    private final AbstractResource menubarsLayoutXmlResource;
+    private final boolean supportsReloading;
+    private final AtomicReference<AbstractResource> menubarsLayoutXmlResourceRef;
 
     @Inject
     public MenuBarsLoaderServiceDefault(
             final IsisSystemEnvironment isisSystemEnvironment,
             final JaxbService jaxbService,
             final IsisConfiguration isisConfiguration) {
-        this.isisSystemEnvironment = isisSystemEnvironment;
         this.jaxbService = jaxbService;
+        this.supportsReloading = isisSystemEnvironment.isPrototyping();
 
-        this.menubarsLayoutXmlResource =
+        val menubarsLayoutXmlResource =
                 new ClassPathResource(isisConfiguration.getViewer().getWicket().getApplication().getMenubarsLayoutXml());
+        this.menubarsLayoutXmlResourceRef = new AtomicReference<>(menubarsLayoutXmlResource);
+    }
+
+    // JUnit support
+    public MenuBarsLoaderServiceDefault(
+            final JaxbService jaxbService,
+            final AtomicReference<AbstractResource> menubarsLayoutXmlResourceRef) {
+        this.jaxbService = jaxbService;
+        this.supportsReloading = true;
+
+        menubarsLayoutXmlResourceRef.getAndUpdate(r->r!=null
+                ? r
+                : new AbstractResource() {
+                    @Override public String getDescription() { return "Empty Resource"; }
+                    @Override public InputStream getInputStream() throws IOException { return null; }}
+                );
+        this.menubarsLayoutXmlResourceRef = menubarsLayoutXmlResourceRef;
     }
 
     @Override
     public boolean supportsReloading() {
-        return isisSystemEnvironment.isPrototyping();
+        return supportsReloading;
     }
 
     @Override
@@ -80,7 +100,7 @@ implements MenuBarsLoaderService {
         try {
             return jaxbService.fromXml(BS3MenuBars.class, xmlString);
         } catch (Exception e) {
-            severeCannotLoad(menubarsLayoutXmlResource, e);
+            severeCannotLoad(menubarsLayoutXmlResourceRef.get(), e);
             return null;
         }
     }
@@ -88,6 +108,8 @@ implements MenuBarsLoaderService {
     // -- HELPER
 
     private String loadMenubarsLayoutResource() {
+
+        val menubarsLayoutXmlResource = menubarsLayoutXmlResourceRef.get();
         try {
 
             if(!menubarsLayoutXmlResource.exists()) {
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 48b8336..84b7738 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
@@ -33,6 +33,7 @@ import javax.inject.Named;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 
+import org.apache.isis.applib.Identifier;
 import org.apache.isis.applib.annotation.DomainServiceLayout;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
@@ -108,7 +109,30 @@ implements MenuBarsService {
             return menuBarsFromAnnotationsOnly;
         }
 
-        // else load (and only fallback if nothing could be loaded)...
+        return menuBarsDefault();
+    }
+
+
+    @Override
+    public Optional<ServiceActionLayoutData> lookupLayout(Identifier serviceActionIdentifier) {
+        val actionId = serviceActionIdentifier.getLogicalTypeName()
+                + "#" + serviceActionIdentifier.getMemberLogicalName();
+
+        if(menuBars == null) {
+            menuBarsDefault();
+        }
+
+        val layoutData = serviceActionLayoutDataByActionId.get(actionId);
+        return Optional.ofNullable(layoutData);
+    }
+
+    // -- HELPER
+
+    private BS3MenuBars menuBarsDefault() {
+
+        val menuBarsFromAnnotationsOnly = this.menuBarsFromAnnotationsOnly.get();
+
+        // load (and only fallback if nothing could be loaded)...
         if(menuBars == null || menuBarsLoaderService.supportsReloading()) {
             this.menuBars = loadOrElse(menuBarsFromAnnotationsOnly);
         }
@@ -116,11 +140,12 @@ implements MenuBarsService {
         return menuBars;
     }
 
-    // -- HELPER
+    private final Map<String, ServiceActionLayoutData> serviceActionLayoutDataByActionId = _Maps.newHashMap();
 
     private BS3MenuBars loadOrElse(final BS3MenuBars menuBarsFromAnnotationsOnly) {
 
         val menuBars = Optional.ofNullable(menuBarsLoaderService.menuBars())
+                .map(this::updateActionLayoutLookupTable)
                 .map(this::addTnsAndSchemaLocation)
                 .orElse(menuBarsFromAnnotationsOnly);
 
@@ -160,6 +185,15 @@ implements MenuBarsService {
         return menuBars;
     }
 
+    private BS3MenuBars updateActionLayoutLookupTable(final BS3MenuBars menuBarsFromXml) {
+        serviceActionLayoutDataByActionId.clear();
+        menuBarsFromXml.visit(serviceActionLayoutData->
+            serviceActionLayoutDataByActionId.put(
+                    serviceActionLayoutData.getLogicalTypeNameAndId(),
+                    serviceActionLayoutData));
+        return menuBarsFromXml;
+    }
+
     private BS3MenuBars addTnsAndSchemaLocation(final BS3MenuBars menuBars) {
         menuBars.setTnsAndSchemaLocation(tnsAndSchemaLocation());
         return menuBars;
@@ -431,7 +465,5 @@ implements MenuBarsService {
     }
 
 
-
-
 }
 
diff --git a/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/RuntimeServicesTestAbstract.java b/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/RuntimeServicesTestAbstract.java
index 9f1a55d..f9afba4 100644
--- a/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/RuntimeServicesTestAbstract.java
+++ b/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/RuntimeServicesTestAbstract.java
@@ -18,8 +18,11 @@
  */
 package org.apache.isis.core.runtimeservices;
 
+import java.util.concurrent.atomic.AtomicReference;
+
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.springframework.core.io.AbstractResource;
 
 import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.services.menu.MenuBarsLoaderService;
@@ -49,21 +52,25 @@ implements HasMetaModelContext {
     @Getter(onMethod_ = {@Override})
     private MetaModelContext metaModelContext;
 
+    protected final AtomicReference<AbstractResource> menubarsLayoutXmlResourceRef =
+            new AtomicReference<>();
+
     @BeforeEach
     final void setUp() throws Exception {
         val mmcBuilder = MetaModelContext_forTesting.builder();
 
         // install runtime services into MMC (extend as needed)
 
+        onSetUp(mmcBuilder);
+
         mmcBuilder.singletonProvider(
                 _ManagedBeanAdapter
                 .forTestingLazy(MenuBarsLoaderService.class, ()->{
 
                     val jaxbService = getServiceRegistry().lookupServiceElseFail(JaxbService.class);
                     return new MenuBarsLoaderServiceDefault(
-                            getSystemEnvironment(),
                             jaxbService,
-                            getConfiguration());
+                            menubarsLayoutXmlResourceRef);
                 }));
 
 
@@ -83,20 +90,11 @@ implements HasMetaModelContext {
 
                     }));
 
-        mmcBuilder.singletonProvider(
-                _ManagedBeanAdapter
-                .forTestingLazy(MenuBarsLoaderService.class, ()->{
 
-                    val jaxbService = getServiceRegistry().lookupServiceElseFail(JaxbService.class);
-                    return new MenuBarsLoaderServiceDefault(
-                            getSystemEnvironment(),
-                            jaxbService,
-                            getConfiguration());
+        metaModelContext = mmcBuilder.build();
 
-                    }));
+        getConfiguration().getCore().getMetaModel().getIntrospector().setLockAfterFullIntrospection(false);
 
-        onSetUp(mmcBuilder);
-        metaModelContext = mmcBuilder.build();
         afterSetUp();
     }
 
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 ae36940..a6e7512 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
@@ -18,14 +18,15 @@
  */
 package org.apache.isis.core.runtimeservices.menubars.bootstrap3;
 
+import java.nio.charset.StandardCharsets;
+
 import org.junit.jupiter.api.Test;
+import org.springframework.core.io.ByteArrayResource;
 
 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.FacetUtil;
-import org.apache.isis.core.metamodel.facets.actions.layout.MemberNamedFacetForMenuBarEntry;
 import org.apache.isis.core.runtimeservices.RuntimeServicesTestAbstract;
 import org.apache.isis.core.runtimeservices.menubars.MenuBarsLoaderServiceDefault;
 
@@ -49,6 +50,9 @@ extends RuntimeServicesTestAbstract {
 
     @Override
     protected void afterSetUp() {
+
+        getConfiguration().getCore().getMetaModel().getIntrospector().setValidateIncrementally(false);
+
         layoutService = getServiceRegistry().lookupServiceElseFail(LayoutService.class);
         menuBarsService = (MenuBarsServiceBS3) getServiceRegistry().lookupServiceElseFail(MenuBarsService.class);
         menuBarsLoaderService = (MenuBarsLoaderServiceDefault) getServiceRegistry().lookupServiceElseFail(MenuBarsLoaderService.class);
@@ -99,7 +103,7 @@ extends RuntimeServicesTestAbstract {
         val customNamed = "Hello";
         val xml = sampleMenuBarsXmlWithCustomName(customNamed);
 
-        // after round-trip
+        // create menubars-xml from scratch (annotations and fallback naming only)
         val menuBars = menuBarsLoaderService.loadMenuBars(xml);
         assertNotNull(menuBars);
         assertEquals(1L, menuBars.stream().count());
@@ -108,18 +112,19 @@ extends RuntimeServicesTestAbstract {
         assertEquals(customNamed, layoutData.getNamed());
         assertEquals(null, layoutData.getNamedEscaped()); // deprecated: always escape
 
-        // verify action's member named facet is able to pick that up
         getSpecificationLoader().disposeMetaModel();
 
+        // load the modified menubars-xml with the MenuBarsLoaderService
+        super.menubarsLayoutXmlResourceRef.set(new ByteArrayResource(xml.getBytes(StandardCharsets.UTF_8)));
+
+        getSpecificationLoader().createMetaModel();
+
+        // verify service-action's member named facet was able to pick up the name from XML
+
         val serviceSpec = getSpecificationLoader().specForTypeElseFail(Bar.class);
         val objectAction = serviceSpec.getAction("createSimpleObject").orElse(null);
         assertNotNull(objectAction);
 
-        //TODO[ISIS-2787] we want this by the framework done internally, yet might require redesign of
-        // org.apache.isis.core.runtimeservices.menubars.bootstrap3.MenuBarsServiceBS3.menuBars(Type)
-        FacetUtil.addFacetIfPresent(MemberNamedFacetForMenuBarEntry
-                .create(layoutData, objectAction));
-
         assertEquals(customNamed, objectAction.getStaticFriendlyName().orElse(null));
     }