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 2e2a402230 ISIS-3272: separation of concerns: encapsulate menu-bars 
marshalling into its own service
2e2a402230 is described below

commit 2e2a402230a9654660bf4058cffb395901f3d82a
Author: Andi Huber <[email protected]>
AuthorDate: Sat Nov 5 08:09:05 2022 +0100

    ISIS-3272: separation of concerns: encapsulate menu-bars marshalling
    into its own service
---
 .../applib/services/layout/LayoutService.java      |   6 +-
 .../services/menu/MenuBarsLoaderService.java       |   2 -
 ...Service.java => MenuBarsMarshallerService.java} |  45 ++++----
 .../applib/services/menu/MenuBarsService.java      |  23 +++--
 .../services/layout/LayoutServiceDefault.java      | 114 +++++++++------------
 .../CausewayModuleCoreRuntimeServices.java         |   2 +
 .../bootstrap/MenuBarsLoaderServiceBootstrap.java  |  40 +++-----
 .../MenuBarsMarshallerServiceBootstrap.java        | 105 +++++++++++++++++++
 .../bootstrap/MenuBarsServiceBootstrap.java        |  22 ++--
 .../RuntimeServicesTestAbstract.java               |  18 +++-
 .../menubars/bootstrap/MenuBarsServiceBSTest.java  |   4 +-
 11 files changed, 239 insertions(+), 142 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/layout/LayoutService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/layout/LayoutService.java
index a889b019bb..2266a75f09 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/layout/LayoutService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/layout/LayoutService.java
@@ -41,13 +41,13 @@ public interface LayoutService {
 
     /**
      * Obtains the serialized form of the object layout (grid) for the 
specified domain class.
-     * @throws UnsupportedOperationException - when format is not supported
+     * @throws UnsupportedOperationException when format is not supported
      */
     String objectLayout(Class<?> domainClass, LayoutExportStyle style, 
CommonMimeType format);
 
     /**
      * Obtains a zip file of the serialized layouts (grids) of all domain 
entities and view models.
-     * @throws UnsupportedOperationException - when format is not supported
+     * @throws UnsupportedOperationException when format is not supported
      */
     byte[] toZip(LayoutExportStyle style, CommonMimeType format);
 
@@ -61,7 +61,7 @@ public interface LayoutService {
 
     /**
      * Obtains the serialized form of the menu bars layout ({@link 
MenuBarsService}).
-     * @throws UnsupportedOperationException - when format is not supported
+     * @throws UnsupportedOperationException when format is not supported
      */
     String menuBarsLayout(MenuBarsService.Type type, 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 5e5986e269..607bd4bc1b 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
@@ -41,8 +41,6 @@ import 
org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
  */
 public interface MenuBarsLoaderService<T extends MenuBars> {
 
-    Class<T> implementedMenuBarsClass();
-
     /**
      * Whether dynamic reloading of layouts is enabled.
      *
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/MenuBarsMarshallerService.java
similarity index 55%
copy from 
api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsLoaderService.java
copy to 
api/applib/src/main/java/org/apache/causeway/applib/services/menu/MenuBarsMarshallerService.java
index 5e5986e269..65e6a19fd1 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/MenuBarsMarshallerService.java
@@ -19,50 +19,43 @@
 package org.apache.causeway.applib.services.menu;
 
 import java.util.EnumSet;
-import java.util.Optional;
+
+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;
 
 /**
- * Returns the {@link MenuBars} instance for the UI.
- *
+ * Supports {@link MenuBars} marshaling and unmarshaling.
  * <p>
- *     The default implementation de-serializes the `menubars.layout...` file
- *     read from the classpath.
- * </p>
+ * The service is <i>called</i> by the default implementations of
+ * {@link MenuBarsService} and {@link LayoutService}.
  *
- * <p>
- *     The service is <i>called</i> by the default implementation of
- *     {@link MenuBarsService}.
- * </p>
- *
- * @since 1.x - revised for 2.0 {@index}
+ * @since 2.0 {@index}
  */
-public interface MenuBarsLoaderService<T extends MenuBars> {
+public interface MenuBarsMarshallerService<T extends MenuBars> {
 
     Class<T> implementedMenuBarsClass();
 
     /**
-     * Whether dynamic reloading of layouts is enabled.
-     *
-     * <p>
-     * If not, then the calling {@link MenuBarsService}will cache the layout
-     * once loaded.
-     * </p>
+     * Supported format(s) for {@link #unmarshal(String, CommonMimeType)}
+     * and {@link #marshal(MenuBars, CommonMimeType)}.
      */
-    boolean supportsReloading();
+    EnumSet<CommonMimeType> supportedFormats();
 
     /**
-     * Supported format(s) for {@link #menuBars()}.
+     * @throws UnsupportedOperationException when format is not supported
      */
-    EnumSet<CommonMimeType> supportedFormats();
+    String marshal(@NonNull T menuBars, @NonNull CommonMimeType format);
 
     /**
-     * Returns a new instance of a {@link MenuBars} if possible,
-     * else <tt>Optional.empty()</tt>.
-     * @throws UnsupportedOperationException - when format is not supported
+     * Returns a new instance of a {@link MenuBars} wrapped in a {@link Try}.
+     * @throws UnsupportedOperationException when format is not supported (not 
wrapped)
      */
-    Optional<T> menuBars();
+    Try<T> unmarshal(@Nullable String layoutFileContent, @NonNull 
CommonMimeType format);
 
 }
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 994e5f392b..a23c26baf8 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
@@ -18,11 +18,8 @@
  */
 package org.apache.causeway.applib.services.menu;
 
-import javax.inject.Named;
-
-import org.apache.causeway.applib.CausewayModuleApplib;
-import org.apache.causeway.applib.annotation.Value;
 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,16 +66,26 @@ public interface MenuBarsService {
      */
     MenuBars menuBars(final Type type);
 
+    // -- LAYOUT EXPORT
+
+    MenuBarsMarshallerService<? extends MenuBars> marshaller();
+    String menuBarsFormatted(Type type, CommonMimeType format);
+
+
     // -- JUNIT SUPPORT
 
     static MenuBarsService forTesting() {
         return new MenuBarsService() {
-
             @Override
             public MenuBars menuBars(final Type type) {
-                throw _Exceptions.unsupportedOperation();
-            }
-
+                throw _Exceptions.unsupportedOperation(); }
+            @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/services/layout/LayoutServiceDefault.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/layout/LayoutServiceDefault.java
index b6f46171c7..6e9366b471 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
@@ -33,7 +33,6 @@ 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.menubars.MenuBars;
 import org.apache.causeway.applib.services.grid.GridService;
 import org.apache.causeway.applib.services.jaxb.JaxbService;
 import org.apache.causeway.applib.services.layout.LayoutExportStyle;
@@ -74,99 +73,86 @@ public class LayoutServiceDefault implements LayoutService {
 
     @Override
     public String objectLayout(final Class<?> domainClass, final 
LayoutExportStyle style, final CommonMimeType format) {
-        switch(format) {
-        case XML:{
-            final Grid grid = gridService.toGridForExport(domainClass, style);
-            return gridToXml(grid);
-        }
-        default:
-            throw _Exceptions.unsupportedOperation("format %s not supported", 
format.name());
-        }
+        final Grid grid = gridService.toGridForExport(domainClass, style);
+        return gridToFormatted(grid, format);
     }
 
     @Override
     public byte[] toZip(final LayoutExportStyle style, final CommonMimeType 
format) {
-        switch(format) {
-        case XML:{
-            return toZipAsXml(style);
-        }
-        default:
-            throw _Exceptions.unsupportedOperation("format %s not supported", 
format.name());
+        val domainObjectSpecs = specificationLoader.snapshotSpecifications()
+                .filter(spec ->
+                        !spec.isAbstract()
+                        && (spec.isEntity() || spec.isViewModel()));
+
+        val zipWriter = ZipWriter.ofFailureMessage("Unable to create zip of 
layouts");
+
+        for (val objectSpec : domainObjectSpecs) {
+            val domainClass = objectSpec.getCorrespondingClass();
+
+            tryGridToFormatted(domainClass, style, format)
+            .accept(
+                    failure->
+                        log.warn("failed to generate layout file for {}", 
domainClass),
+                    contentIfAny->{
+                        contentIfAny.ifPresent(contentString->{
+                            zipWriter.nextEntry(zipEntryNameFor(objectSpec, 
format), writer->
+                                writer.writeCharactersUtf8(contentString)
+                            );
+                        });
+                    });
         }
+
+        return zipWriter.toBytes();
     }
 
     // -- MENUBARS LAYOUT
 
     @Override
     public EnumSet<CommonMimeType> supportedMenuBarsLayoutFormats() {
-        return EnumSet.of(CommonMimeType.XML);
+        return menuBarsService.marshaller().supportedFormats();
     }
 
     @Override
     public String menuBarsLayout(
             final MenuBarsService.Type type,
             final CommonMimeType format) {
-        switch(format) {
-        case XML:{
-            final MenuBars menuBars = menuBarsService.menuBars(type);
-            return jaxbService.toXml(menuBars, _Maps.unmodifiable(
-                    Marshaller.JAXB_SCHEMA_LOCATION,
-                    menuBars.getTnsAndSchemaLocation()
-                    ));
-        }
-        default:
-            throw _Exceptions.unsupportedOperation("format %s not supported", 
format.name());
-        }
+        return menuBarsService.menuBarsFormatted(type, format);
     }
 
     // -- HELPER
 
-    private byte[] toZipAsXml(final LayoutExportStyle style) {
-        val domainObjectSpecs = specificationLoader.snapshotSpecifications()
-        .filter(spec ->
-                !spec.isAbstract()
-                && (spec.isEntity() || spec.isViewModel()));
-
-        val zipWriter = ZipWriter.ofFailureMessage("Unable to create zip of 
layouts");
-
-        for (val objectSpec : domainObjectSpecs) {
-            val domainClass = objectSpec.getCorrespondingClass();
-
-            tryGridToXml(domainClass, style)
-            .accept(failure->{
-                log.warn("failed to generate layout XML for {}", 
domainClass);//, failure);
-            },
-            xmlIfAny->{
-                xmlIfAny.ifPresent(xmlString->{
-                    zipWriter.nextEntry(zipEntryNameFor(objectSpec), writer->
-                        writer.writeCharactersUtf8(xmlString)
-                    );
-                });
-            });
-        }
-
-        return zipWriter.toBytes();
-    }
-
-    private Try<String> tryGridToXml(final Class<?> domainClass, final 
LayoutExportStyle style) {
+    private Try<String> tryGridToFormatted(
+            final Class<?> domainClass,
+            final LayoutExportStyle style,
+            final CommonMimeType format) {
         return Try.call(()->
-            gridToXml(gridService.toGridForExport(domainClass, style)));
+            gridToFormatted(gridService.toGridForExport(domainClass, style), 
format));
     }
 
-    private String gridToXml(final @Nullable Grid grid) {
+    private String gridToFormatted(final @Nullable Grid grid, final 
CommonMimeType format) {
         if(grid==null) {
             return null;
         }
-        return jaxbService.toXml(grid,
-                _Maps.unmodifiable(
-                        Marshaller.JAXB_SCHEMA_LOCATION,
-                        Objects.requireNonNull(grid.getTnsAndSchemaLocation())
-                        ));
+        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());
+        }
     }
 
-    private static String zipEntryNameFor(final ObjectSpecification 
objectSpec) {
+    private static String zipEntryNameFor(
+            final ObjectSpecification objectSpec,
+            final CommonMimeType format) {
         final String fqn = objectSpec.getFullIdentifier();
-        return fqn.replace(".", File.separator)+".layout.xml";
+        return fqn.replace(".", File.separator)
+                + ".layout."
+                + format.getProposedFileExtensions().getFirstOrFail();
     }
 
 
diff --git 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/CausewayModuleCoreRuntimeServices.java
 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/CausewayModuleCoreRuntimeServices.java
index 526ac6c74b..a5b66aa3f2 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/CausewayModuleCoreRuntimeServices.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/CausewayModuleCoreRuntimeServices.java
@@ -45,6 +45,7 @@ import 
org.apache.causeway.core.runtimeservices.jaxb.JaxbServiceDefault;
 import org.apache.causeway.core.runtimeservices.locale.LanguageProviderDefault;
 import 
org.apache.causeway.core.runtimeservices.locale.LocaleChoiceProviderDefault;
 import 
org.apache.causeway.core.runtimeservices.menubars.bootstrap.MenuBarsLoaderServiceBootstrap;
+import 
org.apache.causeway.core.runtimeservices.menubars.bootstrap.MenuBarsMarshallerServiceBootstrap;
 import 
org.apache.causeway.core.runtimeservices.menubars.bootstrap.MenuBarsServiceBootstrap;
 import org.apache.causeway.core.runtimeservices.message.MessageServiceDefault;
 import 
org.apache.causeway.core.runtimeservices.placeholder.PlaceholderRenderServiceDefault;
@@ -103,6 +104,7 @@ import 
org.apache.causeway.core.runtimeservices.xmlsnapshot.XmlSnapshotServiceDe
         LanguageProviderDefault.class,
         LocaleChoiceProviderDefault.class,
         MemberExecutorServiceDefault.class,
+        MenuBarsMarshallerServiceBootstrap.class,
         MenuBarsLoaderServiceBootstrap.class,
         MenuBarsServiceBootstrap.class,
         MessageServiceDefault.class,
diff --git 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsLoaderServiceBootstrap.java
 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsLoaderServiceBootstrap.java
index 41d4c89922..f9b11c5514 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsLoaderServiceBootstrap.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsLoaderServiceBootstrap.java
@@ -36,9 +36,10 @@ import org.springframework.stereotype.Service;
 
 import org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.layout.menubars.bootstrap.BSMenuBars;
-import org.apache.causeway.applib.services.jaxb.JaxbService;
 import org.apache.causeway.applib.services.menu.MenuBarsLoaderService;
+import org.apache.causeway.applib.services.menu.MenuBarsMarshallerService;
 import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
+import org.apache.causeway.commons.functional.Try;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.core.config.viewer.web.WebAppContextPath;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
@@ -57,7 +58,7 @@ import lombok.extern.log4j.Log4j2;
 public class MenuBarsLoaderServiceBootstrap
 implements MenuBarsLoaderService<BSMenuBars> {
 
-    private final JaxbService jaxbService;
+    private final MenuBarsMarshallerService<BSMenuBars> marshaller;
     private final boolean supportsReloading;
 
     @Getter(onMethod_={@Override}) @Accessors(fluent = true)
@@ -68,8 +69,8 @@ implements MenuBarsLoaderService<BSMenuBars> {
     @Inject
     public MenuBarsLoaderServiceBootstrap(
             final MetaModelContext mmc,
-            final JaxbService jaxbService) {
-        this.jaxbService = jaxbService;
+            final MenuBarsMarshallerService<BSMenuBars> marshaller) {
+        this.marshaller = marshaller;
         this.supportsReloading = mmc.getSystemEnvironment().isPrototyping();
 
         val menubarsLayoutFile = 
mmc.getConfiguration().getViewer().getCommon().getApplication()
@@ -87,9 +88,9 @@ implements MenuBarsLoaderService<BSMenuBars> {
 
     // JUnit support
     public MenuBarsLoaderServiceBootstrap(
-            final JaxbService jaxbService,
+            final MenuBarsMarshallerService<BSMenuBars> marshaller,
             final AtomicReference<AbstractResource> menubarsLayoutResourceRef) 
{
-        this.jaxbService = jaxbService;
+        this.marshaller = marshaller;
         this.supportsReloading = true;
 
         menubarsLayoutResourceRef.getAndUpdate(r->r!=null
@@ -102,11 +103,6 @@ implements MenuBarsLoaderService<BSMenuBars> {
         this.menubarsLayoutMimeType = CommonMimeType.XML;
     }
 
-    @Override
-    public Class<BSMenuBars> implementedMenuBarsClass() {
-        return BSMenuBars.class;
-    }
-
     @Override
     public boolean supportsReloading() {
         return supportsReloading;
@@ -114,24 +110,14 @@ implements MenuBarsLoaderService<BSMenuBars> {
 
     @Override
     public Optional<BSMenuBars> menuBars() {
-        return Optional.ofNullable(loadMenuBars(loadMenubarsLayoutResource()));
+        return tryLoadMenuBars(loadMenubarsLayoutResource())
+                
.ifFailure(failure->severeCannotLoad(menubarsLayoutResourceRef.get(), failure))
+                .getValue();
     }
 
     // public, in support of JUnit testing
-    public BSMenuBars loadMenuBars(final String layoutFileContent) {
-
-        switch(menubarsLayoutMimeType) {
-        case XML:{
-            try {
-                return jaxbService.fromXml(BSMenuBars.class, 
layoutFileContent);
-            } catch (Exception e) {
-                severeCannotLoad(menubarsLayoutResourceRef.get(), e);
-                return null;
-            }
-        }
-        default:
-            return null;
-        }
+    public Try<BSMenuBars> tryLoadMenuBars(final String layoutFileContent) {
+        return marshaller.unmarshal(layoutFileContent, menubarsLayoutMimeType);
     }
 
     // -- HELPER
@@ -175,7 +161,7 @@ implements MenuBarsLoaderService<BSMenuBars> {
         warnedOnce = true;
     }
 
-    private void severeCannotLoad(final AbstractResource 
menubarsLayoutResource, final Exception cause) {
+    private void severeCannotLoad(final AbstractResource 
menubarsLayoutResource, final Throwable cause) {
 
         log.error("{}: could not find readable resource {} for the 
Menubars-Layout.",
                         WebAppContextPath.class.getName(),
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
new file mode 100644
index 0000000000..732356f9f8
--- /dev/null
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsMarshallerServiceBootstrap.java
@@ -0,0 +1,105 @@
+/*
+ *  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.runtimeservices.menubars.bootstrap;
+
+import java.util.EnumSet;
+
+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;
+import org.springframework.stereotype.Service;
+
+import org.apache.causeway.applib.annotation.PriorityPrecedence;
+import org.apache.causeway.applib.layout.menubars.bootstrap.BSMenuBars;
+import org.apache.causeway.applib.services.jaxb.JaxbService;
+import org.apache.causeway.applib.services.menu.MenuBarsMarshallerService;
+import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
+import org.apache.causeway.commons.functional.Try;
+import org.apache.causeway.commons.internal.base._Strings;
+import org.apache.causeway.commons.internal.collections._Maps;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
+import 
org.apache.causeway.core.runtimeservices.CausewayModuleCoreRuntimeServices;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.experimental.Accessors;
+
+@Service
+@Named(CausewayModuleCoreRuntimeServices.NAMESPACE + 
".MenuBarsMarshallerBootstrap")
+@Priority(PriorityPrecedence.MIDPOINT)
+@Qualifier("Default")
+//@Log4j2
+public class MenuBarsMarshallerServiceBootstrap
+implements MenuBarsMarshallerService<BSMenuBars> {
+
+    private final JaxbService jaxbService;
+
+    @Getter(onMethod_={@Override}) @Accessors(fluent = true)
+    private final EnumSet<CommonMimeType> supportedFormats =
+            EnumSet.of(CommonMimeType.XML);
+
+    @Inject
+    public MenuBarsMarshallerServiceBootstrap(
+            final JaxbService jaxbService) {
+        this.jaxbService = jaxbService;
+    }
+
+    @Override
+    public Class<BSMenuBars> implementedMenuBarsClass() {
+        return BSMenuBars.class;
+    }
+
+    @Override
+    public String marshal(final @NonNull BSMenuBars menuBars, final @NonNull 
CommonMimeType format) {
+        throwIfFormatNotSupported(format);
+        return jaxbService.toXml(menuBars, _Maps.unmodifiable(
+                Marshaller.JAXB_SCHEMA_LOCATION, 
menuBars.getTnsAndSchemaLocation()));
+    }
+
+    @Override
+    public Try<BSMenuBars> unmarshal(
+            final @Nullable String layoutFileContent,
+            final @NonNull CommonMimeType format) {
+        throwIfFormatNotSupported(format);
+        if(_Strings.isEmpty(layoutFileContent)) {
+            return Try.success(null); // empty
+        }
+        switch(format) {
+        case XML:{
+            return Try.call(()->jaxbService.fromXml(BSMenuBars.class, 
layoutFileContent));
+        }
+        default:
+            throw _Exceptions.unmatchedCase(format); // supported format, but 
not implemented
+        }
+    }
+
+    // -- HELPER
+
+    private void throwIfFormatNotSupported(final CommonMimeType format) {
+        if(!supportedFormats().contains(format)) {
+            throw _Exceptions.unsupportedOperation("menu-bars layout file 
format %s not supported", format.name());
+        }
+    }
+
+}
+
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 0b426692d5..e043a270b1 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
@@ -43,8 +43,10 @@ import 
org.apache.causeway.applib.layout.menubars.bootstrap.BSMenuBars;
 import org.apache.causeway.applib.layout.menubars.bootstrap.BSMenuSection;
 import org.apache.causeway.applib.services.jaxb.JaxbService;
 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;
@@ -98,6 +100,7 @@ implements MenuBarsService {
     public static final String LINKS_SCHEMA_LOCATION = 
GridServiceDefault.LINKS_SCHEMA_LOCATION;
 
     private final MenuBarsLoaderService<BSMenuBars> loader;
+    private final MenuBarsMarshallerService<BSMenuBars> marshaller;
     private final MessageService messageService;
     private final JaxbService jaxbService;
     private final MetaModelContext metaModelContext;
@@ -108,28 +111,33 @@ implements MenuBarsService {
     BSMenuBars menuBars;
 
     @Override
-    public MenuBars menuBars(final Type type) {
-
+    public BSMenuBars menuBars(final Type type) {
         val menuBarsFromAnnotationsOnly = 
this.menuBarsFromAnnotationsOnly.get();
-
         if(type == Type.ANNOTATED) {
             return menuBarsFromAnnotationsOnly;
         }
-
         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() {
-
         val menuBarsFromAnnotationsOnly = 
this.menuBarsFromAnnotationsOnly.get();
-
         // load (and only fallback if nothing could be loaded)...
         if(menuBars == null || loader.supportsReloading()) {
             this.menuBars = loadOrElse(menuBarsFromAnnotationsOnly);
         }
-
         return menuBars;
     }
 
diff --git 
a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
 
b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
index 3565b936b8..1c16c906bc 100644
--- 
a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
+++ 
b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
@@ -26,6 +26,7 @@ import org.springframework.core.io.AbstractResource;
 
 import org.apache.causeway.applib.services.jaxb.JaxbService;
 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.commons.internal.ioc._ManagedBeanAdapter;
@@ -34,6 +35,7 @@ import 
org.apache.causeway.core.metamodel._testing.MetaModelContext_forTesting.M
 import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import 
org.apache.causeway.core.runtimeservices.menubars.bootstrap.MenuBarsLoaderServiceBootstrap;
+import 
org.apache.causeway.core.runtimeservices.menubars.bootstrap.MenuBarsMarshallerServiceBootstrap;
 import 
org.apache.causeway.core.runtimeservices.menubars.bootstrap.MenuBarsServiceBootstrap;
 
 import lombok.Getter;
@@ -65,11 +67,19 @@ implements HasMetaModelContext {
 
         mmcBuilder.singletonProvider(
                 _ManagedBeanAdapter
-                .forTestingLazy(MenuBarsLoaderService.class, ()->{
-
+                .forTestingLazy(MenuBarsMarshallerService.class, ()->{
                     val jaxbService = 
getServiceRegistry().lookupServiceElseFail(JaxbService.class);
+                    return new MenuBarsMarshallerServiceBootstrap(
+                            jaxbService);
+                }));
+
+
+        mmcBuilder.singletonProvider(
+                _ManagedBeanAdapter
+                .forTestingLazy(MenuBarsLoaderService.class, ()->{
+                    val menuBarsMenuBarsMarshaller = 
getServiceRegistry().lookupServiceElseFail(MenuBarsMarshallerService.class);
                     return new MenuBarsLoaderServiceBootstrap(
-                            jaxbService,
+                            menuBarsMenuBarsMarshaller,
                             menubarsLayoutXmlResourceRef);
                 }));
 
@@ -80,9 +90,11 @@ implements HasMetaModelContext {
 
                     val messageService = 
getServiceRegistry().lookupServiceElseFail(MessageService.class);
                     val jaxbService = 
getServiceRegistry().lookupServiceElseFail(JaxbService.class);
+                    val menuBarsMenuBarsMarshaller = 
getServiceRegistry().lookupServiceElseFail(MenuBarsMarshallerService.class);
                     val menuBarsLoaderService = 
getServiceRegistry().lookupServiceElseFail(MenuBarsLoaderService.class);
                     return new MenuBarsServiceBootstrap(
                             menuBarsLoaderService,
+                            menuBarsMenuBarsMarshaller,
                             messageService,
                             jaxbService,
                             metaModelContext);
diff --git 
a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBSTest.java
 
b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBSTest.java
index 1590ac3e4b..490062e31f 100644
--- 
a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBSTest.java
+++ 
b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/menubars/bootstrap/MenuBarsServiceBSTest.java
@@ -95,7 +95,7 @@ extends RuntimeServicesTestAbstract {
         val xml = layoutService.menuBarsLayout(MenuBarsService.Type.DEFAULT, 
format);
 
         // after round-trip
-        val menuBars2 = menuBarsLoaderService.loadMenuBars(xml);
+        val menuBars2 = 
menuBarsLoaderService.tryLoadMenuBars(xml).getValue().orElse(null);
         assertNotNull(menuBars2);
         assertEquals(1L, menuBars2.stream().count());
 
@@ -111,7 +111,7 @@ extends RuntimeServicesTestAbstract {
         val xml = sampleMenuBarsXmlWithCustomName(customNamed);
 
         // create menubars-xml from scratch (annotations and fallback naming 
only)
-        val menuBars = menuBarsLoaderService.loadMenuBars(xml);
+        val menuBars = 
menuBarsLoaderService.tryLoadMenuBars(xml).getValue().orElse(null);
         assertNotNull(menuBars);
         assertEquals(1L, menuBars.stream().count());
 

Reply via email to