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

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


The following commit(s) were added to refs/heads/v3 by this push:
     new da85a3ede45 CAUSEWAY-2297: ditch ActionMemento and 
ActionParameterMemento (make ObjectAction serializable)
da85a3ede45 is described below

commit da85a3ede45373fd2b0ed6fa7d5b0d9326dd0f00
Author: Andi Huber <[email protected]>
AuthorDate: Sat Nov 23 18:07:43 2024 +0100

    CAUSEWAY-2297: ditch ActionMemento and ActionParameterMemento (make
    ObjectAction serializable)
---
 .../core/metamodel/spec/feature/ObjectAction.java  |  8 --
 .../spec/feature/ObjectActionParameter.java        |  7 --
 .../spec/feature/memento/ActionMemento.java        | 83 --------------------
 .../feature/memento/ActionParameterMemento.java    | 90 ----------------------
 .../specloader/specimpl/ObjectActionDefault.java   | 32 +++++++-
 .../specloader/specimpl/ObjectActionMixedIn.java   |  1 +
 .../domainmodel/DomainModelTest_serialization.java | 76 ++++++++++++++++++
 .../interaction/act/ActionInteractionWkt.java      | 10 +--
 8 files changed, 110 insertions(+), 197 deletions(-)

diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectAction.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectAction.java
index 619b6eba3e5..fdb38c350e2 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectAction.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectAction.java
@@ -53,7 +53,6 @@ import 
org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.ManagedObjects;
 import org.apache.causeway.core.metamodel.spec.ActionScope;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
-import org.apache.causeway.core.metamodel.spec.feature.memento.ActionMemento;
 import 
org.apache.causeway.core.metamodel.specloader.specimpl.ObjectActionDefault;
 import 
org.apache.causeway.core.metamodel.specloader.specimpl.ObjectActionMixedIn;
 
@@ -264,13 +263,6 @@ public interface ObjectAction extends ObjectMember {
         return prefix + ownerId + "-" + getId();
     }
 
-    /**
-     * Returns a serializable representation of this action.
-     */
-    default ActionMemento getMemento() {
-        return ActionMemento.forAction(this);
-    }
-
     default PromptStyle getPromptStyle() {
         var promptStyle = lookupFacet(PromptStyleFacet.class)
                 .map(PromptStyleFacet::value);
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectActionParameter.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectActionParameter.java
index f6b1037f660..f9a28d6b663 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectActionParameter.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/ObjectActionParameter.java
@@ -36,7 +36,6 @@ import 
org.apache.causeway.core.metamodel.interactions.managed.ParameterNegotiat
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.ManagedObjects;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
-import 
org.apache.causeway.core.metamodel.spec.feature.memento.ActionParameterMemento;
 import org.apache.causeway.core.metamodel.util.Facets;
 
 import lombok.NonNull;
@@ -284,10 +283,4 @@ extends ObjectFeature, CurrentHolder {
         return getAction().getCssClass(prefix) + "-" + getId();
     }
 
-    /**
-     * Returns a serializable representation of this parameter.
-     */
-    default ActionParameterMemento getMemento() {
-        return ActionParameterMemento.forActionParameter(this);
-    }
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/memento/ActionMemento.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/memento/ActionMemento.java
deleted file mode 100644
index f0379f900c4..00000000000
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/memento/ActionMemento.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.core.metamodel.spec.feature.memento;
-
-import java.io.Serializable;
-import java.util.function.Supplier;
-
-import org.apache.causeway.applib.Identifier;
-import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
-import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
-
-import lombok.AccessLevel;
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NonNull;
-import lombok.Synchronized;
-
-/**
- * {@link Serializable} representation of an {@link ObjectAction}
- *
- * @implNote thread-safe memoization
- *
- * @since 2.0 {index}
- */
-@EqualsAndHashCode
-@AllArgsConstructor(access = AccessLevel.PROTECTED)
-public class ActionMemento implements Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    @EqualsAndHashCode.Include
-    @Getter private final @NonNull Identifier identifier;
-
-    // -- FACTORY
-
-    public static ActionMemento forAction(final @NonNull ObjectAction action) {
-        return new ActionMemento(
-                action.getFeatureIdentifier(),
-                action);
-    }
-
-    // -- LOAD/UNMARSHAL
-
-    @EqualsAndHashCode.Exclude
-    private transient ObjectAction action;
-
-    @Synchronized
-    public ObjectAction getAction(final @NonNull Supplier<SpecificationLoader> 
specLoader) {
-        if (action == null) {
-            action = specLoader.get()
-                    
.specForLogicalTypeElseFail(getIdentifier().getLogicalType())
-                    .getActionElseFail(
-                            
getIdentifier().getMemberNameAndParameterClassNamesIdentityString());
-        }
-        return action;
-    }
-
-    // -- OBJECT CONTRACT
-
-    @Override
-    public String toString() {
-        return getIdentifier().getLogicalTypeName() + "#"
-                + 
getIdentifier().getMemberNameAndParameterClassNamesIdentityString();
-    }
-
-}
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/memento/ActionParameterMemento.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/memento/ActionParameterMemento.java
deleted file mode 100644
index f3e645f5b06..00000000000
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/memento/ActionParameterMemento.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.core.metamodel.spec.feature.memento;
-
-import java.io.Serializable;
-import java.util.function.Supplier;
-
-import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
-import org.apache.causeway.core.metamodel.spec.feature.ObjectActionParameter;
-import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
-
-import lombok.AccessLevel;
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NonNull;
-import lombok.Synchronized;
-
-/**
- * {@link Serializable} representation of an {@link ObjectActionParameter 
parameter}
- * that belongs to an {@link ObjectAction}.
- *
- * @implNote thread-safe memoization
- *
- * @see ActionMemento
- *
- * @since 2.0 {index}
- */
-@EqualsAndHashCode
-@AllArgsConstructor(access = AccessLevel.PROTECTED)
-public class ActionParameterMemento implements Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    @EqualsAndHashCode.Include
-    @Getter private final @NonNull ActionMemento actionMemento;
-
-    @EqualsAndHashCode.Include
-    @Getter private final int number;
-
-    // -- FACTORY
-
-    public static ActionParameterMemento forActionParameter(
-            final @NonNull ObjectActionParameter actionParameter) {
-        return new ActionParameterMemento(
-                actionParameter.getAction().getMemento(),
-                actionParameter.getParameterIndex(),
-                actionParameter);
-    }
-
-    // -- LOAD/UNMARSHAL
-
-    @EqualsAndHashCode.Exclude
-    private transient ObjectActionParameter actionParameter;
-
-    @Synchronized
-    public ObjectActionParameter getActionParameter(final @NonNull 
Supplier<SpecificationLoader> specLoader) {
-        if (actionParameter == null) {
-            this.actionParameter = actionMemento
-                    .getAction(specLoader)
-                    .getParameters()
-                    .getElseFail(number);
-        }
-        return actionParameter;
-    }
-
-    // -- OBJECT CONTRACT
-
-    @Override
-    public String toString() {
-        return getActionMemento().toString() + "[" + getNumber() + "]";
-    }
-
-}
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionDefault.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionDefault.java
index 712a8ebd8cc..a5afc9570bc 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionDefault.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionDefault.java
@@ -18,6 +18,9 @@
  */
 package org.apache.causeway.core.metamodel.specloader.specimpl;
 
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.UUID;
@@ -42,6 +45,7 @@ import org.apache.causeway.core.metamodel.commons.UtilStr;
 import org.apache.causeway.core.metamodel.consent.Consent;
 import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.causeway.core.metamodel.consent.InteractionResultSet;
+import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
 import org.apache.causeway.core.metamodel.facetapi.FeatureType;
 import org.apache.causeway.core.metamodel.facets.FacetedMethod;
@@ -72,7 +76,8 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 public class ObjectActionDefault
 extends ObjectMemberAbstract
-implements ObjectAction {
+implements ObjectAction, Serializable {
+    private static final long serialVersionUID = 1L;
 
     public static ActionScope getType(final String typeStr) {
         final ActionScope type = ActionScope.valueOf(typeStr);
@@ -587,7 +592,7 @@ implements ObjectAction {
 
     // -- HELPER
 
-    protected String argsFor(Can<ObjectActionParameter> parameters, 
Can<ManagedObject> arguments) {
+    protected String argsFor(final Can<ObjectActionParameter> parameters, 
final Can<ManagedObject> arguments) {
         if(parameters.size() != arguments.size()) {
             return "???"; // shouldn't happen
         }
@@ -620,4 +625,27 @@ implements ObjectAction {
                 || methodFacade.synthesize(ActionLayout.class).isPresent();
     }
 
+    // -- SERIALIZATION PROXY
+
+    protected final Object writeReplace() {
+        return new SerializationProxy(this);
+    }
+
+    protected final void readObject(final ObjectInputStream stream) throws 
InvalidObjectException {
+        throw new InvalidObjectException("Proxy required");
+    }
+
+    protected record SerializationProxy(Identifier identifier) implements 
Serializable {
+        SerializationProxy(final ObjectActionDefault action) {
+            this(action.getFeatureIdentifier());
+        }
+        private Object readResolve() {
+            return MetaModelContext.instanceElseFail()
+                .getSpecificationLoader()
+                .specForLogicalTypeElseFail(identifier.getLogicalType())
+                .getActionElseFail(
+                        
identifier.getMemberNameAndParameterClassNamesIdentityString());
+        }
+    }
+
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionMixedIn.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionMixedIn.java
index 08b373575d5..f0fdddc1b90 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionMixedIn.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectActionMixedIn.java
@@ -49,6 +49,7 @@ import lombok.extern.log4j.Log4j2;
 public class ObjectActionMixedIn
 extends ObjectActionDefault
 implements MixedInMember {
+    private static final long serialVersionUID = 1L;
 
     // -- FACTORIES
 
diff --git 
a/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_serialization.java
 
b/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_serialization.java
new file mode 100644
index 00000000000..7570c0fcf4d
--- /dev/null
+++ 
b/regressiontests/domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_serialization.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.testdomain.domainmodel;
+
+import jakarta.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.TestPropertySource;
+
+import org.apache.causeway.commons.internal.testing._SerializationTester;
+import org.apache.causeway.core.config.presets.CausewayPresets;
+import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
+import 
org.apache.causeway.core.metamodel.specloader.specimpl.ObjectActionDefault;
+import 
org.apache.causeway.core.metamodel.specloader.specimpl.ObjectActionMixedIn;
+import org.apache.causeway.testdomain.conf.Configuration_headless;
+import 
org.apache.causeway.testdomain.model.good.Configuration_usingValidDomain;
+import org.apache.causeway.testdomain.model.good.ProperMemberSupport;
+import 
org.apache.causeway.testing.integtestsupport.applib.CausewayIntegrationTestAbstract;
+
+@SpringBootTest(
+        classes = {
+                Configuration_headless.class,
+                Configuration_usingValidDomain.class,
+
+        },
+        properties = {
+                
//"causeway.core.meta-model.introspector.policy=annotation_optional",
+                "causeway.core.meta-model.introspector.mode=FULL",
+                "causeway.applib.annotation.domain-object.editing=TRUE",
+                
"causeway.core.meta-model.validator.explicit-object-type=FALSE" // does not 
override any of the imports
+        })
+@TestPropertySource({
+    CausewayPresets.SilenceMetaModel,
+    CausewayPresets.SilenceProgrammingModel
+})
+class DomainModelTest_serialization extends CausewayIntegrationTestAbstract {
+
+    @Inject private SpecificationLoader specificationLoader;
+
+    @Test
+    void objectActionDefault_shouldBe_Serializable() {
+        var holderSpec = 
specificationLoader.specForTypeElseFail(ProperMemberSupport.class);
+        var act = (ObjectActionDefault) 
holderSpec.getDeclaredAction("hideMe").orElseThrow();
+        assertEquals(ObjectActionDefault.class, act.getClass());
+        _SerializationTester.assertEqualsOnRoundtrip(act);
+    }
+
+    @Test
+    void objectActionMixedIn_shouldBe_Serializable() {
+        var holderSpec = 
specificationLoader.specForTypeElseFail(ProperMemberSupport.class);
+        var mixin = (ObjectActionMixedIn) 
holderSpec.getDeclaredAction("openRestApi").orElseThrow(); // built-in mixin 
support
+        assertEquals(ObjectActionMixedIn.class, mixin.getClass());
+        _SerializationTester.assertEqualsOnRoundtrip(mixin);
+    }
+
+}
diff --git 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
index 0547bfc9b22..fcfca864d5d 100644
--- 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
+++ 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
@@ -18,7 +18,6 @@
  */
 package org.apache.causeway.viewer.wicket.model.models.interaction.act;
 
-import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.IntStream;
 import java.util.stream.Stream;
@@ -38,7 +37,6 @@ import 
org.apache.causeway.core.metamodel.interactions.managed.ParameterNegotiat
 import 
org.apache.causeway.core.metamodel.interactions.managed.PendingParamsSnapshot;
 import org.apache.causeway.core.metamodel.object.ManagedObjects;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
-import org.apache.causeway.core.metamodel.spec.feature.memento.ActionMemento;
 import org.apache.causeway.viewer.wicket.model.models.EntityCollectionModel;
 import org.apache.causeway.viewer.wicket.model.models.InlinePromptContext;
 import org.apache.causeway.viewer.wicket.model.models.ScalarParameterModel;
@@ -80,7 +78,7 @@ extends HasBookmarkedOwnerAbstract<ActionInteraction> {
      * <p>
      * nullable in support of lazy evaluation
      */
-    private @Nullable ActionMemento actionMemento;
+    private @Nullable ObjectAction actionMemento;
 
     private Can<UiParameterWkt> childModels;
     private @Nullable ScalarPropertyModel associatedWithPropertyIfAny;
@@ -128,7 +126,6 @@ extends HasBookmarkedOwnerAbstract<ActionInteraction> {
         this.memberId = memberId;
         this.where = where;
         this.actionMemento = objectAction
-                    .map(ObjectAction::getMemento) // if present, eagerly 
memoize
                     .orElse(null);
         this.associatedWithPropertyIfAny = associatedWithPropertyIfAny;
         this.associatedWithParameterIfAny = associatedWithParameterIfAny;
@@ -170,14 +167,13 @@ extends HasBookmarkedOwnerAbstract<ActionInteraction> {
             var objectAction = actionInteraction().getMetamodel()
                 .orElseThrow(()->_Exceptions
                         .noSuchElement("could not resolve action by memberId 
'%s'", memberId));
-            this.actionMemento = objectAction.getMemento();
+            this.actionMemento = objectAction;
             return objectAction;
         }
 
         // re-attachment fails, if the owner is not found (eg. deleted entity),
         // hence we return the directly memoized meta-model of the underlying 
action
-        return 
Objects.requireNonNull(actionMemento.getAction(this::getSpecificationLoader),
-                ()->"framework bug: lost objectAction on model recycling 
(serialization issue)");
+        return actionMemento;
     }
 
     public Optional<ScalarPropertyModel> associatedWithProperty() {

Reply via email to