You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2022/06/02 09:01:39 UTC

[isis] branch master updated: ISIS-3049: refactor ObjectMemento, to potentially remove later

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 e7f42e8ba0 ISIS-3049: refactor ObjectMemento, to potentially remove later
e7f42e8ba0 is described below

commit e7f42e8ba0cade1c9fbc7f1dbc1ec8ef3d1449b2
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Jun 2 11:01:33 2022 +0200

    ISIS-3049: refactor ObjectMemento, to potentially remove later
---
 .../isis/applib/services/bookmark/Bookmark.java    |  1 -
 .../org/apache/isis/applib/util/ZipWriter.java     | 38 +++++------
 .../java/org/apache/isis/applib/value/Blob.java    |  2 +-
 .../binding/InternalBidirectionalBinding.java      | 27 ++++----
 .../commons/internal/exceptions/_Exceptions.java   | 16 ++++-
 .../commons/internal/ioc/_IocContainer_Spring.java |  2 +-
 .../internal/memento/_Mementos_MementoDefault.java | 11 +++-
 .../commons/internal/resources/_Serializables.java |  2 +-
 .../isis/commons/internal/resources/_Xml.java      | 10 +--
 .../_testing/MetaModelContext_forTesting.java      |  2 +-
 .../core/metamodel/commons/ClassExtensions.java    | 13 ++--
 .../context/MetaModelContext_usingIoc.java         |  2 +-
 .../core/metamodel/facets/DomainEventHelper.java   |  4 +-
 .../facets/object/grid/GridFacetDefault.java       |  2 +-
 .../facets/object/mixin/MixinFacetAbstract.java    |  4 +-
 .../ViewModelFacetForDomainObjectAnnotation.java   |  6 +-
 .../metamodel/interactions/InteractionHead.java    |  4 +-
 .../interactions/managed/ActionInteraction.java    |  2 +-
 .../create/ObjectCreator_builtinHandlers.java      |  5 +-
 .../objectmanager/memento/ObjectMemento.java       | 10 ++-
 .../memento/ObjectMementoCollection.java           |  5 +-
 .../memento/ObjectMementoForEmpty.java             |  5 +-
 .../metamodel/specloader/SpecificationLoader.java  |  2 +-
 .../specimpl/ObjectActionParameterAbstract.java    |  2 +-
 .../specloader/specimpl/ObjectMemberAbstract.java  |  2 +-
 .../command/CommandExecutorServiceDefault.java     |  2 +-
 .../memento/ObjectMementoServiceDefault.java       | 11 +---
 .../runtimeservices/memento/_ObjectMemento.java    | 73 +++++++++++-----------
 .../session/InteractionServiceDefault.java         |  2 +-
 .../_infra/resources/AsciiDocConverterService.java | 13 ++--
 .../_infra/resources/ResourceReaderService.java    | 26 ++++----
 .../AbstractUserAndRolesFixtureScript.java         |  4 +-
 .../viewer/javafx/model/binding/BindingsFx.java    |  9 ++-
 .../components/temporal/TemporalFieldFactory.java  |  4 +-
 .../jdo/datanucleus/oid/JdoObjectIdSerializer.java |  4 +-
 .../apache/isis/tooling/j2adoc/J2AdocContext.java  |  2 +-
 .../restfulobjects/client/ResponseDigest.java      |  2 +-
 .../ActionResultResponseHandlingStrategy.java      |  2 +-
 .../scalars/reference/ReferencePanel.java          |  2 +-
 .../valuechoices/ValueChoicesSelect2Panel.java     |  2 +-
 .../ui/components/widgets/select2/Select2.java     | 17 +++--
 .../ObjectAdapterMementoProviderAbstract.java      | 13 +---
 ...tAdapterMementoProviderForValueChoicesTest.java |  5 +-
 43 files changed, 189 insertions(+), 183 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java b/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java
index f5bce4590a..8cd98c67c9 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java
@@ -104,7 +104,6 @@ public final class Bookmark implements Oid {
             final String logicalTypeName,
             final String identifier,
             final String hintId) {
-
         this.logicalTypeName = logicalTypeName;
         this.identifier = identifier;
         this.hintId = hintId;
diff --git a/api/applib/src/main/java/org/apache/isis/applib/util/ZipWriter.java b/api/applib/src/main/java/org/apache/isis/applib/util/ZipWriter.java
index 06e7ddcd83..87014af74c 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/util/ZipWriter.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/util/ZipWriter.java
@@ -44,7 +44,7 @@ import lombok.val;
  * Typical use:
  * <pre>
  * val zipWriter = ZipWriter.newInstance();
- * 
+ *
  * for (Map.Entry<String, String> entry : schemaMap.entrySet()) {
  *      val namespaceUri = entry.getKey();
  *      val schemaText = entry.getValue();
@@ -52,10 +52,10 @@ import lombok.val;
  *     	    writer.write(schemaText);
  *      });
  * }
- *  
+ *
  * return BlobClobFactory.blobZip(fileName, zipWriter.toBytes());
  * </pre>
- * 
+ *
  * @since 2.0 {@index}
  * @apiNote Implementation is <em>not</em> thread safe.
  *
@@ -66,21 +66,21 @@ public class ZipWriter {
     @Value(staticConstructor = "of")
     public static class ZipOutputStreamWrapper {
         private final @NonNull ZipOutputStream zipOutputStream;
-        
+
         public void writeCharactersUtf8(final @Nullable CharSequence chars) throws IOException {
             writeCharacters(chars, StandardCharsets.UTF_8);
         }
-        
+
         public void writeCharacters(
-                final @Nullable CharSequence chars, 
+                final @Nullable CharSequence chars,
                 final @NonNull Charset charset) throws IOException {
-            if(chars==null 
+            if(chars==null
                     || chars.length()==0) {
                 return; // no-op
             }
             zipOutputStream.write(_Strings.toBytes(chars.toString(), charset));
         }
-        
+
         public void writeBytes(final @Nullable byte bytes[]) throws IOException {
             if(_NullSafe.isEmpty(bytes)) {
                 return; // no-op
@@ -88,15 +88,15 @@ public class ZipWriter {
             zipOutputStream.write(bytes);
         }
 
-        public void writeBytes(final @Nullable byte bytes[], int off, int len) throws IOException {
+        public void writeBytes(final @Nullable byte bytes[], final int off, final int len) throws IOException {
             if(_NullSafe.isEmpty(bytes)) {
                 return; // no-op
             }
             zipOutputStream.write(bytes, off, len);
         }
-        
+
     }
-    
+
     @FunctionalInterface
     public interface OnZipEntry {
         public void accept(ZipOutputStreamWrapper writer) throws IOException;
@@ -106,7 +106,7 @@ public class ZipWriter {
         return ofFailureMessage("Unable to create zip");
     }
 
-    public static ZipWriter ofFailureMessage(String failureMessage) {
+    public static ZipWriter ofFailureMessage(final String failureMessage) {
         val baos = new ByteArrayOutputStream();
         val zos = new ZipOutputStream(baos);
         val writer = new ZipOutputStreamWrapper(zos);
@@ -121,12 +121,12 @@ public class ZipWriter {
 
     /**
      * Adds a new zipEntry with given {@code zipEntryName}, and provides the
-     * {@link OutputStreamWriter} via {@link OnZipEntry} for the consumer to 
-     * write the actual (uncompressed) zip-entry content. 
+     * {@link OutputStreamWriter} via {@link OnZipEntry} for the consumer to
+     * write the actual (uncompressed) zip-entry content.
      * @param zipEntryName
      * @param onZipEntry
      */
-    public void nextEntry(String zipEntryName, OnZipEntry onZipEntry) {
+    public void nextEntry(final String zipEntryName, final OnZipEntry onZipEntry) {
         if(zippedBytes!=null) {
             throw new IllegalStateException("Cannot create a new ZipEntry an a closed ZipWriter");
         }
@@ -135,13 +135,13 @@ public class ZipWriter {
             onZipEntry.accept(writer);
             zos.closeEntry();
         } catch (final IOException e) {
-            throw _Exceptions.unrecoverable(failureMessage, e);
+            throw _Exceptions.unrecoverable(e, failureMessage);
         }
     }
 
     /**
-     * Terminal operation, closes the writer. 
-     * Calling this operation multiple times, will return the same array instance object. 
+     * Terminal operation, closes the writer.
+     * Calling this operation multiple times, will return the same array instance object.
      * @return the byte array created by the underlying ZipOutputStream
      */
     public byte[] toBytes() {
@@ -149,7 +149,7 @@ public class ZipWriter {
             try {
                 zos.close();
             } catch (IOException e) {
-                throw _Exceptions.unrecoverable(failureMessage, e);
+                throw _Exceptions.unrecoverable(e, failureMessage);
             }
             zippedBytes = baos.toByteArray();
         }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/value/Blob.java b/api/applib/src/main/java/org/apache/isis/applib/value/Blob.java
index 169deb762a..703582d20b 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/value/Blob.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/value/Blob.java
@@ -227,7 +227,7 @@ public final class Blob implements NamedWithMimeType {
                     unzippedBytes = _Bytes.of(zipInputStream);
                 } catch (IOException e) {
                     throw _Exceptions
-                        .unrecoverable(String.format("failed to read zip entry %s", zipEntry.getName()), e);
+                        .unrecoverable(e, "failed to read zip entry %s", zipEntry.getName());
                 }
                 return Blob.of(zipEntry.getName(), resultingMimeType, unzippedBytes);
             })
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/binding/InternalBidirectionalBinding.java b/commons/src/main/java/org/apache/isis/commons/internal/binding/InternalBidirectionalBinding.java
index fcbb774610..a826028a33 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/binding/InternalBidirectionalBinding.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/binding/InternalBidirectionalBinding.java
@@ -33,7 +33,7 @@ import lombok.val;
 abstract class InternalBidirectionalBinding<T>
 implements ChangeListener<T>, InternalUtil.WeakListener {
 
-    public static <T> InternalBidirectionalBinding<T> bind(Bindable<T> left, Bindable<T> right) {
+    public static <T> InternalBidirectionalBinding<T> bind(final Bindable<T> left, final Bindable<T> right) {
         checkParameters(left, right);
         val binding = new GenericBidirectionalBinding<T>(left, right);
         left.setValue(right.getValue());
@@ -42,14 +42,14 @@ implements ChangeListener<T>, InternalUtil.WeakListener {
         return binding;
     }
 
-    public static <T> void unbind(Bindable<T> left, Bindable<T> right) {
+    public static <T> void unbind(final Bindable<T> left, final Bindable<T> right) {
         checkParameters(left, right);
         val binding = new UntypedBidirectionalBinding(left, right);
         left.removeListener(binding);
         right.removeListener(binding);
     }
 
-    public static void unbind(Object left, Object right) {
+    public static void unbind(final Object left, final Object right) {
         checkParameters(left, right);
         val binding = new UntypedBidirectionalBinding(left, right);
         if (left instanceof Observable) {
@@ -76,7 +76,7 @@ implements ChangeListener<T>, InternalUtil.WeakListener {
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(final Object obj) {
         if (this == obj) {
             return true;
         }
@@ -107,7 +107,7 @@ implements ChangeListener<T>, InternalUtil.WeakListener {
 
     // -- HELPER
 
-    private static void checkParameters(@NonNull Object left, @NonNull Object right) {
+    private static void checkParameters(@NonNull final Object left, @NonNull final Object right) {
         if (left == right) {
             throw _Exceptions.illegalArgument("Cannot bind to self");
         }
@@ -115,7 +115,7 @@ implements ChangeListener<T>, InternalUtil.WeakListener {
 
     private final int cachedHash;
 
-    private InternalBidirectionalBinding(Object left, Object right) {
+    private InternalBidirectionalBinding(final Object left, final Object right) {
         cachedHash = left.hashCode() * right.hashCode();
     }
 
@@ -125,7 +125,7 @@ implements ChangeListener<T>, InternalUtil.WeakListener {
         private final WeakReference<Bindable<T>> rightRef;
         private boolean updating = false;
 
-        private GenericBidirectionalBinding(Bindable<T> left, Bindable<T> right) {
+        private GenericBidirectionalBinding(final Bindable<T> left, final Bindable<T> right) {
             super(left, right);
             leftRef = new WeakReference<Bindable<T>>(left);
             rightRef = new WeakReference<Bindable<T>>(right);
@@ -176,16 +176,15 @@ implements ChangeListener<T>, InternalUtil.WeakListener {
                 } catch (Exception e2) {
                     e2.addSuppressed(e);
                     unbind(left, right);
-                    throw _Exceptions.unrecoverableFormatted(
+                    throw _Exceptions.unrecoverable(e2,
                             "Bidirectional binding failed with an attempt to restore the "
                             + "Observable to the previous value. "
                             + "Removing the bidirectional binding from bindables %s and %s",
                             ""+left,
-                            ""+right,
-                            e2);
+                            ""+right);
                 }
-                throw _Exceptions.unrecoverable(
-                        "Bidirectional binding failed, setting to the previous value", e);
+                throw _Exceptions.unrecoverable(e,
+                        "Bidirectional binding failed, setting to the previous value");
             } finally {
                 updating = false;
             }
@@ -198,14 +197,14 @@ implements ChangeListener<T>, InternalUtil.WeakListener {
         @Getter private final Object left;
         @Getter private final Object right;
 
-        public UntypedBidirectionalBinding(Object left, Object right) {
+        public UntypedBidirectionalBinding(final Object left, final Object right) {
             super(left, right);
             this.left = left;
             this.right = right;
         }
 
         @Override
-        public void changed(Observable<? extends Object> sourceProperty, Object oldValue, Object newValue) {
+        public void changed(final Observable<? extends Object> sourceProperty, final Object oldValue, final Object newValue) {
             throw _Exceptions.unexpectedCodeReach();
         }
     }
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java b/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java
index 8a00655b0f..039224d805 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java
@@ -80,6 +80,13 @@ public final class _Exceptions {
         return new IllegalArgumentException(String.format(format, args));
     }
 
+    public static final IllegalArgumentException illegalArgument(
+            final @NonNull Throwable cause,
+            final @NonNull String format,
+            final @Nullable Object ... args) {
+        return new IllegalArgumentException(String.format(format, args), cause);
+    }
+
     // -- ILLEGAL STATE
 
     public static IllegalStateException illegalState(
@@ -141,15 +148,20 @@ public final class _Exceptions {
         return new RuntimeException(String.format("unrecoverable error: '%s'", msg));
     }
 
-    public static RuntimeException unrecoverable(final String msg, final Throwable cause) {
+    public static RuntimeException unrecoverable(final Throwable cause, final String msg) {
         return new RuntimeException(String.format("unrecoverable error: '%s' with cause ...", msg), cause);
     }
 
-    public static RuntimeException unrecoverableFormatted(final String format, final Object ...args) {
+    public static RuntimeException unrecoverable(final String format, final Object ...args) {
         return new RuntimeException(String.format("unrecoverable error: '%s'",
                 String.format(format, args)));
     }
 
+    public static RuntimeException unrecoverable(final Throwable cause, final String format, final Object ...args) {
+        return new RuntimeException(String.format("unrecoverable error: '%s' with cause ...",
+                String.format(format, args)), cause);
+    }
+
     // -- UNSUPPORTED
 
     public static UnsupportedOperationException unsupportedOperation() {
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/ioc/_IocContainer_Spring.java b/commons/src/main/java/org/apache/isis/commons/internal/ioc/_IocContainer_Spring.java
index 6818a0142e..ba1079525d 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/ioc/_IocContainer_Spring.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/ioc/_IocContainer_Spring.java
@@ -57,7 +57,7 @@ final class _IocContainer_Spring implements _IocContainer {
         try {
             return Optional.ofNullable(provider.getIfUnique());
         } catch (Exception cause) {
-            throw _Exceptions.unrecoverable("Failed to create an instance of type " + requiredType, cause);
+            throw _Exceptions.unrecoverable(cause, "Failed to create an instance of type %s", requiredType);
         }
     }
 
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/memento/_Mementos_MementoDefault.java b/commons/src/main/java/org/apache/isis/commons/internal/memento/_Mementos_MementoDefault.java
index d99334ec18..5656ee8086 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/memento/_Mementos_MementoDefault.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/memento/_Mementos_MementoDefault.java
@@ -32,9 +32,11 @@ import org.springframework.lang.Nullable;
 
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.base._NullSafe;
+import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.commons.internal.context._Context;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.commons.internal.memento._Mementos.EncoderDecoder;
 import org.apache.isis.commons.internal.memento._Mementos.Memento;
 import org.apache.isis.commons.internal.memento._Mementos.SerializingAdapter;
@@ -105,7 +107,10 @@ class _Mementos_MementoDefault implements _Mementos.Memento {
 
     // -- PARSER
 
-    static Memento parse(final @NonNull EncoderDecoder codec, final SerializingAdapter serializer, final @Nullable String str) {
+    static Memento parse(
+            final @NonNull EncoderDecoder codec,
+            final SerializingAdapter serializer,
+            final @Nullable String str) {
         if(_NullSafe.isEmpty(str)) {
             return null;
         }
@@ -123,7 +128,9 @@ class _Mementos_MementoDefault implements _Mementos.Memento {
             final HashMap<String, Serializable> valuesByKey = _Casts.uncheckedCast(ois.readObject());
             return new _Mementos_MementoDefault(codec, serializer, valuesByKey);
         } catch (Exception e) {
-            throw new IllegalArgumentException("failed to parse memento from serialized string", e);
+            throw _Exceptions.illegalArgument(e,
+                    "failed to parse memento from serialized string '%s'",
+                    _Strings.ellipsifyAtEnd(str, 200, "..."));
         }
     }
 
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/resources/_Serializables.java b/commons/src/main/java/org/apache/isis/commons/internal/resources/_Serializables.java
index 1c49570934..2fc0d3fd5f 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/resources/_Serializables.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/resources/_Serializables.java
@@ -64,7 +64,7 @@ public class _Serializables {
         try(val ois = new ObjectInputStream(content)){
             val pojo = ois.readObject();
             if(!(requiredClass.isAssignableFrom(pojo.getClass()))) {
-                throw _Exceptions.unrecoverableFormatted(
+                throw _Exceptions.unrecoverable(
                         "de-serializion of input stream did not yield an object of required type %s",
                         requiredClass.getName());
             }
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/resources/_Xml.java b/commons/src/main/java/org/apache/isis/commons/internal/resources/_Xml.java
index 22d0ed26ed..b77ef58ddb 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/resources/_Xml.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/resources/_Xml.java
@@ -182,7 +182,8 @@ public final class _Xml {
 
                 if(_NullSafe.size(errors)>0) {
 
-                    return _Exceptions.unrecoverable(String.format("Error %s, "
+                    return _Exceptions.unrecoverable(e,
+                            "Error %s, "
                             + "due to illegal annotations on object class '%s'; "
                             + "%d error(s) reported: %s",
                             doingWhat,
@@ -190,8 +191,7 @@ public final class _Xml {
                             errors.size(),
                             errors.stream()
                                 .map(Exception::getMessage)
-                                .collect(Collectors.joining("; "))),
-                            e);
+                                .collect(Collectors.joining("; ")));
                 }
 
             } catch (Exception ex) {
@@ -199,8 +199,8 @@ public final class _Xml {
             }
         }
 
-        return _Exceptions.unrecoverable(String.format("Error %s; "
-                + "object class is '%s'", doingWhat, dtoClassName), e);
+        return _Exceptions.unrecoverable(e,
+                "Error %s; object class is '%s'", doingWhat, dtoClassName);
     }
 
     private static boolean isIllegalAnnotationsException(final Exception e) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java
index 876701df0d..e47fdbf7e2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java
@@ -479,7 +479,7 @@ implements MetaModelContext {
     private final Optional<ManagedObject> toManagedObject(final _ManagedBeanAdapter managedBeanAdapter) {
 
         val servicePojo = managedBeanAdapter.getInstance().getFirst()
-                .orElseThrow(()->_Exceptions.unrecoverableFormatted(
+                .orElseThrow(()->_Exceptions.unrecoverable(
                         "Cannot get service instance of type '%s'",
                         managedBeanAdapter.getBeanClass()));
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/commons/ClassExtensions.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/commons/ClassExtensions.java
index 970c129851..1f263be22f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/commons/ClassExtensions.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/commons/ClassExtensions.java
@@ -33,8 +33,6 @@ import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.commons.internal.resources._Resources;
 
-import lombok.val;
-
 public final class ClassExtensions {
 
 
@@ -65,12 +63,11 @@ public final class ClassExtensions {
                     constructor = extendee.getConstructor();
                     return constructor.newInstance();
                 } catch (final NoSuchMethodException e) {
-                    val msg = String.format("Failed to call contructor for type %s trying, "
-                            + "args '%s' then trying no args.",
-                            extendee.getName(),
-                            _Lists.of(constructorParamTypes).toString());
-
-                    throw _Exceptions.unrecoverable(msg, e);
+                    throw _Exceptions.unrecoverable(e,
+                            "Failed to call contructor for type %s trying, "
+                                    + "args '%s' then trying no args.",
+                                    extendee.getName(),
+                                    _Lists.of(constructorParamTypes).toString());
                 }
             }
         } catch (final SecurityException | IllegalArgumentException | IllegalAccessException | InstantiationException | InvocationTargetException ex) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/context/MetaModelContext_usingIoc.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/context/MetaModelContext_usingIoc.java
index cce36b1edc..8fb707d282 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/context/MetaModelContext_usingIoc.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/context/MetaModelContext_usingIoc.java
@@ -169,7 +169,7 @@ class MetaModelContext_usingIoc implements MetaModelContext {
 
     private ManagedObject toManagedObject(final _ManagedBeanAdapter managedBeanAdapter) {
         val servicePojo = managedBeanAdapter.getInstance().getFirst()
-                .orElseThrow(()->_Exceptions.unrecoverableFormatted(
+                .orElseThrow(()->_Exceptions.unrecoverable(
                         "Cannot get service instance of type '%s'",
                         managedBeanAdapter.getBeanClass()));
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java
index 747ba0b80c..36ebc3d2e0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java
@@ -421,8 +421,8 @@ public class DomainEventHelper {
             return constructor.newInstance(args);
         } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                 | InvocationTargetException e) {
-            throw _Exceptions.unrecoverableFormatted(
-                    "failed to invoke constructor %s", constructor, e);
+            throw _Exceptions.unrecoverable(e,
+                    "failed to invoke constructor %s", constructor);
         }
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetDefault.java
index 894d55ea38..7d8b1d94c6 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/grid/GridFacetDefault.java
@@ -93,7 +93,7 @@ implements GridFacet {
         }
 
         if(!getSpecification().equals(objectAdapter.getSpecification())) {
-            throw _Exceptions.unrecoverableFormatted(
+            throw _Exceptions.unrecoverable(
                     "getGrid(adapter) was called passing an adapter (type: %s), "
                     + "for which this GridFacet (type: %s) is not responsible; "
                     + "indicates that some framework internals are wired up in a wrong way",
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/mixin/MixinFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/mixin/MixinFacetAbstract.java
index 8aa7df4c6c..94b7eacfce 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/mixin/MixinFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/mixin/MixinFacetAbstract.java
@@ -69,7 +69,7 @@ implements MixinFacet {
     @Override
     public Object instantiate(final Object mixee) {
         if(constructor == null) {
-            throw _Exceptions.unrecoverableFormatted(
+            throw _Exceptions.unrecoverable(
                     "Failed to instantiate mixin. "
                     + "Invalid mix-in declaration of type %s, missing contructor", mixinType);
         }
@@ -88,7 +88,7 @@ implements MixinFacet {
             getServiceInjector().injectServicesInto(mixinPojo);
             return mixinPojo;
         } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
-            throw _Exceptions.unrecoverableFormatted(
+            throw _Exceptions.unrecoverable(e,
                     "Failed to instantiate mixin. "
                     + "Invalid mix-in declaration of type %s, "
                     + "failing instance construction with %s", mixinType, e);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/viewmodel/ViewModelFacetForDomainObjectAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/viewmodel/ViewModelFacetForDomainObjectAnnotation.java
index 45d0f3f1a2..a7ec19673c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/viewmodel/ViewModelFacetForDomainObjectAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/viewmodel/ViewModelFacetForDomainObjectAnnotation.java
@@ -82,7 +82,7 @@ extends ViewModelFacetAbstract {
 
         val viewmodel = viewmodelSpec.createObject();
 
-        val memento = parseMemento(bookmark.getIdentifier());
+        val memento = parseMemento(bookmark);
         val mementoKeys = memento.keySet();
 
         if(mementoKeys.isEmpty()) {
@@ -157,9 +157,9 @@ extends ViewModelFacetAbstract {
         return _Mementos.create(codec, serializer);
     }
 
-    private _Mementos.Memento parseMemento(final String input) {
+    private _Mementos.Memento parseMemento(final Bookmark bookmark) {
         ensureDependenciesInited();
-        return _Mementos.parse(codec, serializer, input);
+        return _Mementos.parse(codec, serializer, bookmark.getIdentifier());
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/InteractionHead.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/InteractionHead.java
index c9c54ad376..fcfd4b9971 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/InteractionHead.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/InteractionHead.java
@@ -77,12 +77,12 @@ public class InteractionHead {
     private static InteractionHead of(final @NonNull ManagedObject owner, final @NonNull ManagedObject target) {
         if(ManagedObjects.isSpecified(owner)
                 && owner.getSpecification().getBeanSort().isMixin()) {
-            throw _Exceptions.unrecoverableFormatted("unexpected: owner is a mixin %s", owner);
+            throw _Exceptions.unrecoverable("unexpected: owner is a mixin %s", owner);
         }
         if(ManagedObjects.isSpecified(target)
                 && target.getSpecification().getBeanSort().isMixin()
                 && target.getPojo()==null) {
-            throw _Exceptions.unrecoverableFormatted("target not spec. %s", target);
+            throw _Exceptions.unrecoverable("target not spec. %s", target);
         }
         return new InteractionHead(owner, target);
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteraction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteraction.java
index 0afce5ccba..e582096569 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteraction.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ActionInteraction.java
@@ -273,7 +273,7 @@ extends MemberInteraction<ManagedAction, ActionInteraction> {
     // -- HELPER
 
     private static RuntimeException onMissingDefaultsProvider(final ObjectFeature feature) {
-        return _Exceptions.unrecoverableFormatted("Could not find a DefaultsProvider for ObjectFeature %s",
+        return _Exceptions.unrecoverable("Could not find a DefaultsProvider for ObjectFeature %s",
                 feature.getFeatureIdentifier());
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/create/ObjectCreator_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/create/ObjectCreator_builtinHandlers.java
index 2d0aa78031..0b12b5d0ab 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/create/ObjectCreator_builtinHandlers.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/create/ObjectCreator_builtinHandlers.java
@@ -19,7 +19,6 @@
 package org.apache.isis.core.metamodel.objectmanager.create;
 
 import java.lang.reflect.Array;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
 
 import org.apache.isis.commons.internal._Constants;
@@ -103,8 +102,8 @@ final class ObjectCreator_builtinHandlers {
                 return newInstance;
 
             } catch (Exception  e) {
-                throw _Exceptions.unrecoverable(
-                        "Failed to create instance of type " + spec.getFullIdentifier(), e);
+                throw _Exceptions.unrecoverable(e,
+                        "Failed to create instance of type %s", spec.getFullIdentifier());
             }
 
         }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMemento.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMemento.java
index 5b7d35427a..c1bde41ae5 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMemento.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMemento.java
@@ -34,7 +34,15 @@ import org.apache.isis.commons.internal.collections._Lists;
  */
 public interface ObjectMemento extends HasLogicalType, Serializable {
 
-    String asString();
+    /**
+     * In a strict sense, bookmarks are only available for viewmodels, entities and managed beans,
+     * not for values or enums. However, the {@link Bookmark} as an immutable value,
+     * is also perfectly suitable to represent an enum value or any value type.
+     * @apiNote this is an intermediate refactoring step,
+     * possibly providing a way of getting rid of {@link ObjectMemento} entirely,
+     * with {@link Bookmark} being the replacement
+     */
+    Bookmark asPseudoBookmark();
 
     /**
      * Returns a bookmark only if
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMementoCollection.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMementoCollection.java
index 5098651a91..6abb7c035b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMementoCollection.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMementoCollection.java
@@ -44,8 +44,8 @@ public final class ObjectMementoCollection implements ObjectMemento {
     @NonNull private final LogicalType logicalType;
 
     @Override
-    public String asString() {
-        return getContainer().toString();
+    public Bookmark asPseudoBookmark() {
+        throw _Exceptions.notImplemented(); // please unwrap at call-site
     }
 
     @Override
@@ -62,5 +62,4 @@ public final class ObjectMementoCollection implements ObjectMemento {
         return getContainer();
     }
 
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMementoForEmpty.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMementoForEmpty.java
index c8e7dc8c8e..dbe130ff63 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMementoForEmpty.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/memento/ObjectMementoForEmpty.java
@@ -34,8 +34,8 @@ public class ObjectMementoForEmpty implements ObjectMemento {
     @NonNull private LogicalType logicalType;
 
     @Override
-    public String asString() {
-        return getLogicalTypeName();
+    public Bookmark asPseudoBookmark() {
+        return Bookmark.empty(logicalType);
     }
 
     @Override
@@ -48,5 +48,4 @@ public class ObjectMementoForEmpty implements ObjectMemento {
         return null;
     }
 
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
index 78a784d09c..673b5948f7 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
@@ -133,7 +133,7 @@ public interface SpecificationLoader {
 
     default LogicalType lookupLogicalTypeElseFail(@NonNull final String logicalTypeName) {
         return lookupLogicalType(logicalTypeName)
-        .orElseThrow(()->_Exceptions.unrecoverableFormatted(
+        .orElseThrow(()->_Exceptions.unrecoverable(
                 "Lookup of logical-type-name '%s' failed, also found no matching fully qualified "
                         + "class name to use instead. This indicates, that the class we are not finding here"
                         + " is not discovered by Spring during bootstrapping of this application.",
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java
index c88c25c013..6a95578098 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java
@@ -132,7 +132,7 @@ implements
         return lookupFacet(ParamNamedFacet.class)
         .map(ParamNamedFacet::translated)
         .orElseThrow(()->_Exceptions
-                .unrecoverableFormatted("action parameters must have a ParamNamedFacet %s", this));
+                .unrecoverable("action parameters must have a ParamNamedFacet %s", this));
     }
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
index 8ebec66ab8..f028465926 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
@@ -110,7 +110,7 @@ implements
         val namedFacet = getFacet(MemberNamedFacet.class);
 
         if(namedFacet==null) {
-            throw _Exceptions.unrecoverableFormatted("no MemberNamedFacet preset on %s", getFeatureIdentifier());
+            throw _Exceptions.unrecoverable("no MemberNamedFacet preset on %s", getFeatureIdentifier());
         }
 
         return namedFacet
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
index a932c328ea..213b2d07ad 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
@@ -234,7 +234,7 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
                 val targetAdapter = valueMarshaller.recoverReferenceFrom(targetOidDto);
 
                 if(ManagedObjects.isNullOrUnspecifiedOrEmpty(targetAdapter)) {
-                    throw _Exceptions.unrecoverableFormatted("cannot recreate ManagedObject from bookmark %s",
+                    throw _Exceptions.unrecoverable("cannot recreate ManagedObject from bookmark %s",
                             Bookmark.forOidDto(targetOidDto));
                 }
 
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/ObjectMementoServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/ObjectMementoServiceDefault.java
index 069a40a9a2..7f58c252e3 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/ObjectMementoServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/ObjectMementoServiceDefault.java
@@ -158,7 +158,7 @@ public class ObjectMementoServiceDefault implements ObjectMementoService {
             return objectMementoAdapter.reconstructObject(mmc);
         }
 
-        throw _Exceptions.unrecoverableFormatted("unsupported ObjectMemento type %s", memento.getClass());
+        throw _Exceptions.unrecoverable("unsupported ObjectMemento type %s", memento.getClass());
     }
 
     @RequiredArgsConstructor(staticName = "of")
@@ -169,8 +169,8 @@ public class ObjectMementoServiceDefault implements ObjectMementoService {
         private final _ObjectMemento delegate;
 
         @Override
-        public String asString() {
-            return delegate.asString();
+        public Bookmark asPseudoBookmark() {
+            return delegate.asPseudoBookmark();
         }
 
         @Override
@@ -192,11 +192,6 @@ public class ObjectMementoServiceDefault implements ObjectMementoService {
             return delegate.reconstructObject(mmc);
         }
 
-        @Override
-        public String toString() {
-            return delegate.toString();
-        }
-
     }
 
 }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/_ObjectMemento.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/_ObjectMemento.java
index e7a9958df2..bd4f97c8dd 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/_ObjectMemento.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/memento/_ObjectMemento.java
@@ -87,10 +87,14 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
             public ManagedObject asAdapter(
                     final _ObjectMemento memento,
                     final MetaModelContext mmc) {
-
                 return memento.recreateStrategy.recreateObject(memento, mmc);
             }
 
+            @Override
+            public Bookmark asPseudoBookmark(final _ObjectMemento memento) {
+                return memento.recreateStrategy.asPseudoBookmark(memento);
+            }
+
             @Override
             public int hashCode(final _ObjectMemento memento) {
                 return memento.recreateStrategy.hashCode(memento);
@@ -107,11 +111,6 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
                 }
                 return memento.recreateStrategy.equals(memento, otherMemento);
             }
-
-            @Override
-            public String asString(final _ObjectMemento memento) {
-                return memento.recreateStrategy.toString(memento);
-            }
         },
         /**
          * represents a list of objects
@@ -128,6 +127,13 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
                 return ManagedObject.lazy(mmc.getSpecificationLoader(), listOfPojos);
             }
 
+            @Override
+            public Bookmark asPseudoBookmark(final _ObjectMemento memento) {
+                return Bookmark.forLogicalTypeNameAndIdentifier(
+                        memento.getLogicalTypeName(),
+                        memento.list.toString());
+            }
+
             @Override
             public int hashCode(final _ObjectMemento memento) {
                 return memento.list.hashCode();
@@ -145,10 +151,6 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
                 return memento.list.equals(otherMemento.list);
             }
 
-            @Override
-            public String asString(final _ObjectMemento memento) {
-                return memento.list.toString();
-            }
         };
 
         void ensure(final Cardinality sort) {
@@ -166,7 +168,7 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
 
         public abstract boolean equals(_ObjectMemento memento, Object other);
 
-        public abstract String asString(_ObjectMemento memento);
+        public abstract Bookmark asPseudoBookmark(_ObjectMemento memento);
     }
 
     private enum RecreateStrategy {
@@ -184,13 +186,20 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
                 val valueSerializer = mmc.getSpecificationLoader()
                         .specForLogicalType(memento.logicalType)
                         .flatMap(spec->Facets.valueSerializer(spec, spec.getCorrespondingClass()))
-                        .orElseThrow(()->_Exceptions.unrecoverableFormatted(
+                        .orElseThrow(()->_Exceptions.unrecoverable(
                                 "logical type %s is expected to have a ValueFacet", memento.logicalType));
 
                 return mmc.getObjectManager().adapt(
                         valueSerializer.fromEncodedString(Format.JSON, memento.encodableValue));
             }
 
+            @Override
+            public Bookmark asPseudoBookmark(final _ObjectMemento memento) {
+                return Bookmark.forLogicalTypeNameAndIdentifier(
+                        memento.getLogicalTypeName(),
+                        memento.encodableValue);
+            }
+
             @Override
             public boolean equals(
                     final _ObjectMemento memento,
@@ -205,11 +214,6 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
                 return memento.encodableValue.hashCode();
             }
 
-            @Override
-            public String toString(final _ObjectMemento memento) {
-                return memento.encodableValue;
-            }
-
             @Override
             public void resetVersion(
                     final _ObjectMemento memento,
@@ -260,6 +264,11 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
                 memento.persistentOidStr = ManagedObjects.stringifyElseFail(adapter);
             }
 
+            @Override
+            public Bookmark asPseudoBookmark(final _ObjectMemento memento) {
+                return memento.asBookmark();
+            }
+
             @Override
             public boolean equals(final _ObjectMemento oam, final _ObjectMemento other) {
                 return other.recreateStrategy == LOOKUP
@@ -271,11 +280,6 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
                 return oam.persistentOidStr.hashCode();
             }
 
-            @Override
-            public String toString(final _ObjectMemento oam) {
-                return oam.persistentOidStr;
-            }
-
         },
         /**
          * If all other strategies fail, as last resort we use plain java serialization, provided
@@ -307,8 +311,10 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
             }
 
             @Override
-            public String toString(final _ObjectMemento memento) {
-                return "ObjectMementoWkt {SERIALIZABLE " + memento.getLogicalTypeName() + "}";
+            public Bookmark asPseudoBookmark(final _ObjectMemento memento) {
+                return Bookmark.forLogicalTypeNameAndIdentifier(
+                        memento.getLogicalTypeName(),
+                        "SERIALIZABLE");
             }
 
             @Override
@@ -329,7 +335,7 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
 
         public abstract int hashCode(_ObjectMemento memento);
 
-        public abstract String toString(_ObjectMemento memento);
+        public abstract Bookmark asPseudoBookmark(_ObjectMemento memento);
 
         public abstract void resetVersion(
                 _ObjectMemento memento,
@@ -408,7 +414,7 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
         // -- // TODO[2112] do we ever need to create ENCODEABLE here?
         val logicalTypeName = bookmark.getLogicalTypeName();
         val spec = specificationLoader.specForLogicalTypeName(logicalTypeName)
-                .orElseThrow(()->_Exceptions.unrecoverableFormatted(
+                .orElseThrow(()->_Exceptions.unrecoverable(
                         "cannot recreate spec from logicalTypeName %s", logicalTypeName));
 
         this.cardinality = Cardinality.SCALAR;
@@ -524,6 +530,10 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
         return cardinality.asAdapter(this, mmc);
     }
 
+    public Bookmark asPseudoBookmark() {
+        return cardinality.asPseudoBookmark(this);
+    }
+
     @Override
     public int hashCode() {
         return cardinality.hashCode(this);
@@ -534,16 +544,6 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
         return cardinality.equals(this, obj);
     }
 
-
-    @Override
-    public String toString() {
-        return asString();
-    }
-
-    public String asString() {
-        return cardinality.asString(this);
-    }
-
     // -- FUNCTIONS
 
     @NoArgsConstructor(access = AccessLevel.PRIVATE)
@@ -571,5 +571,4 @@ final class _ObjectMemento implements HasLogicalType, Serializable {
         getCardinality().ensure(Cardinality.SCALAR);
     }
 
-
 }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionServiceDefault.java
index c46723e71d..2fdc21f4c5 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionServiceDefault.java
@@ -397,7 +397,7 @@ implements
         if(interaction instanceof IsisInteraction) {
             return (IsisInteraction) interaction;
         }
-        throw _Exceptions.unrecoverableFormatted("the framework does not recognice "
+        throw _Exceptions.unrecoverable("the framework does not recognice "
                 + "this implementation of an Interaction: %s", interaction.getClass().getName());
     }
 
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/_infra/resources/AsciiDocConverterService.java b/examples/demo/domain/src/main/java/demoapp/dom/_infra/resources/AsciiDocConverterService.java
index 388a19f52f..6b8b79fc9a 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/_infra/resources/AsciiDocConverterService.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/_infra/resources/AsciiDocConverterService.java
@@ -24,9 +24,8 @@ import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.asciidoctor.Asciidoctor;
-import org.asciidoctor.AttributesBuilder;
+import org.asciidoctor.Attributes;
 import org.asciidoctor.Options;
-import org.asciidoctor.OptionsBuilder;
 import org.asciidoctor.SafeMode;
 import org.asciidoctor.ast.Document;
 import org.asciidoctor.extension.IncludeProcessor;
@@ -50,14 +49,14 @@ public class AsciiDocConverterService {
     public AsciiDocConverterService(final ResourceReaderService resourceReaderService) {
         this.resourceReaderService = resourceReaderService;
         this.asciidoctor = createAsciidoctor();
-        this.options = OptionsBuilder.options()
+        this.options = Options.builder()
                 .safe(SafeMode.UNSAFE)
                 .toFile(false)
-                .attributes(AttributesBuilder.attributes()
+                .attributes(Attributes.builder()
                         .sourceHighlighter("prism")
                         .icons("font")
-                        .get())
-                .get();
+                        .build())
+                .build();
 
     }
 
@@ -74,7 +73,7 @@ public class AsciiDocConverterService {
             public void process(final Document document, final PreprocessorReader reader, final String target, final Map<String, Object> attributes) {
                 val contextClass = context.get();
                 val content = resourceReaderService.readResource(contextClass, target, attributes);
-                reader.push_include(content, target, target, 1, attributes);
+                reader.pushInclude(content, target, target, 1, attributes);
             }
         }
 
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/_infra/resources/ResourceReaderService.java b/examples/demo/domain/src/main/java/demoapp/dom/_infra/resources/ResourceReaderService.java
index 4e3c724663..dfab46cb2f 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/_infra/resources/ResourceReaderService.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/_infra/resources/ResourceReaderService.java
@@ -38,10 +38,10 @@ import lombok.val;
 @Named("demo.ResourceReaderService")
 public class ResourceReaderService {
 
-    public String readResource(Class<?> aClass, String resourceName) {
+    public String readResource(final Class<?> aClass, final String resourceName) {
         return readResource(aClass, resourceName, Collections.emptyMap());
     }
-    public String readResource(Class<?> aClass, String resourceName, Map<String, Object> attributes) {
+    public String readResource(final Class<?> aClass, final String resourceName, final Map<String, Object> attributes) {
         InputStream resourceStream = aClass.getResourceAsStream(resourceName);
         if(resourceStream==null) {
             // horrendous hack...
@@ -57,16 +57,16 @@ public class ResourceReaderService {
         }
     }
 
-    /**
-     * Read the given {@code input} into a String, while also pre-processing placeholders.
-     *
-     * @param input
-     * @return
-     * @throws IOException
-     */
-    private String read(InputStream input) throws IOException {
-        return read(input, Collections.emptyMap());
-    }
+//    /**
+//     * Read the given {@code input} into a String, while also pre-processing placeholders.
+//     *
+//     * @param input
+//     * @return
+//     * @throws IOException
+//     */
+//    private String read(InputStream input) throws IOException {
+//        return read(input, Collections.emptyMap());
+//    }
 
     /**
      * Read the given {@code input} into a String, while also pre-processing placeholders.
@@ -75,7 +75,7 @@ public class ResourceReaderService {
      * @return
      * @throws IOException
      */
-    private String read(InputStream input, Map<String, Object> attributes) throws IOException {
+    private String read(final InputStream input, final Map<String, Object> attributes) throws IOException {
         val in = new InputStreamReader(input);
         val tagHandler = new TagHandler(attributes);
         try (val bufferReader = new BufferedReader(in)) {
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/fixtures/AbstractUserAndRolesFixtureScript.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/fixtures/AbstractUserAndRolesFixtureScript.java
index 75f73ca0ba..7097ca3686 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/fixtures/AbstractUserAndRolesFixtureScript.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/fixtures/AbstractUserAndRolesFixtureScript.java
@@ -148,7 +148,7 @@ public abstract class AbstractUserAndRolesFixtureScript extends FixtureScript {
             }
 
             if(applicationUser == null) {
-                throw _Exceptions.unrecoverableFormatted("failed to create user '%s'", username);
+                throw _Exceptions.unrecoverable("failed to create user '%s'", username);
             }
 
             // update tenancy (repository checks for null)
@@ -166,7 +166,7 @@ public abstract class AbstractUserAndRolesFixtureScript extends FixtureScript {
 
     }
 
-    private static <T> Supplier<T> nullSafe(Supplier<T> supplier) {
+    private static <T> Supplier<T> nullSafe(final Supplier<T> supplier) {
         return supplier != null ? supplier : () -> null;
     }
 
diff --git a/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java
index bb92da0901..193a8f6f3d 100644
--- a/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java
+++ b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java
@@ -206,16 +206,15 @@ public class BindingsFx {
                     e2.addSuppressed(e);
                     left.removeListener(this);
                     right.removeListener(this);
-                    throw _Exceptions.unrecoverableFormatted(
+                    throw _Exceptions.unrecoverable(e2,
                             "Bidirectional binding failed with an attempt to restore the "
                             + "Observable to the previous value. "
                             + "Removing the bidirectional binding from bindables %s and %s",
                             ""+left,
-                            ""+right,
-                            e2);
+                            ""+right);
                 }
-                throw _Exceptions.unrecoverable(
-                        "Bidirectional binding failed, setting to the previous value", e);
+                throw _Exceptions.unrecoverable(e,
+                        "Bidirectional binding failed, setting to the previous value");
             } finally {
                 updating = false;
             }
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java
index 3613711fc5..6459940e69 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java
@@ -105,7 +105,7 @@ public class TemporalFieldFactory implements UiComponentHandlerVaa {
         if(request.isFeatureTypeEqualTo(java.util.Date.class)) {
             return TemporalCharacteristic.DATE_TIME;
         }
-        throw _Exceptions.unrecoverableFormatted("type %s not handled", request.getFeatureType());
+        throw _Exceptions.unrecoverable("type %s not handled", request.getFeatureType());
     }
 
     private OffsetCharacteristic getOffsetCharacteristic(final ComponentRequest request) {
@@ -119,7 +119,7 @@ public class TemporalFieldFactory implements UiComponentHandlerVaa {
         if(request.isFeatureTypeEqualTo(java.util.Date.class)) {
             return OffsetCharacteristic.LOCAL;
         }
-        throw _Exceptions.unrecoverableFormatted("type %s not handled", request.getFeatureType());
+        throw _Exceptions.unrecoverable("type %s not handled", request.getFeatureType());
     }
 
     private Optional<TemporalValueSemantics<?>> getTemporalValueSemantics(final ComponentRequest request) {
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/oid/JdoObjectIdSerializer.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/oid/JdoObjectIdSerializer.java
index 4acea86798..c3af52b7d4 100644
--- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/oid/JdoObjectIdSerializer.java
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/oid/JdoObjectIdSerializer.java
@@ -213,8 +213,8 @@ public final class JdoObjectIdSerializer {
                                 final Object dnOid = cons.newInstance(keyStr);
                                 return dnOid.toString();
                             } catch (ClassNotFoundException | IllegalArgumentException | InstantiationException | IllegalAccessException | SecurityException | InvocationTargetException | NoSuchMethodException e) {
-                                throw _Exceptions.unrecoverableFormatted(
-                                        "failed to instantiate %s with arg %s", clsName, keyStr, e);
+                                throw _Exceptions.unrecoverable(e,
+                                        "failed to instantiate %s with arg %s", clsName, keyStr);
                             }
                         })
                 );
diff --git a/tooling/java2adoc/src/main/java/org/apache/isis/tooling/j2adoc/J2AdocContext.java b/tooling/java2adoc/src/main/java/org/apache/isis/tooling/j2adoc/J2AdocContext.java
index 2e4a262e84..647783d014 100644
--- a/tooling/java2adoc/src/main/java/org/apache/isis/tooling/j2adoc/J2AdocContext.java
+++ b/tooling/java2adoc/src/main/java/org/apache/isis/tooling/j2adoc/J2AdocContext.java
@@ -87,7 +87,7 @@ public class J2AdocContext {
         val unitKey = LookupKey.of(unit.getResourceCoordinates());
         val previousKey = unitIndex.put(unitKey, unit);
         if(previousKey!=null) {
-            throw _Exceptions.unrecoverableFormatted(
+            throw _Exceptions.unrecoverable(
                     "J2AUnit index entries must be unique, "
                     + "index key collision on \nexists: %s\nnew:    %s",
                     previousKey,
diff --git a/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/ResponseDigest.java b/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/ResponseDigest.java
index d2e94fa5c7..8ce7dd9731 100644
--- a/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/ResponseDigest.java
+++ b/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/ResponseDigest.java
@@ -183,7 +183,7 @@ class ResponseDigest<T> {
 
         } catch (Exception e) {
             entities = Can.empty();
-            failureCause = _Exceptions.unrecoverable("failed to read JAX-RS response content", e);
+            failureCause = _Exceptions.unrecoverable(e, "failed to read JAX-RS response content");
         }
 
         return this;
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java
index c000791757..43951ba972 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java
@@ -101,7 +101,7 @@ public enum ActionResultResponseHandlingStrategy {
                         scheduleJs(target, javascriptFor_sameWindow(fullUrl), 100);
                     }
                 } else {
-                    throw _Exceptions.unrecoverableFormatted(
+                    throw _Exceptions.unrecoverable(
                             "no logic implemented to handle IRequestHandler of type %s",
                             requestHandler.getClass().getName());
                 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
index 0f8c326857..28f51aeb99 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
@@ -75,7 +75,7 @@ public class ReferencePanel extends ScalarPanelSelectAbstract {
 
     @Override
     protected String obtainOutputFormat() {
-        return select2.obtainInlinePromptModel().getObject();
+        return select2.obtainOutputFormatModel().getObject();
     }
 
     @Override
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java
index 3317273893..bb09114b7d 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java
@@ -76,7 +76,7 @@ extends ScalarPanelSelectAbstract {
 
     @Override
     protected String obtainOutputFormat() {
-        return select2.obtainInlinePromptModel2().getObject();
+        return select2.obtainOutputFormatModelForValue().getObject();
     }
 
     // --
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2.java
index c94cde150e..038e39196f 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2.java
@@ -152,21 +152,26 @@ implements
         mementoModel().setObject(null);
     }
 
-    public IModel<String> obtainInlinePromptModel() {
+    public IModel<String> obtainOutputFormatModel() {
         return LambdaModel.<String>of(()->{
-            val memento = mementoModel().getObject();
+            val memento = memento();
             if(memento == null) {
                 return null;
             }
             val adapter = getCommonContext().reconstructObject(memento);
-            return adapter != null ? adapter.titleString() : null;
+            return adapter != null
+                    ? adapter.titleString()
+                    : null;
         });
     }
 
-    public IModel<String> obtainInlinePromptModel2() {
+    @Deprecated // value case should be handled by obtainInlinePromptModel()
+    public IModel<String> obtainOutputFormatModelForValue() {
         return LambdaModel.<String>of(()->{
-            final ObjectMemento inlinePromptMemento = this.memento();
-            return inlinePromptMemento != null ? inlinePromptMemento.asString(): null;
+            val memento = memento();
+            return memento != null
+                    ? memento.asPseudoBookmark().getIdentifier()
+                    : null;
         });
     }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java
index e21da48406..656f730441 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/providers/ObjectAdapterMementoProviderAbstract.java
@@ -76,18 +76,7 @@ extends ChoiceProvider<ObjectMemento> {
         if (choiceMemento == null) {
             return PlaceholderLiteral.NULL_REPRESENTATION.asText(this::translate);
         }
-        val logicalType = choiceMemento.getLogicalType();
-        val spec = getCommonContext().getSpecificationLoader()
-                .specForLogicalType(logicalType)
-                .orElse(null);
-
-        // support enums that are implementing an interface; only know this late in the day
-        // TODO: this is a hack, really should push this deeper so that Encodeable OAMs also prefix themselves with their logicalTypeName
-        if(spec != null && spec.isValue()) {
-            return logicalType.getLogicalTypeName() + ":" + choiceMemento.asString();
-        } else {
-            return choiceMemento.asString();
-        }
+        return choiceMemento.asPseudoBookmark().stringify();
     }
 
     @Override
diff --git a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ObjectAdapterMementoProviderForValueChoicesTest.java b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ObjectAdapterMementoProviderForValueChoicesTest.java
index d88cceea58..029055d8ef 100644
--- a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ObjectAdapterMementoProviderForValueChoicesTest.java
+++ b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ObjectAdapterMementoProviderForValueChoicesTest.java
@@ -32,6 +32,7 @@ import org.junit.Test;
 import static org.hamcrest.CoreMatchers.is;
 
 import org.apache.isis.applib.id.LogicalType;
+import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2;
 import org.apache.isis.core.metamodel.objectmanager.memento.ObjectMemento;
@@ -113,8 +114,8 @@ public class ObjectAdapterMementoProviderForValueChoicesTest {
             allowing(mock).getLogicalType();
             will(returnValue(logicalType));
 
-            allowing(mock).asString();
-            will(returnValue(id));
+            allowing(mock).asPseudoBookmark();
+            will(returnValue(Bookmark.forLogicalTypeAndIdentifier(logicalType, id)));
         }});
         return mock;
     }