This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch spring6
in repository https://gitbox.apache.org/repos/asf/isis.git

commit b437629424a124dace877e224a3c5e6f777a2cdc
Merge: 8ccd97fd9e ddac408c5d
Author: Andi Huber <[email protected]>
AuthorDate: Wed Dec 14 06:14:21 2022 +0100

    ISIS-3275: [Incubating] Migrate from Spring Framework 5.x to 6.x

 .../causeway/applib/services/jaxb/JaxbService.java |  12 +-
 .../services/metamodel/MetaModelServiceMenu.java   |  14 +-
 .../services/publishing/log/CommandLogger.java     |   4 +-
 .../publishing/log/EntityChangesLogger.java        |   2 +-
 .../services/publishing/log/ExecutionLogger.java   |   2 +-
 .../org/apache/causeway/applib/util/JaxbUtil.java  | 136 ++++-----
 .../applib/util/schema/ChangesDtoUtils.java        |  81 +-----
 .../applib/util/schema/CommandDtoUtils.java        |  92 ++----
 .../applib/util/schema/CommonDtoUtils.java         |  27 +-
 .../applib/util/schema/InteractionDtoUtils.java    | 118 ++------
 .../applib/util/schema/InteractionsDtoUtils.java   |  74 +----
 .../util/schema/MemberExecutionDtoUtils.java       |  55 ++--
 .../applib/services/jaxb/JaxbServiceTest.java      |   7 +-
 commons/src/main/java/module-info.java             |   1 +
 .../apache/causeway/commons/functional/Try.java    |   5 +
 .../commons/internal/resources/_DataSink.java      |  35 ---
 .../commons/internal/resources/_DataSource.java    | 139 ---------
 .../causeway/commons/internal/resources/_Json.java | 246 ----------------
 .../causeway/commons/internal/resources/_Xml.java  | 232 ---------------
 .../causeway/commons/internal/resources/_Yaml.java | 168 -----------
 .../org/apache/causeway/commons/io/DataSink.java   | 106 +++++++
 .../org/apache/causeway/commons/io/DataSource.java |  96 +++++++
 .../org/apache/causeway/commons/io/DtoMapper.java  |  77 +++++
 .../org/apache/causeway/commons/io/JaxbUtils.java  | 315 +++++++++++++++++++++
 .../org/apache/causeway/commons/io/JsonUtils.java  | 151 ++++++++++
 .../org/apache/causeway/commons/io/YamlUtils.java  | 130 +++++++++
 .../commons/resource/ResourceCoordinates.java      |   3 +-
 .../internal/resources/JsonYamlReaderTest.java     |  20 +-
 .../internal/resources/XmlRoundTripTest.java       |  17 +-
 .../valuesemantics/ChangesDtoValueSemantics.java   |   4 +-
 .../valuesemantics/CommandDtoValueSemantics.java   |   4 +-
 .../InteractionDtoValueSemantics.java              |   4 +-
 .../runtimeservices/jaxb/JaxbServiceDefault.java   |   4 +-
 .../dom/domain/_interactions/InteractionDtoVm.java |   4 +-
 .../applib/dom/CommandLogEntryRepository.java      |   6 +-
 .../applib/job/RunBackgroundCommandsJob.java       |   5 +-
 .../subscriber/CommandSubscriberForCommandLog.java |   9 +-
 .../applib/dom/ExecutionOutboxEntryRepository.java |   7 +-
 .../applib/restapi/OutboxRestApi.java              |   2 +-
 .../restclient/api/OutboxClient.java               |   6 +-
 .../secman/applib/util/ApplicationSecurityDto.java |   4 +-
 .../pdfjs/applib/config/PdfJsConfig.java           |   4 +-
 .../schema/v2/CausewayChangesDtoConverter.java     |   8 +-
 .../schema/v2/CausewayCommandDtoConverter.java     |   8 +-
 .../schema/v2/CausewayInteractionDtoConverter.java |   8 +-
 .../schema/v2/CausewayChangesDtoConverter.java     |   8 +-
 .../schema/v2/CausewayCommandDtoConverter.java     |   8 +-
 .../schema/v2/CausewayInteractionDtoConverter.java |   8 +-
 .../domainmodel/MetaModelRegressionTest.java       |   4 +-
 .../testdomain/value/ValueSemanticsTester.java     |  12 +-
 .../EntityChangesSubscriberForTesting.java         |  18 +-
 .../subscriber/ExecutionSubscriberForTesting.java  |   2 +-
 .../org/apache/causeway/tooling/cli/CliConfig.java |   5 +-
 .../causeway/tooling/cli/test/CliConfigTest.java   |  10 +-
 .../valuetypes/vega/applib/value/Vega.java         |   5 +-
 .../restfulobjects/applib/JsonRepresentation.java  |  70 ++---
 .../viewer/resources/_EndpointLogging.java         |   9 +-
 .../serialization/SerializationStrategy.java       |  12 +-
 58 files changed, 1212 insertions(+), 1411 deletions(-)

diff --cc 
api/applib/src/main/java/org/apache/causeway/applib/services/jaxb/JaxbService.java
index 3d96c03338,c5bfa12316..9176eb46e3
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/jaxb/JaxbService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/jaxb/JaxbService.java
@@@ -26,12 -31,8 +26,12 @@@ import org.springframework.lang.Nullabl
  
  import org.apache.causeway.commons.internal.base._Casts;
  import org.apache.causeway.commons.internal.base._NullSafe;
- import org.apache.causeway.commons.internal.resources._Xml;
+ import org.apache.causeway.commons.io.JaxbUtils;
  
 +import jakarta.xml.bind.JAXBContext;
 +import jakarta.xml.bind.JAXBException;
 +import jakarta.xml.bind.Marshaller;
 +import jakarta.xml.bind.Unmarshaller;
  import lombok.NonNull;
  import lombok.SneakyThrows;
  import lombok.val;
diff --cc 
api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/MetaModelServiceMenu.java
index 689d481ab7,f0b26c4cba..6b00574d1f
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/MetaModelServiceMenu.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/MetaModelServiceMenu.java
@@@ -43,13 -46,11 +43,13 @@@ import org.apache.causeway.applib.servi
  import org.apache.causeway.applib.value.Blob;
  import org.apache.causeway.applib.value.Clob;
  import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
+ import org.apache.causeway.commons.internal.base._Strings;
  import org.apache.causeway.commons.internal.collections._Sets;
- import org.apache.causeway.commons.internal.resources._Xml;
- import org.apache.causeway.commons.internal.resources._Xml.WriteOptions;
+ import org.apache.causeway.commons.io.JaxbUtils;
  import org.apache.causeway.schema.metamodel.v2.MetamodelDto;
  
 +import jakarta.inject.Inject;
 +import jakarta.inject.Named;
  import lombok.val;
  
  /**
diff --cc commons/src/main/java/org/apache/causeway/commons/io/JaxbUtils.java
index 0000000000,92691b4e99..eeb8a354e0
mode 000000,100644..100644
--- a/commons/src/main/java/org/apache/causeway/commons/io/JaxbUtils.java
+++ b/commons/src/main/java/org/apache/causeway/commons/io/JaxbUtils.java
@@@ -1,0 -1,314 +1,315 @@@
+ /*
+  *  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.commons.io;
+ 
+ import java.io.InputStream;
+ import java.io.OutputStream;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.Optional;
+ import java.util.function.Consumer;
+ import java.util.function.UnaryOperator;
+ import java.util.stream.Collectors;
+ 
 -import javax.xml.bind.JAXBContext;
 -import javax.xml.bind.JAXBElement;
 -import javax.xml.bind.Marshaller;
 -import javax.xml.bind.Unmarshaller;
 -import javax.xml.bind.annotation.XmlRootElement;
+ import javax.xml.namespace.QName;
+ 
++import jakarta.xml.bind.JAXBContext;
++import jakarta.xml.bind.JAXBElement;
++import jakarta.xml.bind.Marshaller;
++import jakarta.xml.bind.Unmarshaller;
++import jakarta.xml.bind.annotation.XmlRootElement;
++
+ import org.springframework.lang.Nullable;
+ 
+ import org.apache.causeway.commons.functional.Try;
+ import org.apache.causeway.commons.internal.base._Casts;
+ import org.apache.causeway.commons.internal.base._NullSafe;
+ import org.apache.causeway.commons.internal.codec._DocumentFactories;
+ import org.apache.causeway.commons.internal.collections._Maps;
+ import org.apache.causeway.commons.internal.exceptions._Exceptions;
+ import org.apache.causeway.commons.internal.reflection._Annotations;
+ 
+ import lombok.Builder;
+ import lombok.Data;
+ import lombok.NonNull;
+ import lombok.Singular;
+ import lombok.SneakyThrows;
+ import lombok.val;
+ import lombok.experimental.UtilityClass;
+ 
+ /**
+  * Utilities to convert from and to JAXB-XML format.
+  *
+  * @since 2.0 {@index}
+  */
+ @UtilityClass
+ public class JaxbUtils {
+ 
+     @Data @Builder
+     public static class JaxbOptions {
+         private final @Builder.Default boolean useContextCache = true;
+         private final @Builder.Default boolean allowMissingRootElement = 
false;
+         private final @Builder.Default boolean formattedOutput = false;
+         private final @Singular Map<String, Object> properties;
+         public static JaxbOptions defaults() {
+             return JaxbOptions.builder().build();
+         }
+ 
+         // -- HELPER
+ 
+         private boolean shouldMissingXmlRootElementBeHandledOn(final Class<?> 
mappedType) {
+             return isAllowMissingRootElement()
+                     && !_Annotations.isPresent(mappedType, 
XmlRootElement.class); //TODO ask _ClassCache
+         }
+         @SneakyThrows
+         private JAXBContext jaxbContext(final Class<?> mappedType) {
+             return jaxbContextFor(mappedType, useContextCache);
+         }
+         @SneakyThrows
+         private Marshaller marshaller(final JAXBContext jaxbContext, final 
Class<?> mappedType) {
+             val marshaller = jaxbContext.createMarshaller();
+             if(properties!=null) {
+                 for(val entry : properties.entrySet()) {
+                     marshaller.setProperty(entry.getKey(), entry.getValue());
+                 }
+             }
+             if(isFormattedOutput()) {
+                 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, 
Boolean.TRUE);
+             }
+             return marshaller;
+         }
+         @SneakyThrows
+         private Unmarshaller unmarshaller(final JAXBContext jaxbContext, 
final Class<?> mappedType) {
+             val unmarshaller = jaxbContext.createUnmarshaller();
+             if(properties!=null) {
+                 for(val entry : properties.entrySet()) {
+                     unmarshaller.setProperty(entry.getKey(), 
entry.getValue());
+                 }
+             }
+             return unmarshaller;
+         }
+         @SneakyThrows
+         private <T> T unmarshal(final Unmarshaller unmarshaller, final 
Class<T> mappedType, final InputStream is) {
+             if(shouldMissingXmlRootElementBeHandledOn(mappedType)) {
+                 val xsr = 
_DocumentFactories.xmlInputFactory().createXMLStreamReader(is);
+                 final JAXBElement<T> userElement = 
unmarshaller.unmarshal(xsr, mappedType);
+                 return userElement.getValue();
+             }
+             return _Casts.uncheckedCast(unmarshaller.unmarshal(is));
+         }
+         @SneakyThrows
+         private <T> void marshal(final Marshaller marshaller, final T pojo, 
final OutputStream os) {
+             @SuppressWarnings("unchecked")
+             val mappedType = (Class<T>)pojo.getClass();
+             if(shouldMissingXmlRootElementBeHandledOn(mappedType)) {
+                 val qName = new QName("", mappedType.getSimpleName());
+                 val jaxbElement = new JAXBElement<T>(qName, mappedType, null, 
pojo);
+                 marshaller.marshal(jaxbElement, os);
+             } else {
+                 marshaller.marshal(pojo, os);
+             }
+         }
+         private <T> T unmarshal(final JAXBContext jaxbContext, final Class<T> 
mappedType, final InputStream is) {
+             return unmarshal(unmarshaller(jaxbContext, mappedType), 
mappedType, is);
+         }
+         private <T> void marshal(final JAXBContext jaxbContext, final T pojo, 
final OutputStream os) {
+             @SuppressWarnings("unchecked")
+             val mappedType = (Class<T>)pojo.getClass();
+             marshal(marshaller(jaxbContext, mappedType), pojo, os);
+         }
+         private <T> T unmarshal(final Class<T> mappedType, final InputStream 
is) {
+             return unmarshal(jaxbContext(mappedType), mappedType, is);
+         }
+         private <T> void marshal(final T pojo, final OutputStream os) {
+             @SuppressWarnings("unchecked")
+             val mappedType = (Class<T>)pojo.getClass();
+             marshal(jaxbContext(mappedType), pojo, os);
+         }
+     }
+ 
+     @FunctionalInterface
+     public interface JaxbCustomizer extends 
UnaryOperator<JaxbOptions.JaxbOptionsBuilder> {}
+ 
+     // -- MAPPER
+ 
+     public <T> DtoMapper<T> mapperFor(final @NonNull Class<T> mappedType, 
final JaxbUtils.JaxbCustomizer ... customizers) {
+ 
+         val opts = createOptions(customizers);
+         val jaxbContext = opts.jaxbContext(mappedType); // cached with this 
instance of DtoMapper
+ 
+         return new DtoMapper<T>() {
+ 
+             @Override
+             public T read(final DataSource source) {
+                 return source.readAll((final InputStream is)->{
+                     return Try.call(()->opts.unmarshal(jaxbContext, 
mappedType, is));
+                 })
+                 .ifFailureFail()
+                 .getValue().orElseThrow();
+             }
+ 
+             @Override
+             public void write(final T dto, final DataSink sink) {
+                 if(dto==null) return;
+                 sink.writeAll(os->Try.run(()->opts.marshal(jaxbContext, dto, 
os)));
+             }
+ 
+         };
+     }
+ 
+ 
+     // -- READING
+ 
+     /**
+      * Tries to deserialize JAXB-XML content from given UTF8 encoded {@link 
String}
+      * into an instance of given {@code mappedType}.
+      */
+     public <T> Try<T> tryRead(
+             final @NonNull Class<T> mappedType,
+             final @Nullable String stringUtf8,
+             final JaxbUtils.JaxbCustomizer ... customizers) {
+         return tryRead(mappedType, DataSource.ofStringUtf8(stringUtf8), 
customizers);
+     }
+ 
+     /**
+      * Tries to deserialize JAXB-XML content from given {@link DataSource} 
into an instance of
+      * given {@code mappedType}.
+      */
+     public <T> Try<T> tryRead(
+             final @NonNull Class<T> mappedType,
+             final @NonNull DataSource source,
+             final JaxbUtils.JaxbCustomizer ... customizers) {
+         return source.readAll((final InputStream is)->{
+             val opts = createOptions(customizers);
+             return Try.call(()->opts.unmarshal(mappedType, is));
+         });
+     }
+ 
+     // -- WRITING
+ 
+     /**
+      * Writes given {@code pojo} to given {@link DataSink}.
+      */
+     public <T> void write(
+             final @Nullable T pojo,
+             final @NonNull DataSink sink,
+             final JaxbUtils.JaxbCustomizer ... customizers) {
+         if(pojo==null) return;
+         val opts = createOptions(customizers);
+         sink.writeAll(os->Try.run(()->opts.marshal(pojo, os)));
+     }
+ 
+     /**
+      * Converts given {@code pojo} to an UTF8 encoded {@link String}.
+      * @return <code>null</code> if pojo is <code>null</code>
+      */
+     @SneakyThrows
+     @Nullable
+     public <T> String toStringUtf8(
+             final @Nullable T pojo,
+             final JaxbUtils.JaxbCustomizer ... customizers) {
+         if(pojo==null) return null;
+         class StringHolder implements Consumer<String> {
+             String s;
+             @Override public void accept(String s) { this.s = s; }
+         }
+         val sh = new StringHolder();
+         write(pojo, DataSink.ofStringUtf8Consumer(sh), customizers);
+         return sh.s;
+     }
+ 
+     // -- CUSTOMIZERS
+ 
+     // -- MAPPER FACTORY
+ 
+     private JaxbOptions createOptions(
+             final JaxbUtils.JaxbCustomizer ... customizers) {
+         var opts = JaxbOptions.builder();
+         for(JaxbUtils.JaxbCustomizer customizer : customizers) {
+             opts = Optional.ofNullable(customizer.apply(opts))
+                     .orElse(opts);
+         }
+         return opts.build();
+     }
+ 
+     // -- JAXB CONTEXT CACHE
+ 
+     private static Map<Class<?>, JAXBContext> jaxbContextByClass = 
_Maps.newConcurrentHashMap();
+ 
+     public static <T> JAXBContext jaxbContextFor(final Class<T> dtoClass, 
final boolean useCache)  {
+         return useCache
+                 ? jaxbContextByClass.computeIfAbsent(dtoClass, 
JaxbUtils::contextOf)
+                 : contextOf(dtoClass);
+     }
+ 
+     @SneakyThrows
+     private static <T> JAXBContext contextOf(final Class<T> dtoClass) {
+         try {
+             return JAXBContext.newInstance(dtoClass);
+         } catch (Exception e) {
+             throw verboseException("obtaining JAXBContext for class", 
dtoClass, e);
+         }
+     }
+ 
+     // -- ENHANCE EXCEPTION MESSAGE IF POSSIBLE
+ 
+     public static Exception verboseException(final String doingWhat, 
@Nullable final Class<?> dtoClass, final Exception e) {
+ 
+         val dtoClassName = 
Optional.ofNullable(dtoClass).map(Class::getName).orElse("unknown");
+ 
+         if(isIllegalAnnotationsException(e)) {
+             // report a better error if possible
+             // this is done reflectively because on JDK 8 this exception type 
is only provided by Oracle JDK
+             try {
+ 
+                 val errors = _Casts.<List<? extends Exception>>uncheckedCast(
+                         e.getClass().getMethod("getErrors").invoke(e));
+ 
+                 if(_NullSafe.size(errors)>0) {
+ 
+                     return _Exceptions.unrecoverable(e,
+                             "Error %s, "
+                             + "due to illegal annotations on object class 
'%s'; "
+                             + "%d error(s) reported: %s",
+                             doingWhat,
+                             dtoClassName,
+                             errors.size(),
+                             errors.stream()
+                                 .map(Exception::getMessage)
+                                 .collect(Collectors.joining("; ")));
+                 }
+ 
+             } catch (Exception ex) {
+                 // just fall through if we hit any issues
+             }
+         }
+ 
+         return _Exceptions.unrecoverable(e,
+                 "Error %s; object class is '%s'", doingWhat, dtoClassName);
+     }
+ 
+     private static boolean isIllegalAnnotationsException(final Exception e) {
+         /*sonar-ignore-on*/
+         return 
"com.sun.xml.bind.v2.runtime.IllegalAnnotationsException".equals(e.getClass().getName());
+         /*sonar-ignore-off*/
+     }
+ 
+ 
+ 
+ }
diff --cc commons/src/main/java/org/apache/causeway/commons/io/JsonUtils.java
index 0000000000,5c5706562a..04efc8c307
mode 000000,100644..100644
--- a/commons/src/main/java/org/apache/causeway/commons/io/JsonUtils.java
+++ b/commons/src/main/java/org/apache/causeway/commons/io/JsonUtils.java
@@@ -1,0 -1,151 +1,151 @@@
+ /*
+  *  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.commons.io;
+ 
+ import java.io.InputStream;
+ import java.util.List;
+ import java.util.Optional;
+ import java.util.function.UnaryOperator;
+ 
+ import com.fasterxml.jackson.annotation.JsonInclude.Include;
+ import com.fasterxml.jackson.databind.ObjectMapper;
+ import com.fasterxml.jackson.databind.SerializationFeature;
 -import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
++import 
com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationModule;
+ 
+ import org.springframework.lang.Nullable;
+ 
+ import org.apache.causeway.commons.functional.Try;
+ 
+ import lombok.NonNull;
+ import lombok.SneakyThrows;
+ import lombok.val;
+ import lombok.experimental.UtilityClass;
+ 
+ /**
+  * Utilities to convert from and to JSON format.
+  *
+  * @since 2.0 {@index}
+  */
+ @UtilityClass
+ public class JsonUtils {
+ 
+     @FunctionalInterface
+     public interface JsonCustomizer extends UnaryOperator<ObjectMapper> {}
+ 
+     // -- READING
+ 
+     /**
+      * Tries to deserialize JSON content from given UTF8 encoded {@link 
String}
+      * into an instance of given {@code mappedType}.
+      */
+     public <T> Try<T> tryRead(
+             final @NonNull Class<T> mappedType,
+             final @Nullable String stringUtf8,
+             final JsonUtils.JsonCustomizer ... customizers) {
+         return tryRead(mappedType, DataSource.ofStringUtf8(stringUtf8), 
customizers);
+     }
+ 
+     /**
+      * Tries to deserialize JSON content from given {@link DataSource} into 
an instance of
+      * given {@code mappedType}.
+      */
+     public <T> Try<T> tryRead(
+             final @NonNull Class<T> mappedType,
+             final @NonNull DataSource source,
+             final JsonUtils.JsonCustomizer ... customizers) {
+         return source.readAll((final InputStream is)->{
+             return Try.call(()->createMapper(customizers).readValue(is, 
mappedType));
+         });
+     }
+ 
+     /**
+      * Tries to deserialize JSON content from given {@link DataSource} into a 
{@link List}
+      * with given {@code elementType}.
+      */
+     public <T> Try<List<T>> tryReadAsList(
+             final @NonNull Class<T> elementType,
+             final @NonNull DataSource source,
+             final JsonUtils.JsonCustomizer ... customizers) {
+         return source.readAll((final InputStream is)->{
+             return Try.call(()->{
+                 val mapper = createMapper(customizers);
+                 val listFactory = 
mapper.getTypeFactory().constructCollectionType(List.class, elementType);
+                 return mapper.readValue(is, listFactory);
+             });
+         });
+     }
+ 
+     // -- WRITING
+ 
+     /**
+      * Writes given {@code pojo} to given {@link DataSink}.
+      */
+     public void write(
+             final @Nullable Object pojo,
+             final @NonNull DataSink sink,
+             final JsonUtils.JsonCustomizer ... customizers) {
+         if(pojo==null) return;
+         sink.writeAll(os->
+             Try.run(()->createMapper(customizers).writeValue(os, pojo)));
+     }
+ 
+     /**
+      * Converts given {@code pojo} to an UTF8 encoded {@link String}.
+      * @return <code>null</code> if pojo is <code>null</code>
+      */
+     @SneakyThrows
+     @Nullable
+     public static String toStringUtf8(
+             final @Nullable Object pojo,
+             final JsonUtils.JsonCustomizer ... customizers) {
+         return pojo!=null
+                 ? createMapper(customizers).writeValueAsString(pojo)
+                 : null;
+     }
+ 
+     // -- CUSTOMIZERS
+ 
+     /** enable indentation for the underlying generator */
+     public ObjectMapper indentedOutput(final ObjectMapper mapper) {
+         return mapper.enable(SerializationFeature.INDENT_OUTPUT);
+     }
+ 
+     /** only properties with non-null values are to be included */
+     public ObjectMapper onlyIncludeNonNull(final ObjectMapper mapper) {
+         return mapper.setSerializationInclusion(Include.NON_NULL);
+     }
+ 
+     /** add support for JAXB annotations */
+     public ObjectMapper jaxbAnnotationSupport(final ObjectMapper mapper) {
 -        return mapper.registerModule(new JaxbAnnotationModule());
++        return mapper.registerModule(new JakartaXmlBindAnnotationModule());
+     }
+ 
+     // -- MAPPER FACTORY
+ 
+     private ObjectMapper createMapper(
+             final JsonUtils.JsonCustomizer ... customizers) {
+         var mapper = new ObjectMapper();
+         for(JsonUtils.JsonCustomizer customizer : customizers) {
+             mapper = Optional.ofNullable(customizer.apply(mapper))
+                     .orElse(mapper);
+         }
+         return mapper;
+     }
+ 
+ }
diff --cc 
commons/src/test/java/org/apache/causeway/commons/internal/resources/XmlRoundTripTest.java
index b6daf41baa,a550026a72..c4150dcf4b
--- 
a/commons/src/test/java/org/apache/causeway/commons/internal/resources/XmlRoundTripTest.java
+++ 
b/commons/src/test/java/org/apache/causeway/commons/internal/resources/XmlRoundTripTest.java
@@@ -18,16 -18,19 +18,19 @@@
   */
  package org.apache.causeway.commons.internal.resources;
  
- import org.junit.jupiter.api.Test;
- 
- import static org.junit.jupiter.api.Assertions.assertEquals;
- import static org.junit.jupiter.api.Assertions.assertNotNull;
- 
 -import javax.xml.bind.JAXBContext;
 -import javax.xml.bind.annotation.XmlAccessType;
 -import javax.xml.bind.annotation.XmlAccessorType;
 -import javax.xml.bind.annotation.XmlElement;
 -import javax.xml.bind.annotation.XmlType;
 +import jakarta.xml.bind.JAXBContext;
 +import jakarta.xml.bind.annotation.XmlAccessType;
 +import jakarta.xml.bind.annotation.XmlAccessorType;
 +import jakarta.xml.bind.annotation.XmlElement;
 +import jakarta.xml.bind.annotation.XmlType;
+ 
+ import org.junit.jupiter.api.Test;
+ 
+ import static org.junit.jupiter.api.Assertions.assertEquals;
+ import static org.junit.jupiter.api.Assertions.assertNotNull;
+ 
+ import org.apache.causeway.commons.io.JaxbUtils;
+ 
  import lombok.Data;
  import lombok.EqualsAndHashCode;
  import lombok.SneakyThrows;
diff --cc 
extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java
index 2cb08b64b4,1d9650109b..e34dbe0def
--- 
a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java
+++ 
b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java
@@@ -18,10 -18,9 +18,9 @@@
   */
  package org.apache.causeway.extensions.commandlog.applib.subscriber;
  
 -import javax.inject.Inject;
 -import javax.inject.Named;
 +import jakarta.inject.Inject;
 +import jakarta.inject.Named;
  
- import org.apache.causeway.extensions.commandlog.applib.dom.ExecuteIn;
  import org.springframework.stereotype.Service;
  
  import org.apache.causeway.applib.annotation.PriorityPrecedence;
diff --cc 
viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/serialization/SerializationStrategy.java
index 88a845ca76,d1432c3c67..e4b0582dbf
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/serialization/SerializationStrategy.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/serialization/SerializationStrategy.java
@@@ -20,9 -20,9 +20,9 @@@ package org.apache.causeway.viewer.rest
  
  import java.util.Collection;
  
 -import javax.ws.rs.core.MediaType;
 +import jakarta.ws.rs.core.MediaType;
  
- import org.apache.causeway.commons.internal.resources._Json;
+ import org.apache.causeway.commons.io.JsonUtils;
  import org.apache.causeway.viewer.restfulobjects.applib.RepresentationType;
  
  public enum SerializationStrategy {

Reply via email to