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 8a41ef59d7 ISIS-3272: separation of concerns: grid-loading vs 
grid-marshalling
8a41ef59d7 is described below

commit 8a41ef59d7350be28cd7f01e4b6e0fbd98d0d800
Author: Andi Huber <[email protected]>
AuthorDate: Sat Nov 5 12:43:09 2022 +0100

    ISIS-3272: separation of concerns: grid-loading vs grid-marshalling
---
 .../applib/services/grid/GridLoaderService.java    |  40 ++++--
 .../services/grid/GridMarshallerService.java       |  31 +++++
 .../causeway/applib/services/grid/GridService.java |   5 +-
 .../MarshallerService.java}                        |  22 ++-
 .../services/menu/MenuBarsLoaderService.java       |   4 +-
 .../services/menu/MenuBarsMarshallerService.java   |  34 +----
 .../applib/services/menu/MenuBarsService.java      |   6 -
 .../metamodel/CausewayModuleCoreMetamodel.java     |   4 +-
 .../_testing/MetaModelContext_forTesting.java      |  18 +--
 .../services/grid/GridLoaderServiceDefault.java    | 151 ++++++++++++---------
 .../services/grid/GridReaderUsingJaxb.java         |  93 -------------
 .../services/grid/GridServiceDefault.java          |  14 +-
 .../bootstrap/GridMarshallerServiceBootstrap.java  | 125 +++++++++++++++++
 .../grid/bootstrap/GridSystemServiceBootstrap.java |   7 +-
 .../services/layout/LayoutServiceDefault.java      |  58 +++-----
 .../GridLoaderServiceDefault_resourceNameTest.java |  19 +--
 .../metamodel/services/grid/GridLoadingTest.java   |   6 +-
 .../MenuBarsMarshallerServiceBootstrap.java        |   2 +-
 .../bootstrap/MenuBarsServiceBootstrap.java        |  17 +--
 19 files changed, 350 insertions(+), 306 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java
index 37833c16ee..a7aa795524 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java
@@ -18,15 +18,21 @@
  */
 package org.apache.causeway.applib.services.grid;
 
+import java.util.EnumSet;
+import java.util.Optional;
+
 import org.springframework.lang.Nullable;
 
 import org.apache.causeway.applib.layout.grid.Grid;
 import org.apache.causeway.applib.mixins.metamodel.Object_rebuildMetamodel;
+import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
+
+import lombok.NonNull;
 
 /**
  * Provides the ability to load the XML layout (grid) for a domain class.
  *
- * @since 1.x {@index}
+ * @since 1.x - revised for 2.0 {@index}
  */
 public interface GridLoaderService {
 
@@ -57,29 +63,33 @@ public interface GridLoaderService {
      *     to obtain a default grid for the domain class).
      * </p>
      */
-    boolean existsFor(Class<?> domainClass);
-
-    /**
-     * Returns a new instance of a {@link Grid} for the specified domain 
class, eg from a
-     * <code>layout.xml</code> file, else <code>null</code>.
-     */
-    default Grid load(final Class<?> domainClass) {
-        return load(domainClass, null);
-    }
+    boolean existsFor(Class<?> domainClass, EnumSet<CommonMimeType> 
supportedFormats);
 
     /**
-     * Loads a specific alternative {@link Grid} layout for the specified 
domain
-     * class.
-     *
+     * Optionally returns a new instance of a {@link Grid},
+     * based on whether the underlying resource could be found, loaded and 
parsed.
      * <p>
      *     The layout alternative will typically be specified through a
      *     `layout()` method on the domain object, the value of which is used
      *     for the suffix of the layout file (eg "Customer-layout.archived.xml"
      *     to use a different layout for customers that have been archived).
      * </p>
+     * @throws UnsupportedOperationException - when format is not supported
      */
-    Grid load(
+    <T extends Grid> Optional<T> load(
             Class<?> domainClass,
-            @Nullable String layout);
+            @Nullable String layoutIfAny,
+            @NonNull GridMarshallerService<T> marshaller);
+
+    /**
+     * Optionally returns a new instance of a {@link Grid},
+     * based on whether the underlying resource could be found, loaded and 
parsed.
+     * @throws UnsupportedOperationException - when format is not supported
+     */
+    default <T extends Grid> Optional<T> load(
+            final Class<?> domainClass,
+            final @NonNull GridMarshallerService<T> marshaller) {
+        return load(domainClass, null, marshaller);
+    }
 
 }
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshallerService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshallerService.java
new file mode 100644
index 0000000000..d864416e82
--- /dev/null
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshallerService.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 org.apache.causeway.applib.services.grid;
+
+import org.apache.causeway.applib.layout.grid.Grid;
+import org.apache.causeway.applib.services.marshal.MarshallerService;
+
+/**
+ * Supports {@link Grid} marshaling and unmarshaling.
+ *
+ * @since 2.0 {@index}
+ */
+public interface GridMarshallerService<T extends Grid>
+extends MarshallerService<T> {
+}
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java
index 62745e0839..ee1c042cae 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java
@@ -33,7 +33,6 @@ import 
org.apache.causeway.commons.internal.exceptions._Exceptions;
  */
 public interface GridService {
 
-
     /**
      * Whether dynamic reloading of layouts is enabled.
      *
@@ -156,6 +155,10 @@ public interface GridService {
      */
     Grid minimal(Grid grid);
 
+    // -- LAYOUT EXPORT
+
+    GridMarshallerService<? extends Grid> marshaller();
+
     default Grid toGridForExport(
             final Class<?> domainClass,
             final LayoutExportStyle style) {
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsMarshallerService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/marshal/MarshallerService.java
similarity index 65%
copy from 
api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsMarshallerService.java
copy to 
api/applib/src/main/java/org/apache/causeway/applib/services/marshal/MarshallerService.java
index 65e6a19fd1..18049ac32a 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsMarshallerService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/marshal/MarshallerService.java
@@ -16,46 +16,42 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.causeway.applib.services.menu;
+package org.apache.causeway.applib.services.marshal;
 
 import java.util.EnumSet;
 
 import org.springframework.lang.Nullable;
 
-import org.apache.causeway.applib.layout.menubars.MenuBars;
-import org.apache.causeway.applib.services.layout.LayoutService;
 import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.causeway.commons.functional.Try;
 
 import lombok.NonNull;
 
 /**
- * Supports {@link MenuBars} marshaling and unmarshaling.
- * <p>
- * The service is <i>called</i> by the default implementations of
- * {@link MenuBarsService} and {@link LayoutService}.
+ * Supports marshaling and unmarshaling of the generic type T
+ * for a set of mime types.
  *
  * @since 2.0 {@index}
  */
-public interface MenuBarsMarshallerService<T extends MenuBars> {
+public interface MarshallerService<T> {
 
-    Class<T> implementedMenuBarsClass();
+    Class<T> supportedClass();
 
     /**
      * Supported format(s) for {@link #unmarshal(String, CommonMimeType)}
-     * and {@link #marshal(MenuBars, CommonMimeType)}.
+     * and {@link #marshal(Object, CommonMimeType)}.
      */
     EnumSet<CommonMimeType> supportedFormats();
 
     /**
      * @throws UnsupportedOperationException when format is not supported
      */
-    String marshal(@NonNull T menuBars, @NonNull CommonMimeType format);
+    String marshal(@NonNull T value, @NonNull CommonMimeType format);
 
     /**
-     * Returns a new instance of a {@link MenuBars} wrapped in a {@link Try}.
+     * Returns a new de-serialized instance wrapped in a {@link Try}.
      * @throws UnsupportedOperationException when format is not supported (not 
wrapped)
      */
-    Try<T> unmarshal(@Nullable String layoutFileContent, @NonNull 
CommonMimeType format);
+    Try<T> unmarshal(@Nullable String content, @NonNull CommonMimeType format);
 
 }
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsLoaderService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsLoaderService.java
index 6faf1aff38..9b80bbb256 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsLoaderService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsLoaderService.java
@@ -52,8 +52,8 @@ public interface MenuBarsLoaderService {
     boolean supportsReloading();
 
     /**
-     * Returns a new instance of a {@link MenuBars} if possible,
-     * else <tt>Optional.empty()</tt>.
+     * Optionally returns a new instance of a {@link MenuBars},
+     * based on whether the underlying resource could be found, loaded and 
parsed.
      * @throws UnsupportedOperationException - when format is not supported
      */
     <T extends MenuBars> Optional<T> menuBars(@NonNull 
MenuBarsMarshallerService<T> marshaller);
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsMarshallerService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsMarshallerService.java
index 65e6a19fd1..838a6aa3fe 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsMarshallerService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsMarshallerService.java
@@ -18,44 +18,18 @@
  */
 package org.apache.causeway.applib.services.menu;
 
-import java.util.EnumSet;
-
-import org.springframework.lang.Nullable;
-
 import org.apache.causeway.applib.layout.menubars.MenuBars;
 import org.apache.causeway.applib.services.layout.LayoutService;
-import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
-import org.apache.causeway.commons.functional.Try;
-
-import lombok.NonNull;
+import org.apache.causeway.applib.services.marshal.MarshallerService;
 
 /**
  * Supports {@link MenuBars} marshaling and unmarshaling.
  * <p>
  * The service is <i>called</i> by the default implementations of
- * {@link MenuBarsService} and {@link LayoutService}.
+ * {@link MenuBarsService} and (indirectly) {@link LayoutService}.
  *
  * @since 2.0 {@index}
  */
-public interface MenuBarsMarshallerService<T extends MenuBars> {
-
-    Class<T> implementedMenuBarsClass();
-
-    /**
-     * Supported format(s) for {@link #unmarshal(String, CommonMimeType)}
-     * and {@link #marshal(MenuBars, CommonMimeType)}.
-     */
-    EnumSet<CommonMimeType> supportedFormats();
-
-    /**
-     * @throws UnsupportedOperationException when format is not supported
-     */
-    String marshal(@NonNull T menuBars, @NonNull CommonMimeType format);
-
-    /**
-     * Returns a new instance of a {@link MenuBars} wrapped in a {@link Try}.
-     * @throws UnsupportedOperationException when format is not supported (not 
wrapped)
-     */
-    Try<T> unmarshal(@Nullable String layoutFileContent, @NonNull 
CommonMimeType format);
-
+public interface MenuBarsMarshallerService<T extends MenuBars>
+extends MarshallerService<T> {
 }
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsService.java
index a23c26baf8..2711e8e371 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsService.java
@@ -19,7 +19,6 @@
 package org.apache.causeway.applib.services.menu;
 
 import org.apache.causeway.applib.layout.menubars.MenuBars;
-import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 
 /**
@@ -69,8 +68,6 @@ public interface MenuBarsService {
     // -- LAYOUT EXPORT
 
     MenuBarsMarshallerService<? extends MenuBars> marshaller();
-    String menuBarsFormatted(Type type, CommonMimeType format);
-
 
     // -- JUNIT SUPPORT
 
@@ -82,9 +79,6 @@ public interface MenuBarsService {
             @Override
             public MenuBarsMarshallerService<? extends MenuBars> marshaller() {
                 throw _Exceptions.unsupportedOperation(); }
-            @Override
-            public String menuBarsFormatted(final Type type, final 
CommonMimeType format) {
-                throw _Exceptions.unsupportedOperation(); }
         };
     }
 
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
index 18894ea9f9..03b9920e10 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
@@ -57,8 +57,8 @@ import 
org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstit
 import 
org.apache.causeway.core.metamodel.services.events.MetamodelEventService;
 import 
org.apache.causeway.core.metamodel.services.exceprecog.ExceptionRecognizerForRecoverableException;
 import 
org.apache.causeway.core.metamodel.services.grid.GridLoaderServiceDefault;
-import org.apache.causeway.core.metamodel.services.grid.GridReaderUsingJaxb;
 import org.apache.causeway.core.metamodel.services.grid.GridServiceDefault;
+import 
org.apache.causeway.core.metamodel.services.grid.bootstrap.GridMarshallerServiceBootstrap;
 import 
org.apache.causeway.core.metamodel.services.grid.bootstrap.GridSystemServiceBootstrap;
 import 
org.apache.causeway.core.metamodel.services.idstringifier.IdStringifierLookupService;
 import org.apache.causeway.core.metamodel.services.layout.LayoutServiceDefault;
@@ -182,7 +182,7 @@ import lombok.NonNull;
         MetamodelEventService.class,
         ExceptionRecognizerForRecoverableException.class,
         GridLoaderServiceDefault.class,
-        GridReaderUsingJaxb.class,
+        GridMarshallerServiceBootstrap.class,
         GridServiceDefault.class,
         GridSystemServiceBootstrap.class,
         LayoutServiceDefault.class,
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java
index d67682f95a..23205ff485 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java
@@ -34,6 +34,7 @@ import org.springframework.util.ClassUtils;
 
 import org.apache.causeway.applib.services.factory.FactoryService;
 import org.apache.causeway.applib.services.grid.GridLoaderService;
+import org.apache.causeway.applib.services.grid.GridMarshallerService;
 import org.apache.causeway.applib.services.grid.GridService;
 import org.apache.causeway.applib.services.i18n.TranslationService;
 import org.apache.causeway.applib.services.iactnlayer.InteractionContext;
@@ -87,8 +88,8 @@ import 
org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstit
 import 
org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
 import 
org.apache.causeway.core.metamodel.services.events.MetamodelEventService;
 import 
org.apache.causeway.core.metamodel.services.grid.GridLoaderServiceDefault;
-import org.apache.causeway.core.metamodel.services.grid.GridReaderUsingJaxb;
 import org.apache.causeway.core.metamodel.services.grid.GridServiceDefault;
+import 
org.apache.causeway.core.metamodel.services.grid.bootstrap.GridMarshallerServiceBootstrap;
 import 
org.apache.causeway.core.metamodel.services.grid.bootstrap.GridSystemServiceBootstrap;
 import org.apache.causeway.core.metamodel.services.layout.LayoutServiceDefault;
 import org.apache.causeway.core.metamodel.services.message.MessageServiceNoop;
@@ -440,17 +441,17 @@ implements MetaModelContext {
     }
 
     @Getter(lazy = true)
-    private final GridReaderUsingJaxb gridReader = createGridReader();
+    private final GridMarshallerService gridMarshallerService = 
createGridMarshallerService();
     //XXX lombok issue: won't compile if inlined
-    private final GridReaderUsingJaxb createGridReader() {
-        return new GridReaderUsingJaxb(getJaxbService(), getServiceRegistry());
+    private final GridMarshallerService createGridMarshallerService() {
+        return new GridMarshallerServiceBootstrap(getJaxbService());
     }
 
     @Getter(lazy = true)
     private final GridLoaderService gridLoaderService = 
createGridLoaderService();
     //XXX lombok issue: won't compile if inlined
     private final GridLoaderService createGridLoaderService() {
-        return new GridLoaderServiceDefault(getGridReader(), 
getMessageService(), /*support reloading*/true);
+        return new GridLoaderServiceDefault(getMessageService(), /*support 
reloading*/true);
     }
 
     @Getter(lazy = true)
@@ -458,14 +459,16 @@ implements MetaModelContext {
     //XXX lombok issue: won't compile if inlined
     private final GridService createGridService() {
         return new GridServiceDefault(
-            getGridLoaderService(), _Lists.of(
+            getGridLoaderService(),
+            getGridMarshallerService(),
+            _Lists.of(
                     new GridSystemServiceBootstrap(
                             getSpecificationLoader(),
                             getTranslationService(),
                             getJaxbService(),
                             getMessageService(),
                             getSystemEnvironment())
-                            .setGridReader(getGridReader())
+                            .setMarshaller(getGridMarshallerService())
                     )); // support reloading
     }
 
@@ -475,7 +478,6 @@ implements MetaModelContext {
     private final LayoutService createLayoutService() {
         return new LayoutServiceDefault(
                 getSpecificationLoader(),
-                getJaxbService(),
                 getGridService(),
                 getMenuBarsService());
     }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java
index 5de8bec20d..a1dd8461bc 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java
@@ -19,6 +19,7 @@
 package org.apache.causeway.core.metamodel.services.grid;
 
 import java.io.IOException;
+import java.util.EnumSet;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -35,7 +36,9 @@ import org.springframework.stereotype.Service;
 import org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.layout.grid.Grid;
 import org.apache.causeway.applib.services.grid.GridLoaderService;
+import org.apache.causeway.applib.services.grid.GridMarshallerService;
 import org.apache.causeway.applib.services.message.MessageService;
+import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.collections._Maps;
 import org.apache.causeway.commons.internal.reflection._Reflect;
@@ -44,10 +47,12 @@ import 
org.apache.causeway.commons.internal.resources._Resources;
 import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
 import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
 
+import lombok.Getter;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.Value;
 import lombok.val;
+import lombok.experimental.Accessors;
 import lombok.extern.log4j.Log4j2;
 
 @Service
@@ -58,49 +63,35 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 public class GridLoaderServiceDefault implements GridLoaderService {
 
-    private final GridReaderUsingJaxb gridReader;
     private final MessageService messageService;
+    @Getter(onMethod_={@Override}) @Accessors(fluent = true)
     private final boolean supportsReloading;
 
     @Inject
     public GridLoaderServiceDefault(
-           final GridReaderUsingJaxb gridReader,
            final MessageService messageService,
            final CausewaySystemEnvironment causewaySystemEnvironment) {
-        this.gridReader = gridReader;
         this.messageService = messageService;
         this.supportsReloading = causewaySystemEnvironment.isPrototyping();
     }
 
     @Value
-    static class DomainClassAndLayout {
-        private final Class<?> domainClass;
-        private final String layoutIfAny;
+    static class LayoutKey {
+        private final @NonNull Class<?> domainClass;
+        private final @Nullable String layoutIfAny; // layout suffix
     }
 
     @Value
-    static class XmlAndResourceName {
-        private final @NonNull String xmlContent;
+    static class LayoutResource {
         private final @NonNull String resourceName;
+        private final @NonNull CommonMimeType format;
+        private final @NonNull String content;
     }
 
-
     // for better logging messages (used only in prototyping mode)
-    private final Map<DomainClassAndLayout, String> 
badXmlByDomainClassAndLayout = _Maps.newHashMap();
-
-    @Value
-    static class DomainClassAndLayoutAndXml {
-        private final DomainClassAndLayout domainClassAndLayout;
-        private final XmlAndResourceName xml;
-    }
-
+    private final Map<LayoutKey, String> badContentByKey = _Maps.newHashMap();
     // cache (used only in prototyping mode)
-    private final Map<DomainClassAndLayoutAndXml, Grid> 
gridByDomainClassAndLayoutAndXml = _Maps.newHashMap();
-
-    @Override
-    public boolean supportsReloading() {
-        return supportsReloading;//causewaySystemEnvironment.isPrototyping();
-    }
+    private final Map<LayoutKey, Grid> gridCache = _Maps.newHashMap();
 
     @Override
     public void remove(final Class<?> domainClass) {
@@ -108,90 +99,96 @@ public class GridLoaderServiceDefault implements 
GridLoaderService {
             return;
         }
         final String layoutIfAny = null;
-        final DomainClassAndLayout dcal = new 
DomainClassAndLayout(domainClass, layoutIfAny);
-        badXmlByDomainClassAndLayout.remove(dcal);
-        final XmlAndResourceName xml = loadXml(dcal).orElse(null);
-        if(xml == null) {
-            return;
-        }
-        gridByDomainClassAndLayoutAndXml.remove(new 
DomainClassAndLayoutAndXml(dcal, xml));
+        val layoutKey = new LayoutKey(domainClass, layoutIfAny);
+        badContentByKey.remove(layoutKey);
+        gridCache.remove(layoutKey);
     }
 
     @Override
-    public boolean existsFor(final Class<?> domainClass) {
-        return loadXml(new DomainClassAndLayout(domainClass, 
null)).isPresent();
+    public boolean existsFor(final Class<?> domainClass, final 
EnumSet<CommonMimeType> supportedFormats) {
+        return loadLayoutResource(new LayoutKey(domainClass, null), 
supportedFormats).isPresent();
     }
 
     @Override
-    public Grid load(final Class<?> domainClass, final String layoutIfAny) {
-        final DomainClassAndLayout dcal = new 
DomainClassAndLayout(domainClass, layoutIfAny);
-        final XmlAndResourceName xml = loadXml(dcal).orElse(null);
-        if(xml == null) {
-            return null;
+    public <T extends Grid> Optional<T> load(
+            final Class<?> domainClass,
+            final String layoutIfAny,
+            final @NonNull GridMarshallerService<T> marshaller) {
+
+        val supportedFormats = marshaller.supportedFormats();
+
+        val layoutKey = new LayoutKey(domainClass, layoutIfAny);
+        val layoutResource = loadLayoutResource(layoutKey, 
supportedFormats).orElse(null);
+        if(layoutResource == null) {
+            return Optional.empty();
         }
 
-        final DomainClassAndLayoutAndXml dcalax = new 
DomainClassAndLayoutAndXml(dcal, xml);
         if(supportsReloading()) {
-            final String badXml = badXmlByDomainClassAndLayout.get(dcal);
-            if(badXml != null) {
-                if(Objects.equals(xml.getXmlContent(), badXml)) {
+            final String badContent = badContentByKey.get(layoutKey);
+            if(badContent != null) {
+                if(Objects.equals(layoutResource.getContent(), badContent)) {
                     // seen this before and already logged; just quit
-                    return null;
+                    return Optional.empty();
                 } else {
-                    // this different XML might be good
-                    badXmlByDomainClassAndLayout.remove(dcal);
+                    // this different content might be good
+                    badContentByKey.remove(layoutKey);
                 }
             }
         } else {
             // if cached, serve from cache - otherwise fall through
-            final Grid grid = gridByDomainClassAndLayoutAndXml.get(dcalax);
+            final Grid grid = gridCache.get(layoutKey);
             if(grid != null) {
-                return grid;
+                return Optional.of((T)grid);
             }
         }
 
         try {
-            final Grid grid = gridReader.loadGrid(xml.getXmlContent());
+            final T grid = marshaller
+                    .unmarshal(layoutResource.getContent(), 
layoutResource.getFormat())
+                    .getValue().orElseThrow();
             grid.setDomainClass(domainClass);
             if(supportsReloading()) {
-                gridByDomainClassAndLayoutAndXml.put(dcalax, grid);
+                gridCache.put(layoutKey, grid);
             }
-            return grid;
+            return Optional.of(grid);
         } catch(Exception ex) {
 
             if(supportsReloading()) {
-                // save fact that this was bad XML, so that we don't log again 
if called next time
-                badXmlByDomainClassAndLayout.put(dcal, xml.getXmlContent());
+                // save fact that this was bad content, so that we don't log 
again if called next time
+                badContentByKey.put(layoutKey, layoutResource.getContent());
             }
 
             // note that we don't blacklist if the file exists but couldn't be 
parsed;
             // the developer might fix so we will want to retry.
-            final String resourceName = xml.getResourceName();
+            final String resourceName = layoutResource.getResourceName();
             final String message = "Failed to parse " + resourceName + " file 
(" + ex.getMessage() + ")";
             if(supportsReloading()) {
                 messageService.warnUser(message);
             }
             log.warn(message);
 
-            return null;
+            return Optional.empty();
         }
     }
 
     // -- HELPER
 
-    Optional<XmlAndResourceName> loadXml(final DomainClassAndLayout dcal) {
+    Optional<LayoutResource> loadLayoutResource(
+            final LayoutKey dcal,
+            final EnumSet<CommonMimeType> supportedFormats) {
         return _Reflect.streamTypeHierarchy(dcal.getDomainClass(), 
InterfacePolicy.EXCLUDE)
-        .map(type->loadXml(type, dcal.getLayoutIfAny()))
+        .map(type->loadContent(type, dcal.getLayoutIfAny(), supportedFormats))
         .filter(Optional::isPresent)
         .findFirst()
         .map(Optional::get);
     }
 
-    private Optional<XmlAndResourceName> loadXml(
+    private Optional<LayoutResource> loadContent(
             final @NonNull Class<?> domainClass,
-            final @Nullable String layoutIfAny) {
-        return streamResourceNameCandidatesFor(domainClass, layoutIfAny)
-        .map(candidateResourceName->tryLoadXml(domainClass, 
candidateResourceName))
+            final @Nullable String layoutIfAny,
+            final EnumSet<CommonMimeType> supportedFormats) {
+        return streamResourceNameCandidatesFor(domainClass, layoutIfAny, 
supportedFormats)
+        .map(candidateResourceName->tryLoadLayoutResource(domainClass, 
candidateResourceName))
         .filter(Optional::isPresent)
         .findFirst()
         .map(Optional::get);
@@ -199,27 +196,47 @@ public class GridLoaderServiceDefault implements 
GridLoaderService {
 
     private Stream<String> streamResourceNameCandidatesFor(
             final @NonNull Class<?> domainClass,
-            final @Nullable String layoutIfAny) {
+            final @Nullable String layoutIfAny,
+            final @NonNull  EnumSet<CommonMimeType> supportedFormats) {
+        return supportedFormats.stream()
+                .flatMap(format->streamResourceNameCandidatesFor(domainClass, 
layoutIfAny, format));
+    }
+
+    private Stream<String> streamResourceNameCandidatesFor(
+            final @NonNull Class<?> domainClass,
+            final @Nullable String layoutIfAny,
+            final @NonNull CommonMimeType format) {
+        return format.getProposedFileExtensions().stream()
+                
.flatMap(fileExtension->streamResourceNameCandidatesFor(domainClass, 
layoutIfAny, fileExtension));
+    }
+
+    private Stream<String> streamResourceNameCandidatesFor(
+            final @NonNull Class<?> domainClass,
+            final @Nullable String layoutIfAny,
+            final @NonNull String fileExtension) {
 
         val typeSimpleName = domainClass.getSimpleName();
 
         return _Strings.isNotEmpty(layoutIfAny)
                 ? Stream.of(
-                        String.format("%s-%s.layout.xml", typeSimpleName, 
layoutIfAny),
-                        String.format("%s.layout.xml", typeSimpleName),
-                        String.format("%s.layout.fallback.xml", 
typeSimpleName))
+                        String.format("%s-%s.layout.%s", typeSimpleName, 
layoutIfAny, fileExtension),
+                        String.format("%s.layout.%s", typeSimpleName, 
fileExtension),
+                        String.format("%s.layout.fallback.%s", typeSimpleName, 
fileExtension))
                 : Stream.of(
-                        String.format("%s.layout.xml", typeSimpleName),
-                        String.format("%s.layout.fallback.xml", 
typeSimpleName));
+                        String.format("%s.layout.%s", typeSimpleName, 
fileExtension),
+                        String.format("%s.layout.fallback.%s", 
typeSimpleName,fileExtension));
     }
 
-    private Optional<XmlAndResourceName> tryLoadXml(
+    private Optional<LayoutResource> tryLoadLayoutResource(
             final @NonNull Class<?> type,
             final @NonNull String candidateResourceName) {
         try {
             return Optional.ofNullable(
                     _Resources.loadAsStringUtf8(type, candidateResourceName))
-                    .map(xml->new XmlAndResourceName(xml, 
candidateResourceName));
+                    .map(fileContent->new LayoutResource(
+                            candidateResourceName,
+                            
CommonMimeType.valueOfFileName(candidateResourceName).orElseThrow(),
+                            fileContent));
         } catch (IOException ex) {
             log.error(
                     "Failed to load layout file {} (relative to {}.class)",
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridReaderUsingJaxb.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridReaderUsingJaxb.java
deleted file mode 100644
index 079f1d3a7d..0000000000
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridReaderUsingJaxb.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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 org.apache.causeway.core.metamodel.services.grid;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.Priority;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
-
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Service;
-
-import org.apache.causeway.applib.annotation.PriorityPrecedence;
-import org.apache.causeway.applib.layout.grid.Grid;
-import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid;
-import org.apache.causeway.applib.services.grid.GridSystemService;
-import org.apache.causeway.applib.services.jaxb.JaxbService;
-import org.apache.causeway.applib.services.registry.ServiceRegistry;
-import org.apache.causeway.commons.internal.collections._Arrays;
-import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
-
-import lombok.RequiredArgsConstructor;
-
-/**
- *
- * @since 2.0
- *
- */
-@Service
-@Named(CausewayModuleCoreMetamodel.NAMESPACE + ".GridReaderUsingJaxb")
-@Priority(PriorityPrecedence.MIDPOINT)
-@Qualifier("Default")
-@RequiredArgsConstructor(onConstructor_ = {@Inject})
-public class GridReaderUsingJaxb {
-
-    private final JaxbService jaxbService;
-    private final ServiceRegistry serviceRegistry;
-
-    private JAXBContext jaxbContext;
-
-    @PostConstruct
-    public void init(){
-
-        final Class<?>[] pageImplementations =
-            serviceRegistry.select(GridSystemService.class)
-            .stream()
-            .map(GridSystemService::gridImplementation)
-            .collect(_Arrays.toArray(Class.class));
-
-        try {
-            jaxbContext = JAXBContext.newInstance(pageImplementations);
-        } catch (JAXBException e) {
-            // leave as null
-        }
-    }
-
-    public Grid loadGrid(final String xml) {
-        initForTesting(); // a no-op if already initialized via @PostConstruct
-        return (Grid) jaxbService.fromXml(jaxbContext, xml);
-    }
-
-    // -- HELPER
-
-    // JUnit support
-    private void initForTesting() {
-        if(jaxbContext==null) {
-            try {
-                jaxbContext = JAXBContext.newInstance(BSGrid.class);
-            } catch (JAXBException e) {
-                // leave as null
-            }
-        }
-    }
-
-}
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java
index cec8609659..c8dae8e3d3 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java
@@ -32,6 +32,7 @@ import org.springframework.stereotype.Service;
 import org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.layout.grid.Grid;
 import org.apache.causeway.applib.services.grid.GridLoaderService;
+import org.apache.causeway.applib.services.grid.GridMarshallerService;
 import org.apache.causeway.applib.services.grid.GridService;
 import org.apache.causeway.applib.services.grid.GridSystemService;
 import org.apache.causeway.commons.internal.base._Casts;
@@ -39,8 +40,10 @@ import 
org.apache.causeway.commons.internal.collections._Lists;
 import org.apache.causeway.commons.internal.collections._Sets;
 import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
 
+import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import lombok.val;
+import lombok.experimental.Accessors;
 
 @Service
 @Named(CausewayModuleCoreMetamodel.NAMESPACE + ".GridServiceDefault")
@@ -56,6 +59,8 @@ public class GridServiceDefault implements GridService {
     public static final String LINKS_SCHEMA_LOCATION = 
"http://causeway.apache.org/applib/layout/links/links.xsd";;
 
     private final GridLoaderService gridLoaderService;
+    @Getter(onMethod_={@Override}) @Accessors(fluent = true)
+    private final GridMarshallerService<? extends Grid> marshaller;
     private final List<GridSystemService<? extends Grid>> gridSystemServices;
 
     // //////////////////////////////////////
@@ -72,17 +77,17 @@ public class GridServiceDefault implements GridService {
 
     @Override
     public boolean existsFor(final Class<?> domainClass) {
-        return gridLoaderService.existsFor(domainClass);
+        return gridLoaderService.existsFor(domainClass, 
marshaller.supportedFormats());
     }
 
     @Override
     public Grid load(final Class<?> domainClass) {
-        return gridLoaderService.load(domainClass);
+        return gridLoaderService.load(domainClass, marshaller).orElse(null);
     }
 
     @Override
     public Grid load(final Class<?> domainClass, final String layout) {
-        return gridLoaderService.load(domainClass, layout);
+        return gridLoaderService.load(domainClass, layout, 
marshaller).orElse(null);
     }
 
     // //////////////////////////////////////
@@ -203,7 +208,4 @@ public class GridServiceDefault implements GridService {
                 : gridSystemServicesForTest;
     }
 
-
-
-
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridMarshallerServiceBootstrap.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridMarshallerServiceBootstrap.java
new file mode 100644
index 0000000000..f5df623423
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridMarshallerServiceBootstrap.java
@@ -0,0 +1,125 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 org.apache.causeway.core.metamodel.services.grid.bootstrap;
+
+import java.util.EnumSet;
+import java.util.Objects;
+
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+import org.apache.causeway.applib.annotation.PriorityPrecedence;
+import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid;
+import org.apache.causeway.applib.services.grid.GridMarshallerService;
+import org.apache.causeway.applib.services.jaxb.JaxbService;
+import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
+import org.apache.causeway.commons.functional.Try;
+import org.apache.causeway.commons.internal.base._Lazy;
+import org.apache.causeway.commons.internal.collections._Maps;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
+import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.experimental.Accessors;
+
+/**
+ * @since 2.0
+ */
+@Service
+@Named(CausewayModuleCoreMetamodel.NAMESPACE + 
".GridMarshallerServiceBootstrap")
+@Priority(PriorityPrecedence.MIDPOINT)
+@Qualifier("Default")
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+//@Log4j2
+public class GridMarshallerServiceBootstrap
+implements GridMarshallerService<BSGrid> {
+
+    private final JaxbService jaxbService;
+//    private final ServiceRegistry serviceRegistry;
+
+    @Getter(onMethod_={@Override}) @Accessors(fluent = true)
+    private final EnumSet<CommonMimeType> supportedFormats =
+        EnumSet.of(CommonMimeType.XML);
+
+    @Override
+    public Class<BSGrid> supportedClass() {
+        return BSGrid.class;
+    }
+
+    @Override
+    public String marshal(final @NonNull BSGrid grid, @NonNull final 
CommonMimeType format) {
+        throwIfFormatNotSupported(format);
+        switch(format) {
+        case XML:{
+            return jaxbService.toXml(grid,
+                    _Maps.unmodifiable(
+                            Marshaller.JAXB_SCHEMA_LOCATION,
+                            
Objects.requireNonNull(grid.getTnsAndSchemaLocation())
+                            ));
+        }
+        default:
+            throw _Exceptions.unsupportedOperation("supported format %s is not 
implemented", format.name());
+        }
+    }
+
+    @Override
+    public Try<BSGrid> unmarshal(final String content, @NonNull final 
CommonMimeType format) {
+        throwIfFormatNotSupported(format);
+        switch(format) {
+        case XML:{
+            return Try.call(()->(BSGrid)jaxbService.fromXml(jaxbContext.get(), 
content));
+        }
+        default:
+            throw _Exceptions.unsupportedOperation("supported format %s is not 
implemented", format.name());
+        }
+    }
+
+    // -- HELPER
+
+    private void throwIfFormatNotSupported(final CommonMimeType format) {
+        if(!supportedFormats().contains(format)) {
+            throw _Exceptions.unsupportedOperation("object layout file format 
%s not supported", format.name());
+        }
+    }
+
+    /** registers all discovered grid types */
+    private final _Lazy<JAXBContext> jaxbContext = 
_Lazy.threadSafe(this::createJaxbContext);
+    @SneakyThrows
+    private JAXBContext createJaxbContext() {
+//        final Class<?>[] supportedGridTypes =
+//                serviceRegistry.select(GridSystemService.class)
+//                .stream()
+//                .map(GridSystemService::gridImplementation)
+//                .collect(Can.toCan())
+//                .distinct()
+//                .stream()
+//                .collect(_Arrays.toArray(Class.class));
+        return JAXBContext.newInstance(BSGrid.class);
+    }
+
+}
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java
index 5017c20502..0622bbbd78 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java
@@ -51,9 +51,11 @@ import 
org.apache.causeway.applib.layout.grid.bootstrap.BSRow;
 import org.apache.causeway.applib.layout.grid.bootstrap.BSTab;
 import org.apache.causeway.applib.layout.grid.bootstrap.BSTabGroup;
 import org.apache.causeway.applib.layout.grid.bootstrap.Size;
+import org.apache.causeway.applib.services.grid.GridMarshallerService;
 import org.apache.causeway.applib.services.i18n.TranslationService;
 import org.apache.causeway.applib.services.jaxb.JaxbService;
 import org.apache.causeway.applib.services.message.MessageService;
+import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.commons.internal.collections._Lists;
 import org.apache.causeway.commons.internal.collections._Maps;
@@ -66,7 +68,6 @@ import 
org.apache.causeway.core.metamodel.facets.actions.position.ActionPosition
 import 
org.apache.causeway.core.metamodel.facets.members.layout.group.GroupIdAndName;
 import 
org.apache.causeway.core.metamodel.facets.members.layout.group.LayoutGroupFacet;
 import 
org.apache.causeway.core.metamodel.layout.LayoutFacetUtil.LayoutDataFactory;
-import org.apache.causeway.core.metamodel.services.grid.GridReaderUsingJaxb;
 import 
org.apache.causeway.core.metamodel.services.grid.GridSystemServiceAbstract;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
@@ -97,7 +98,7 @@ extends GridSystemServiceAbstract<BSGrid> {
 
     @Inject @Lazy // circular dependency (late binding)
     @Setter @Accessors(chain = true) // JUnit support
-    private GridReaderUsingJaxb gridReader;
+    private GridMarshallerService<BSGrid> marshaller;
 
     @Inject
     public GridSystemServiceBootstrap(
@@ -131,7 +132,7 @@ extends GridSystemServiceAbstract<BSGrid> {
         try {
             final String content = _Resources.loadAsStringUtf8(getClass(), 
"GridFallbackLayout.xml");
             return Optional.ofNullable(content)
-                    .map(xml -> gridReader.loadGrid(xml))
+                    .map(xml -> marshaller.unmarshal(xml, 
CommonMimeType.XML).getValue().orElse(null))
                     .filter(BSGrid.class::isInstance)
                     .map(BSGrid.class::cast)
                     .map(bsGrid -> withDomainClass(bsGrid, domainClass))
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/layout/LayoutServiceDefault.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/layout/LayoutServiceDefault.java
index 6e9366b471..e9e2893e30 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/layout/LayoutServiceDefault.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/layout/LayoutServiceDefault.java
@@ -20,12 +20,10 @@ package org.apache.causeway.core.metamodel.services.layout;
 
 import java.io.File;
 import java.util.EnumSet;
-import java.util.Objects;
 
 import javax.annotation.Priority;
 import javax.inject.Inject;
 import javax.inject.Named;
-import javax.xml.bind.Marshaller;
 
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.lang.Nullable;
@@ -34,15 +32,13 @@ import org.springframework.stereotype.Service;
 import org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.layout.grid.Grid;
 import org.apache.causeway.applib.services.grid.GridService;
-import org.apache.causeway.applib.services.jaxb.JaxbService;
 import org.apache.causeway.applib.services.layout.LayoutExportStyle;
 import org.apache.causeway.applib.services.layout.LayoutService;
 import org.apache.causeway.applib.services.menu.MenuBarsService;
 import org.apache.causeway.applib.util.ZipWriter;
 import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.causeway.commons.functional.Try;
-import org.apache.causeway.commons.internal.collections._Maps;
-import org.apache.causeway.commons.internal.exceptions._Exceptions;
+import org.apache.causeway.commons.internal.base._Casts;
 import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
@@ -60,21 +56,37 @@ import lombok.extern.log4j.Log4j2;
 public class LayoutServiceDefault implements LayoutService {
 
     private final SpecificationLoader specificationLoader;
-    private final JaxbService jaxbService;
     private final GridService gridService;
     private final MenuBarsService menuBarsService;
 
+    // -- MENUBARS LAYOUT
+
+    @Override
+    public EnumSet<CommonMimeType> supportedMenuBarsLayoutFormats() {
+        return menuBarsService.marshaller().supportedFormats();
+    }
+
+    @Override
+    public String menuBarsLayout(
+            final MenuBarsService.Type type,
+            final CommonMimeType format) {
+        val menuBars = menuBarsService.menuBars(type);
+        return 
menuBarsService.marshaller().marshal(_Casts.uncheckedCast(menuBars), format);
+    }
+
     // -- OBJECT LAYOUT
 
     @Override
     public EnumSet<CommonMimeType> supportedObjectLayoutFormats() {
-        return EnumSet.of(CommonMimeType.XML);
+        return gridService.marshaller().supportedFormats();
     }
 
     @Override
     public String objectLayout(final Class<?> domainClass, final 
LayoutExportStyle style, final CommonMimeType format) {
-        final Grid grid = gridService.toGridForExport(domainClass, style);
-        return gridToFormatted(grid, format);
+        return tryGridToFormatted(domainClass, style, format)
+                .ifFailureFail()
+                .getValue()
+                .orElse(null);
     }
 
     @Override
@@ -105,20 +117,6 @@ public class LayoutServiceDefault implements LayoutService 
{
         return zipWriter.toBytes();
     }
 
-    // -- MENUBARS LAYOUT
-
-    @Override
-    public EnumSet<CommonMimeType> supportedMenuBarsLayoutFormats() {
-        return menuBarsService.marshaller().supportedFormats();
-    }
-
-    @Override
-    public String menuBarsLayout(
-            final MenuBarsService.Type type,
-            final CommonMimeType format) {
-        return menuBarsService.menuBarsFormatted(type, format);
-    }
-
     // -- HELPER
 
     private Try<String> tryGridToFormatted(
@@ -133,17 +131,7 @@ public class LayoutServiceDefault implements LayoutService 
{
         if(grid==null) {
             return null;
         }
-        switch(format) {
-        case XML:{
-            return jaxbService.toXml(grid,
-                    _Maps.unmodifiable(
-                            Marshaller.JAXB_SCHEMA_LOCATION,
-                            
Objects.requireNonNull(grid.getTnsAndSchemaLocation())
-                            ));
-        }
-        default:
-            throw _Exceptions.unsupportedOperation("format %s not supported", 
format.name());
-        }
+        return gridService.marshaller().marshal(_Casts.uncheckedCast(grid), 
format);
     }
 
     private static String zipEntryNameFor(
@@ -155,6 +143,4 @@ public class LayoutServiceDefault implements LayoutService {
                 + format.getProposedFileExtensions().getFirstOrFail();
     }
 
-
-
 }
diff --git 
a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault_resourceNameTest.java
 
b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault_resourceNameTest.java
index 12487b5ee7..6305cdab0c 100644
--- 
a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault_resourceNameTest.java
+++ 
b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault_resourceNameTest.java
@@ -18,12 +18,15 @@
  */
 package org.apache.causeway.core.metamodel.services.grid;
 
+import java.util.EnumSet;
+
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import 
org.apache.causeway.core.metamodel.services.grid.GridLoaderServiceDefault.DomainClassAndLayout;
+import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
+import 
org.apache.causeway.core.metamodel.services.grid.GridLoaderServiceDefault.LayoutKey;
 
 class GridLoaderServiceDefault_resourceNameTest {
 
@@ -31,41 +34,41 @@ class GridLoaderServiceDefault_resourceNameTest {
 
     @BeforeEach
     void setUp() throws Exception {
-        gridLoaderServiceDefault = new GridLoaderServiceDefault(null, null, 
false);
+        gridLoaderServiceDefault = new GridLoaderServiceDefault(null, false);
     }
 
     @Test
     void when_default_exists() {
         assertEquals(
                 "Foo.layout.xml",
-                resourceNameFor(new 
GridLoaderServiceDefault.DomainClassAndLayout(Foo.class, null)));
+                resourceNameFor(new 
GridLoaderServiceDefault.LayoutKey(Foo.class, null)));
     }
 
     @Test
     void when_fallback_exists() {
         assertEquals(
                 "Foo2.layout.fallback.xml",
-                resourceNameFor(new 
GridLoaderServiceDefault.DomainClassAndLayout(Foo2.class, null)));
+                resourceNameFor(new 
GridLoaderServiceDefault.LayoutKey(Foo2.class, null)));
     }
 
     @Test
     void when_default_and_fallback_both_exist() {
         assertEquals(
                 "Foo3.layout.xml",
-                resourceNameFor(new 
GridLoaderServiceDefault.DomainClassAndLayout(Foo3.class, null)));
+                resourceNameFor(new 
GridLoaderServiceDefault.LayoutKey(Foo3.class, null)));
     }
 
     @Test
     void when_neither_exist() {
         assertEquals(
                 (String)null,
-                resourceNameFor(new 
GridLoaderServiceDefault.DomainClassAndLayout(Foo4.class, null)));
+                resourceNameFor(new 
GridLoaderServiceDefault.LayoutKey(Foo4.class, null)));
     }
 
     // -- HELPER
 
-    private String resourceNameFor(DomainClassAndLayout dcal) {
-        return gridLoaderServiceDefault.loadXml(dcal)
+    private String resourceNameFor(final LayoutKey dcal) {
+        return gridLoaderServiceDefault.loadLayoutResource(dcal, 
EnumSet.of(CommonMimeType.XML))
         .map(xml->xml.getResourceName())
         .orElse(null);
     }
diff --git 
a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingTest.java
 
b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingTest.java
index b51142dc6f..f772b65655 100644
--- 
a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingTest.java
+++ 
b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingTest.java
@@ -18,6 +18,8 @@
  */
 package org.apache.causeway.core.metamodel.services.grid;
 
+import java.util.EnumSet;
+
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
@@ -53,8 +55,8 @@ extends MetaModelTestAbstract {
 
     @Test @Disabled("just a blueprint")
     void blueprint() {
-        val domainClassAndLayout = new 
GridLoaderServiceDefault.DomainClassAndLayout(Bar.class, null);
-        gridLoaderService.loadXml(domainClassAndLayout);
+        val domainClassAndLayout = new 
GridLoaderServiceDefault.LayoutKey(Bar.class, null);
+        gridLoaderService.loadLayoutResource(domainClassAndLayout, 
EnumSet.of(CommonMimeType.XML));
 
         val xml = layoutService.objectLayout(Bar.class, 
LayoutExportStyle.MINIMAL, CommonMimeType.XML);
         System.out.println(xml);
diff --git 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsMarshallerServiceBootstrap.java
 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsMarshallerServiceBootstrap.java
index 732356f9f8..e6c63da24d 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsMarshallerServiceBootstrap.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsMarshallerServiceBootstrap.java
@@ -65,7 +65,7 @@ implements MenuBarsMarshallerService<BSMenuBars> {
     }
 
     @Override
-    public Class<BSMenuBars> implementedMenuBarsClass() {
+    public Class<BSMenuBars> supportedClass() {
         return BSMenuBars.class;
     }
 
diff --git 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBootstrap.java
 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBootstrap.java
index 5c524c3c20..502e226ecd 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBootstrap.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBootstrap.java
@@ -36,7 +36,6 @@ import 
org.apache.causeway.applib.annotation.DomainServiceLayout;
 import org.apache.causeway.applib.annotation.NatureOfService;
 import org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.layout.component.ServiceActionLayoutData;
-import org.apache.causeway.applib.layout.menubars.MenuBars;
 import org.apache.causeway.applib.layout.menubars.bootstrap.BSMenu;
 import org.apache.causeway.applib.layout.menubars.bootstrap.BSMenuBar;
 import org.apache.causeway.applib.layout.menubars.bootstrap.BSMenuBars;
@@ -46,7 +45,6 @@ import 
org.apache.causeway.applib.services.menu.MenuBarsLoaderService;
 import org.apache.causeway.applib.services.menu.MenuBarsMarshallerService;
 import org.apache.causeway.applib.services.menu.MenuBarsService;
 import org.apache.causeway.applib.services.message.MessageService;
-import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.internal.base._Lazy;
 import org.apache.causeway.commons.internal.base._Strings;
@@ -77,8 +75,10 @@ import 
org.apache.causeway.core.metamodel.spec.feature.MixedIn;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
 import 
org.apache.causeway.core.runtimeservices.CausewayModuleCoreRuntimeServices;
 
+import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import lombok.val;
+import lombok.experimental.Accessors;
 import lombok.extern.log4j.Log4j2;
 
 @Service
@@ -100,6 +100,8 @@ implements MenuBarsService {
     public static final String LINKS_SCHEMA_LOCATION = 
GridServiceDefault.LINKS_SCHEMA_LOCATION;
 
     private final MenuBarsLoaderService loader;
+
+    @Getter(onMethod_={@Override}) @Accessors(fluent = true)
     private final MenuBarsMarshallerService<BSMenuBars> marshaller;
     private final MessageService messageService;
     private final JaxbService jaxbService;
@@ -119,17 +121,6 @@ implements MenuBarsService {
         return menuBarsDefault();
     }
 
-    @Override
-    public String menuBarsFormatted(final Type type, final CommonMimeType 
format) {
-        val menuBars = menuBars(type);
-        return marshaller.marshal(menuBars, format);
-    }
-
-    @Override
-    public MenuBarsMarshallerService<? extends MenuBars> marshaller() {
-        return marshaller;
-    }
-
     // -- HELPER
 
     private BSMenuBars menuBarsDefault() {

Reply via email to