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 2021/11/24 15:41:42 UTC

[isis] branch master updated: ISIS-2877: proof of concept, that EncoderDecoder can be replaced with a ValueType Mixin

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 048e02d  ISIS-2877: proof of concept, that EncoderDecoder can be replaced with a ValueType Mixin
048e02d is described below

commit 048e02d8cc91310e55bd38a7524490d1226cf93b
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Nov 24 16:36:23 2021 +0100

    ISIS-2877: proof of concept, that EncoderDecoder can be replaced with a
    ValueType Mixin
    
    - also introduces a pass-through mode for execution (action-interaction/
    property-modification)
---
 .../applib/services/iactn/ActionInvocation.java    |  3 ++-
 .../applib/value/semantics/EncoderDecoder.java     |  4 +++
 .../metamodel/consent/InteractionInitiatedBy.java  | 28 ++++++++++++++++++-
 ...ctionInvocationFacetForDomainEventAbstract.java |  7 +++--
 .../interactions/managed/ManagedAction.java        | 11 ++++++--
 .../specloader/specimpl/ObjectActionDefault.java   |  4 ++-
 .../valuesemantics/OidDtoValueSemantics.java       | 31 ++++++++++++++++++++--
 .../executor/MemberExecutorServiceDefault.java     | 24 ++++++++++++++---
 .../isis/testdomain/value/ValueSemanticsTest.java  | 31 ++++++++++++++++++++++
 9 files changed, 130 insertions(+), 13 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/iactn/ActionInvocation.java b/api/applib/src/main/java/org/apache/isis/applib/services/iactn/ActionInvocation.java
index 403eb40..47943e6 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/iactn/ActionInvocation.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/iactn/ActionInvocation.java
@@ -30,7 +30,8 @@ import lombok.Getter;
 /**
  * @since 1.x {@index}
  */
-public class ActionInvocation extends Execution<ActionInvocationDto, ActionDomainEvent<?>> {
+public class ActionInvocation
+extends Execution<ActionInvocationDto, ActionDomainEvent<?>> {
 
     @Getter
     private final List<Object> args;
diff --git a/api/applib/src/main/java/org/apache/isis/applib/value/semantics/EncoderDecoder.java b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/EncoderDecoder.java
index 536647d..68a6814 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/value/semantics/EncoderDecoder.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/value/semantics/EncoderDecoder.java
@@ -66,4 +66,8 @@ public interface EncoderDecoder<T> {
      */
     T fromEncodedString(String encodedString);
 
+    default Object getConstructorExtractor(final T value) {
+        return null;
+    }
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/consent/InteractionInitiatedBy.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/consent/InteractionInitiatedBy.java
index 4e9f411..f8e6ddf 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/consent/InteractionInitiatedBy.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/consent/InteractionInitiatedBy.java
@@ -26,7 +26,33 @@ import org.apache.isis.core.metamodel.interactions.InteractionContext;
  */
 public enum InteractionInitiatedBy {
 
+    /**
+     * must be authorized, with transactions, with publishing, with domain events
+     */
     USER,
-    FRAMEWORK;
+
+    /**
+     * always authorized, with transactions, with publishing, with domain events
+     */
+    FRAMEWORK,
+
+    /**
+     * always authorized, no transactions, no publishing, no domain events
+     */
+    PASS_THROUGH;
+
+    /**
+     * @see #USER
+     */
+    public boolean isUser() {
+        return this==USER;
+    }
+
+    /**
+     * @see #PASS_THROUGH
+     */
+    public boolean isPassThrough() {
+        return this==PASS_THROUGH;
+    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java
index 1c8f159..499b367 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionInvocationFacetForDomainEventAbstract.java
@@ -30,6 +30,7 @@ import org.apache.isis.applib.services.iactn.ActionInvocation;
 import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
 import org.apache.isis.applib.services.registry.ServiceRegistry;
 import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.functional.Result;
 import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.collections._Arrays;
 import org.apache.isis.core.metamodel.commons.CanonicalParameterUtil;
@@ -91,8 +92,10 @@ implements ImperativeFacet {
             final Can<ManagedObject> argumentAdapters,
             final InteractionInitiatedBy interactionInitiatedBy) {
 
-        val executionResult =
-                getTransactionService().callWithinCurrentTransactionElseCreateNew(()->
+        val executionResult = interactionInitiatedBy.isPassThrough()
+                ? Result.of(()->
+                    doInvoke(owningAction, head, argumentAdapters, interactionInitiatedBy))
+                : getTransactionService().callWithinCurrentTransactionElseCreateNew(()->
                     doInvoke(owningAction, head, argumentAdapters, interactionInitiatedBy));
 
         //PersistableTypeGuard.instate(executionResult);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedAction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedAction.java
index e57f4a7..b62018c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedAction.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedAction.java
@@ -121,18 +121,25 @@ public final class ManagedAction extends ManagedMember {
 
     // -- INTERACTION
 
-    public _Either<ManagedObject, InteractionVeto> invoke(@NonNull final Can<ManagedObject> actionParameters) {
+    public _Either<ManagedObject, InteractionVeto> invoke(
+            final @NonNull Can<ManagedObject> actionParameters,
+            final @NonNull InteractionInitiatedBy interactionInitiatedBy) {
 
         if(isValueTypeMixin()) {
             return _Either.left(invokeValueTypeMixin(actionParameters));
         }
 
         final ManagedObject actionResult = getAction()
-                .execute(interactionHead(), actionParameters, InteractionInitiatedBy.USER);
+                .execute(interactionHead(), actionParameters, interactionInitiatedBy);
 
         return _Either.left(route(actionResult));
     }
 
+    public _Either<ManagedObject, InteractionVeto> invoke(
+            final @NonNull Can<ManagedObject> actionParameters) {
+        return invoke(actionParameters, InteractionInitiatedBy.USER);
+    }
+
     @SneakyThrows
     public ManagedObject invokeWithRuleChecking(
             final @NonNull Can<ManagedObject> actionParameters) throws AuthorizationException {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java
index a51b2af..dfc1878 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java
@@ -393,7 +393,9 @@ implements ObjectAction {
         _Assert.assertEquals(this.getParameterCount(), argumentAdapters.size(),
                 "action's parameter count and provided argument count must match");
 
-        setupCommand(head, argumentAdapters);
+        if(!interactionInitiatedBy.isPassThrough()) {
+            setupCommand(head, argumentAdapters);
+        }
 
         return this.executeInternal(head, argumentAdapters, interactionInitiatedBy);
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/OidDtoValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/OidDtoValueSemantics.java
index a9c2f3b..a87c949 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/OidDtoValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/OidDtoValueSemantics.java
@@ -22,6 +22,8 @@ import javax.inject.Named;
 
 import org.springframework.stereotype.Component;
 
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.value.semantics.EncoderDecoder;
 import org.apache.isis.applib.value.semantics.OrderRelation;
@@ -33,6 +35,7 @@ import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.schema.common.v2.OidDto;
 import org.apache.isis.schema.common.v2.ValueType;
 
+import lombok.RequiredArgsConstructor;
 import lombok.val;
 
 @Component
@@ -80,6 +83,31 @@ implements
         return compare(a, b, epsilon) == 0;
     }
 
+    // -- CONSTRUCTOR EXTRACTOR
+
+    @Action
+    @RequiredArgsConstructor
+    public static class ConstructorExtractor {
+
+        final OidDto value;
+
+        @MemberSupport
+        public OidDto act(final String data) {
+            return Bookmark.parseElseFail(data).toOidDto();
+        }
+
+        @MemberSupport
+        public String defaultData() {
+            return Bookmark.forOidDto(value).stringify();
+        }
+
+    }
+
+    @Override
+    public ConstructorExtractor getConstructorExtractor(final OidDto object) {
+        return new ConstructorExtractor(object);
+    }
+
     // -- ENCODER DECODER
 
     @Override
@@ -89,8 +117,7 @@ implements
 
     @Override
     public OidDto fromEncodedString(final String data) {
-        return Bookmark.parseElseFail(data)
-                .toOidDto();
+        return Bookmark.parseElseFail(data).toOidDto();
     }
 
     // -- RENDERER
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java
index c3bc7e8..9969eba 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java
@@ -45,6 +45,7 @@ import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.core.config.IsisConfiguration;
+import org.apache.isis.core.metamodel.commons.CanonicalParameterUtil;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.execution.InteractionInternal;
 import org.apache.isis.core.metamodel.execution.MemberExecutorService;
@@ -68,6 +69,7 @@ import org.apache.isis.schema.ixn.v2.ActionInvocationDto;
 import lombok.Getter;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
@@ -109,6 +111,11 @@ implements MemberExecutorService {
         _Assert.assertEquals(owningAction.getParameterCount(), argumentAdapters.size(),
                 "action's parameter count and provided argument count must match");
 
+        if(interactionInitiatedBy.isPassThrough()) {
+            val resultPojo = invokeMethodPassThrough(method, head, argumentAdapters);
+            return facetHolder.getObjectManager().adapt(resultPojo);
+        }
+
         val interaction = getInteractionElseFail();
         val command = interaction.getCommand();
 
@@ -133,12 +140,10 @@ implements MemberExecutorService {
                 new ActionInvocation(
                         interaction, actionId, targetPojo, argumentPojos, targetMemberName,
                         targetClass);
-        final InteractionInternal.MemberExecutor<ActionInvocation> memberExecution =
-                actionExecutorFactory.createExecutor(
-                        owningAction, head, argumentAdapters);
+        val memberExecutor = actionExecutorFactory.createExecutor(owningAction, head, argumentAdapters);
 
         // sets up startedAt and completedAt on the execution, also manages the execution call graph
-        interaction.execute(memberExecution, actionInvocation, clockService, metricsService.get(), command);
+        interaction.execute(memberExecutor, actionInvocation, clockService, metricsService.get(), command);
 
         // handle any exceptions
         final Execution<ActionInvocationDto, ?> priorExecution =
@@ -236,6 +241,17 @@ implements MemberExecutorService {
 
     // -- HELPER
 
+    @SneakyThrows
+    private Object invokeMethodPassThrough(
+            final Method method,
+            final InteractionHead head,
+            final Can<ManagedObject> arguments) {
+
+        final Object[] executionParameters = UnwrapUtil.multipleAsArray(arguments);
+        final Object targetPojo = UnwrapUtil.single(head.getTarget());
+        return CanonicalParameterUtil.invoke(method, targetPojo, executionParameters);
+    }
+
     private void setCommandResultIfEntity(
             final Command command,
             final ManagedObject resultAdapter) {
diff --git a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
index 791e00e..eccf0cd 100644
--- a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
+++ b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
@@ -35,6 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.iactnlayer.InteractionContext;
 import org.apache.isis.applib.services.iactnlayer.InteractionService;
@@ -43,6 +44,9 @@ import org.apache.isis.applib.util.schema.CommonDtoUtils;
 import org.apache.isis.applib.value.Password;
 import org.apache.isis.core.config.presets.IsisPresets;
 import org.apache.isis.core.config.valuetypes.ValueSemanticsRegistry;
+import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
+import org.apache.isis.core.metamodel.interactions.managed.ActionInteraction;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.schema.cmd.v2.PropertyDto;
 import org.apache.isis.testdomain.conf.Configuration_headless;
@@ -84,6 +88,33 @@ class ValueSemanticsTest {
                 managedProp->example.getUpdateValue(),
                 (context, codec)->{
 
+                    val constructorExtractor = codec.getConstructorExtractor(example.getValue());
+                    if(constructorExtractor!=null) {
+
+                        val spec = specLoader.specForTypeElseFail(constructorExtractor.getClass());
+                        val interaction = ActionInteraction
+                                .start(ManagedObject.of(spec,  constructorExtractor), "act", Where.ANYWHERE);
+
+                        val pendingParams = interaction
+                                .startParameterNegotiation()
+                                .get();
+
+                        val managedAction = interaction.getManagedActionElseFail();
+                        val typedTuple = pendingParams.getParamValues();
+
+                        val recoveredValue = managedAction
+                                .invoke(typedTuple, InteractionInitiatedBy.PASS_THROUGH)
+                                .leftIfAny()
+                                .getPojo();
+
+                        tester.assertValueEquals(
+                                example.getValue(),
+                                recoveredValue,
+                                "serialization roundtrip failed");
+
+                        return;
+                    }
+
                     // CoderDecoder round-trip test
                     val serialized = codec.toEncodedString(example.getValue());