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 75a54c1cbf ISIS-3103: SerializingAdapterDefault restored to former 
behavior, but
75a54c1cbf is described below

commit 75a54c1cbf4df665512eac41ccbe6e89156062e0
Author: Andi Huber <[email protected]>
AuthorDate: Mon Aug 1 11:17:45 2022 +0200

    ISIS-3103: SerializingAdapterDefault restored to former behavior, but
    
    using IdSerializers as fallback if required
---
 .../IdStringifierForSerializable.java              |  51 +-------
 .../idstringifiers/PredefinedSerializables.java    |  79 ++++++++++++
 .../isis/commons/internal/memento/_Mementos.java   |  12 +-
 .../serializing/SerializingAdapterDefault.java     | 136 +++++++++------------
 .../SerializingAdapterWithIdStringifier.java       | 102 ++++++++++++++++
 5 files changed, 248 insertions(+), 132 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/idstringifiers/IdStringifierForSerializable.java
 
b/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/idstringifiers/IdStringifierForSerializable.java
index 8f0646d64d..3551314c68 100644
--- 
a/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/idstringifiers/IdStringifierForSerializable.java
+++ 
b/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/idstringifiers/IdStringifierForSerializable.java
@@ -20,8 +20,6 @@
 package org.apache.isis.applib.services.bookmark.idstringifiers;
 
 import java.io.Serializable;
-import java.util.List;
-import java.util.Set;
 
 import javax.annotation.Priority;
 import javax.inject.Inject;
@@ -29,14 +27,10 @@ import javax.inject.Inject;
 import org.springframework.stereotype.Component;
 
 import org.apache.isis.applib.annotation.PriorityPrecedence;
-import org.apache.isis.applib.graph.tree.TreeState;
-import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.IdStringifier;
 import org.apache.isis.applib.services.urlencoding.UrlEncodingService;
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.base._Strings;
-import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.commons.internal.memento._Mementos;
 
 import lombok.NonNull;
@@ -68,7 +62,7 @@ public class IdStringifierForSerializable extends 
IdStringifier.Abstract<Seriali
 
     @Override
     public boolean handles(final @NonNull Class<?> candidateValueClass) {
-        return isPredefinedSerializable(candidateValueClass);
+        return 
PredefinedSerializables.isPredefinedSerializable(candidateValueClass);
     }
 
     @Override
@@ -96,47 +90,4 @@ public class IdStringifierForSerializable extends 
IdStringifier.Abstract<Seriali
         return _Mementos.parse(codec, serializer, input);
     }
 
-    private static final Set<Class<? extends Serializable>> 
serializableFinalTypes = _Sets.of(
-            String.class, String[].class,
-            Class.class, Class[].class,
-            Character.class, Character[].class, char[].class,
-            Boolean.class, Boolean[].class, boolean[].class,
-            // Numbers
-            Byte[].class, byte[].class,
-            Short[].class, short[].class,
-            Integer[].class, int[].class,
-            Long[].class, long[].class,
-            Float[].class, float[].class,
-            Double[].class, double[].class
-    );
-
-    private static final List<Class<? extends Serializable>> serializableTypes 
= _Lists.of(
-            java.util.Date.class,
-            java.sql.Date.class,
-            Enum.class,
-            Bookmark.class,
-            TreeState.class
-    );
-
-    private boolean isPredefinedSerializable(final Class<?> cls) {
-        if (!Serializable.class.isAssignableFrom(cls)) {
-            return false;
-        }
-        // primitive ... boolean, byte, char, short, int, long, float, and 
double.
-        if (cls.isPrimitive() || Number.class.isAssignableFrom(cls)) {
-            return true;
-        }
-        //[ahuber] any non-scalar values could be problematic, so we are 
careful with wild-cards here
-        if (cls.getName().startsWith("java.time.")) {
-            return true;
-        }
-        if (cls.getName().startsWith("org.joda.time.")) {
-            return true;
-        }
-        if (serializableFinalTypes.contains(cls)) {
-            return true;
-        }
-        return serializableTypes.stream().anyMatch(t -> 
t.isAssignableFrom(cls));
-    }
-
 }
diff --git 
a/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/idstringifiers/PredefinedSerializables.java
 
b/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/idstringifiers/PredefinedSerializables.java
new file mode 100644
index 0000000000..41c40f96bf
--- /dev/null
+++ 
b/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/idstringifiers/PredefinedSerializables.java
@@ -0,0 +1,79 @@
+/*
+ *  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.isis.applib.services.bookmark.idstringifiers;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.isis.applib.graph.tree.TreeState;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.commons.internal.collections._Sets;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class PredefinedSerializables {
+
+    public boolean isPredefinedSerializable(final Class<?> cls) {
+        if(!Serializable.class.isAssignableFrom(cls)) {
+            return false;
+        }
+        // primitive ... boolean, byte, char, short, int, long, float, and 
double.
+        if(cls.isPrimitive() || Number.class.isAssignableFrom(cls)) {
+            return true;
+        }
+        // any non-scalar values could be problematic, so we are careful with 
wild-cards here
+        if(cls.getName().startsWith("java.time.")) {
+            return true;
+        }
+        if(cls.getName().startsWith("org.joda.time.")) {
+            return true;
+        }
+        if(serializableFinalTypes.contains(cls)) {
+            return true;
+        }
+        return serializableTypes.stream().anyMatch(t->t.isAssignableFrom(cls));
+    }
+
+    // -- HELPER
+
+    private static final Set<Class<? extends Serializable>> 
serializableFinalTypes = _Sets.of(
+            String.class, String[].class,
+            Class.class, Class[].class,
+            Character.class, Character[].class, char[].class,
+            Boolean.class, Boolean[].class, boolean[].class,
+            // Numbers
+            Byte[].class, byte[].class,
+            Short[].class, short[].class,
+            Integer[].class, int[].class,
+            Long[].class, long[].class,
+            Float[].class, float[].class,
+            Double[].class, double[].class
+            );
+
+    private static final List<Class<? extends Serializable>> serializableTypes 
= List.of(
+            java.util.Date.class,
+            java.sql.Date.class,
+            Enum.class,
+            Bookmark.class,
+            TreeState.class
+            );
+
+}
diff --git 
a/commons/src/main/java/org/apache/isis/commons/internal/memento/_Mementos.java 
b/commons/src/main/java/org/apache/isis/commons/internal/memento/_Mementos.java
index 7426cb3e94..cb91a65567 100644
--- 
a/commons/src/main/java/org/apache/isis/commons/internal/memento/_Mementos.java
+++ 
b/commons/src/main/java/org/apache/isis/commons/internal/memento/_Mementos.java
@@ -29,6 +29,8 @@ import java.util.Set;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 
+import lombok.NonNull;
+
 /**
  * <h1>- internal use only -</h1>
  * <p>
@@ -99,7 +101,7 @@ public final class _Mementos {
          * Note: write and read are complementary operators.
          * @param value
          */
-        public Serializable write(Object value);
+        public Serializable write(@NonNull Object value);
 
         /**
          * Converts the {@link Serializable} {@code value} as read from an 
{@link ObjectInput} back into its
@@ -108,7 +110,7 @@ public final class _Mementos {
          * @param cls the expected type which to cast the {@code value} to 
(required)
          * @param value
          */
-        public <T> T read(Class<T> cls, Serializable value);
+        public <T> T read(@NonNull Class<T> cls, @NonNull Serializable value);
     }
 
 
@@ -127,7 +129,7 @@ public final class _Mementos {
      * @param serializer (required)
      * @return non-null
      */
-    public static Memento create(EncoderDecoder codec, SerializingAdapter 
serializer) {
+    public static Memento create(final EncoderDecoder codec, final 
SerializingAdapter serializer) {
         return new _Mementos_MementoDefault(codec, serializer);
     }
 
@@ -163,12 +165,12 @@ public final class _Mementos {
     private static final class EmptyMemento implements Memento {
 
         @Override
-        public <T> T get(String name, Class<T> cls) {
+        public <T> T get(final String name, final Class<T> cls) {
             return null;
         }
 
         @Override
-        public Memento put(String name, Object value) {
+        public Memento put(final String name, final Object value) {
             throw _Exceptions.notImplemented();
         }
 
diff --git 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/serializing/SerializingAdapterDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/serializing/SerializingAdapterDefault.java
index d70ea3b2c9..7cf1720cb7 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/serializing/SerializingAdapterDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/serializing/SerializingAdapterDefault.java
@@ -19,36 +19,36 @@
 package org.apache.isis.core.runtimeservices.serializing;
 
 import java.io.Serializable;
-import java.util.Optional;
 
 import javax.annotation.Priority;
 import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.PriorityPrecedence;
-import org.apache.isis.applib.exceptions.unrecoverable.ObjectNotFoundException;
 import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.bookmark.IdStringifier;
-import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import 
org.apache.isis.applib.services.bookmark.idstringifiers.PredefinedSerializables;
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.commons.internal.memento._Mementos.SerializingAdapter;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.ManagedObjects;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.runtime.idstringifier.IdStringifierLookupService;
 import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
 
-import lombok.val;
+import lombok.NonNull;
+import lombok.Value;
 
 /**
- * This service enables a serializable 'bookmark' to be created for an entity.
+ * Default implementation of {@link SerializingAdapter}, intended as an 
'internal' service.
+ *
+ * @implNote uses {@link Bookmark} or {@link StringifiedValueMemento}
+ * for identifiable objects or any non {@link Serializable} objects,
+ * while some predefined serializable types that implement {@link Serializable}
+ * are written/read directly
+ *
+ * @see PredefinedSerializables
  */
 @Service
 @Named(IsisModuleCoreRuntimeServices.NAMESPACE + ".SerializingAdapterDefault")
@@ -56,96 +56,78 @@ import lombok.val;
 @Qualifier("Default")
 public class SerializingAdapterDefault implements SerializingAdapter {
 
-    @Inject private SpecificationLoader specificationLoader;
-    @Inject private WrapperFactory wrapperFactory;
-    @Inject private ObjectManager objectManager;
-    @Inject private MetaModelContext mmc;
+    @Inject private BookmarkService bookmarkService;
     @Inject private IdStringifierLookupService idStringifierLookupService;
 
     @Override
-    public <T> T read(final Class<T> valueClass, final Serializable value) {
-
-        val idStringifierIfAny = idStringifierLookupService.lookup(valueClass);
-        if(idStringifierIfAny.isPresent()) {
-            final IdStringifier<T> idStringifier = idStringifierIfAny.get();
-            return idStringifier.destring((String)value, null);
+    public Serializable write(final @NonNull Object value) {
+        if(PredefinedSerializables.isPredefinedSerializable(value.getClass())) 
{
+            // the value can be stored/written directly without conversion to 
a bookmark
+            return (Serializable) value;
         }
 
-        // see if the value can be handled as a Bookmark
+        // potentially not working for non serializable value-types
+        return bookmarkService.bookmarkFor(value)
+                .map(Serializable.class::cast)
+                // so fallback to registered IdStringifieries
+                .orElseGet(()->writeNonPredefinedSerializableValue(value));
+    }
+
+    @Override
+    public <T> T read(final @NonNull Class<T> valueClass, final @NonNull 
Serializable value) {
+
+        // see if required/desired value-class is Bookmark, then just cast
         if(Bookmark.class.equals(valueClass)) {
             return _Casts.uncheckedCast(value);
         }
 
         // otherwise, perhaps the value itself is a Bookmark, in which case we 
treat it as a
-        // reference to an Object (probably an entity) to be looked up.
+        // reference to an Object (probably an entity) to be looked up
         if(Bookmark.class.isAssignableFrom(value.getClass())) {
-            final Bookmark valueAsBookmark = (Bookmark) value;
-            return _Casts.uncheckedCast(lookup(valueAsBookmark).orElse(null));
+            final Bookmark valueBookmark = (Bookmark) value;
+            return _Casts.uncheckedCast(bookmarkService.lookup(valueBookmark)
+                        .orElse(null));
         }
 
-        return _Casts.uncheckedCast(value);
-    }
+        // otherwise, perhaps the value itself is a StringifiedValueMemento, 
in which case we treat it as a
+        // memento for a non-predefined-serializable value to be reconstructed
+        if(value instanceof StringifiedValueMemento) {
+            return readNonPredefinedSerializableValue(valueClass, 
(StringifiedValueMemento) value);
+        }
 
-    @Override
-    public Serializable write(final Object value) {
-        return write(_Casts.uncheckedCast(value), value.getClass());
+        // otherwise, the value was directly stored/written, so just recover 
as is
+        return _Casts.uncheckedCast(value);
     }
 
     // -- HELPER
 
-    private <T> Serializable write(final T value, final Class<T> aClass) {
-
-        Optional<IdStringifier<T>> idStringifierIfAny = 
idStringifierLookupService.lookup(aClass);
-        if(idStringifierIfAny.isPresent()) {
-            final IdStringifier<T> idStringifier = idStringifierIfAny.get();
-            return idStringifier.enstring(value);
-        }
-
-        return bookmarkForElseFail(value);
+    @Value(staticConstructor = "of")
+    private static final class StringifiedValueMemento implements Serializable 
{
+        private static final long serialVersionUID = 1L;
+        private final String stringifiedValue;
     }
 
-    // why would we ever store Service Beans as Bookmarks?
-    // - ANSWER: because it might be used by the CommandService to replay a 
command or exec in the background.
-    private Optional<Object> lookup(final @Nullable Bookmark bookmark) {
-        try {
-            return mmc.loadObject(bookmark)
-                    .map(ManagedObject::getPojo);
-        } catch(ObjectNotFoundException ex) {
-            return Optional.empty();
-        }
-    }
+    private <T> StringifiedValueMemento writeNonPredefinedSerializableValue(
+            final @NonNull T value) {
 
-    private Optional<Bookmark> bookmarkFor(final @Nullable Object 
domainObject) {
-        if(domainObject == null) {
-            return Optional.empty();
-        }
-        val adapter = objectManager.adapt(unwrapped(domainObject));
-        if(!ManagedObjects.isIdentifiable(adapter)){
-            // eg values cannot be bookmarked
-            return Optional.empty();
-        }
-        return Optional.of(
-                objectManager.bookmarkObject(adapter));
-    }
+        final Class<T> valueClass = _Casts.uncheckedCast(value.getClass());
 
-    private Object unwrapped(final Object domainObject) {
-        return wrapperFactory != null
-                ? wrapperFactory.unwrap(domainObject)
-                : domainObject;
+        return idStringifierLookupService.lookup(valueClass)
+            .map(idStringifier->idStringifier.enstring(value))
+            
.map(stringifiedValue->StringifiedValueMemento.of(stringifiedValue))
+            .orElseThrow(()->
+                _Exceptions.unrecoverable("cannot create a memento for object 
of type %s", valueClass));
     }
 
+    private <T> T readNonPredefinedSerializableValue(
+            final @NonNull Class<T> valueClass,
+            final @NonNull StringifiedValueMemento memento) {
 
-    private Bookmark bookmarkForElseFail(final @Nullable Object domainObject) {
-        return bookmarkFor(domainObject)
-                .orElseThrow(
-                        ()->_Exceptions.illegalArgument(
-                        "cannot create bookmark for type %s",
-                        domainObject!=null
-                            ? 
specificationLoader.specForType(domainObject.getClass())
-                                    .map(spec->spec.toString())
-                                    
.orElseGet(()->domainObject.getClass().getName())
-                            : "<null>"));
+        return idStringifierLookupService.lookup(valueClass)
+                
.map(idStringifier->idStringifier.destring(memento.getStringifiedValue(), null))
+                .orElseThrow(()->
+                    _Exceptions.unrecoverable("cannot restore object of type 
%s from memento '%s'",
+                            valueClass, memento));
     }
 
-
 }
diff --git 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/serializing/SerializingAdapterWithIdStringifier.java
 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/serializing/SerializingAdapterWithIdStringifier.java
new file mode 100644
index 0000000000..76f801d2fd
--- /dev/null
+++ 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/serializing/SerializingAdapterWithIdStringifier.java
@@ -0,0 +1,102 @@
+/*
+ *  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.isis.core.runtimeservices.serializing;
+
+import java.io.Serializable;
+import java.util.Optional;
+
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.PriorityPrecedence;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.bookmark.IdStringifier;
+import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.commons.internal.memento._Mementos.SerializingAdapter;
+import org.apache.isis.core.runtime.idstringifier.IdStringifierLookupService;
+import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
+
+import lombok.NonNull;
+import lombok.val;
+
+/**
+ * Special implementation of {@link SerializingAdapter}, intended as an 
'internal' service.
+ *
+ * @implNote uses {@link Bookmark} for any non {@link Serializable} objects, 
while
+ * any {@link Serializable} objects are written/read directly
+ */
+@Service
+@Named(IsisModuleCoreRuntimeServices.NAMESPACE + 
".SerializingAdapterWithIdStringifier")
+@Priority(PriorityPrecedence.MIDPOINT)
+@Deprecated
+public class SerializingAdapterWithIdStringifier implements SerializingAdapter 
{
+
+    @Inject private IdStringifierLookupService idStringifierLookupService;
+    @Inject private BookmarkService bookmarkService;
+
+    @Override
+    public <T> T read(final @NonNull Class<T> valueClass, final @NonNull 
Serializable value) {
+
+        val idStringifierIfAny = idStringifierLookupService.lookup(valueClass);
+        if(idStringifierIfAny.isPresent()) {
+            final IdStringifier<T> idStringifier = idStringifierIfAny.get();
+            return idStringifier.destring((String)value, null);
+        }
+
+        // see if required/desired value-class is Bookmark, then just cast
+        if(Bookmark.class.equals(valueClass)) {
+            return _Casts.uncheckedCast(value);
+        }
+
+        // otherwise, perhaps the value itself is a Bookmark, in which case we 
treat it as a
+        // reference to an Object (probably an entity) to be looked up
+        if(Bookmark.class.isAssignableFrom(value.getClass())) {
+            final Bookmark valueBookmark = (Bookmark) value;
+            //TODO[ISIS-3103] ... potentially not working for non serializable 
value-types
+            return 
_Casts.uncheckedCast(bookmarkService.lookup(valueBookmark).orElse(null));
+        }
+
+        // otherwise, the value was directly stored/written, so just recover 
as is
+        return _Casts.uncheckedCast(value);
+    }
+
+    @Override
+    public Serializable write(final Object value) {
+        return write(_Casts.uncheckedCast(value), value.getClass());
+    }
+
+    // -- HELPER
+
+    private <T> Serializable write(final T value, final Class<T> aClass) {
+
+        Optional<IdStringifier<T>> idStringifierIfAny = 
idStringifierLookupService.lookup(aClass);
+        if(idStringifierIfAny.isPresent()) {
+            final IdStringifier<T> idStringifier = idStringifierIfAny.get();
+            return idStringifier.enstring(value);
+        }
+
+        //TODO[ISIS-3103] ... potentially not working for non serializable 
value-types
+        return bookmarkService.bookmarkForElseFail(value);
+    }
+
+}

Reply via email to