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);
+ }
+
+}