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 2019/08/08 14:55:12 UTC
[isis] branch v2 updated: ISIS-2158 fixes some behavior
inconsitences regarding domain object execution mode
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/v2 by this push:
new 4d48551 ISIS-2158 fixes some behavior inconsitences regarding domain object execution mode
4d48551 is described below
commit 4d48551e822f48883b71d74ff9127e4470248330
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Aug 8 16:54:51 2019 +0200
ISIS-2158 fixes some behavior inconsitences regarding domain object
execution mode
---
.../applib/services/wrapper/WrapperFactory.java | 29 +-
.../isis/commons/internal/collections/_Arrays.java | 21 +
.../commons/internal/collections/_ArraysTest.java | 74 +++
.../isis/metamodel/facets/ImperativeFacet.java | 36 +-
...FactoryDefaultTest_wrappedObject_transient.java | 39 +-
.../handlers/DomainObjectInvocationHandler.java | 574 ++++++++++-----------
.../unittestsupport/jmocking/PostponedAction.java | 46 ++
7 files changed, 459 insertions(+), 360 deletions(-)
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/WrapperFactory.java b/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/WrapperFactory.java
index b0d6971..4fda1ce 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/WrapperFactory.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/WrapperFactory.java
@@ -25,6 +25,9 @@ import org.apache.isis.applib.services.factory.FactoryService;
import org.apache.isis.applib.services.wrapper.events.InteractionEvent;
import org.apache.isis.applib.services.wrapper.listeners.InteractionListener;
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+
/**
* Provides the ability to "wrap" of a domain object such that it can
* be interacted with while enforcing the hide/disable/validate rules implies by
@@ -71,21 +74,27 @@ public interface WrapperFactory {
*
* @see WrapperFactory#wrap(Object, ExecutionMode)
*/
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public static enum ExecutionMode {
/**
- * Validate all business rules and then execute.
+ * Validate all business rules and then execute. May throw exceptions in order to fail fast.
*/
- EXECUTE(true,true,true),
+ EXECUTE(true, true, true),
+
/**
- * Validate all business rules and then execute, but don't throw exception if fails.
+ * Validate all business rules and then execute, but don't throw an exception if validation
+ * or execution fails.
*/
- TRY(true,true,false),
+ TRY(true, true, false),
+
/**
- * Skip all business rules and then execute.
+ * Skip all business rules and then execute, does throw an exception if execution fails.
*/
- SKIP_RULES(false, true, false),
+ SKIP_RULES(false, true, true),
+
/**
- * Validate all business rules but do not execute.
+ * Validate all business rules but do not execute, throw an exception if validation
+ * fails.
*/
NO_EXECUTE(true, false, true);
@@ -93,12 +102,6 @@ public interface WrapperFactory {
private final boolean execute;
private final boolean failFast;
- private ExecutionMode(final boolean enforceRules, final boolean execute, final boolean failFast) {
- this.enforceRules = enforceRules;
- this.execute = execute;
- this.failFast = failFast;
- }
-
public boolean shouldEnforceRules() {
return enforceRules;
}
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Arrays.java b/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Arrays.java
index 8cd1eda..1c3adc7 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Arrays.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Arrays.java
@@ -20,6 +20,7 @@
package org.apache.isis.commons.internal.collections;
import java.lang.reflect.Array;
+import java.util.Arrays;
import java.util.Collection;
import java.util.function.BiPredicate;
import java.util.stream.Collector;
@@ -32,6 +33,8 @@ import org.apache.isis.commons.internal.base._NullSafe;
import static org.apache.isis.commons.internal.base._With.requires;
+import lombok.val;
+
/**
* <h1>- internal use only -</h1>
* <p>
@@ -218,6 +221,24 @@ public final class _Arrays {
return _NullSafe.stream(iterable)
.collect(toArray(componentType));
}
+
+ // -- MODIFICATION
+
+ public static <T> T[] removeByIndex(T[] array, int index) {
+ if(array==null || array.length<=1) {
+ throw new IllegalArgumentException("Array must be of lenght 1 or larger.");
+ }
+ if(index>=array.length) {
+ val msg = String.format("Array index %d is out of bounds [0, %d]", index, array.length-1);
+ throw new IllegalArgumentException(msg);
+ }
+ final T[] result = Arrays.copyOf(array, array.length - 1);
+ // copy the elements from index + 1 till end
+ // from original array to the new array
+ val remaining = result.length - index;
+ System.arraycopy(array, index+1, result, index, remaining);
+ return result;
+ }
// -- COMPONENT TYPE INFERENCE
diff --git a/core/commons/src/test/java/org/apache/isis/commons/internal/collections/_ArraysTest.java b/core/commons/src/test/java/org/apache/isis/commons/internal/collections/_ArraysTest.java
new file mode 100644
index 0000000..f4d83a9
--- /dev/null
+++ b/core/commons/src/test/java/org/apache/isis/commons/internal/collections/_ArraysTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.commons.internal.collections;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import lombok.val;
+
+class _ArraysTest {
+
+ @Test
+ void removeByIndex_head() {
+ val input = new Integer[] {0, 1, 2, 3};
+ val output = _Arrays.removeByIndex(input, 0);
+ assertArrayEquals(new Integer[] {1, 2, 3}, output);
+ }
+
+ @Test
+ void removeByIndex_inBetween() {
+ val input = new Integer[] {0, 1, 2, 3};
+ val output = _Arrays.removeByIndex(input, 1);
+ assertArrayEquals(new Integer[] {0, 2, 3}, output);
+ }
+
+ @Test
+ void removeByIndex_tail() {
+ val input = new Integer[] {0, 1, 2, 3};
+ val output = _Arrays.removeByIndex(input, 3);
+ assertArrayEquals(new Integer[] {0, 1, 2}, output);
+ }
+
+ @Test
+ void removeByIndex_outOfBounds() {
+ val input = new Integer[] {0, 1, 2, 3};
+ assertThrows(IllegalArgumentException.class,
+ ()->_Arrays.removeByIndex(input, 4));
+ }
+
+ @Test
+ void removeByIndex_empty() {
+ val input = new Integer[] {};
+ assertThrows(IllegalArgumentException.class,
+ ()->_Arrays.removeByIndex(input, 0));
+ }
+
+ @Test
+ void removeByIndex_null() {
+ val input = (Integer[])null;
+ assertThrows(IllegalArgumentException.class,
+ ()->_Arrays.removeByIndex(input, 0));
+ }
+
+
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/ImperativeFacet.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/ImperativeFacet.java
index dbda762..35bab07 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/ImperativeFacet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/ImperativeFacet.java
@@ -23,16 +23,18 @@ import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
-import java.util.stream.Stream;
+import java.util.stream.Collectors;
import org.apache.isis.applib.services.wrapper.WrapperFactory;
import org.apache.isis.commons.internal.base._Casts;
-import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.commons.internal.base._NullSafe;
import org.apache.isis.metamodel.facetapi.DecoratingFacet;
import org.apache.isis.metamodel.facetapi.Facet;
import org.apache.isis.metamodel.spec.ObjectSpecification;
import org.apache.isis.metamodel.spec.feature.ObjectMember;
+import lombok.val;
+
/**
* A {@link Facet} implementation that ultimately wraps a {@link Method} or
* possibly several equivalent methods, for a Java implementation of a
@@ -133,20 +135,22 @@ public interface ImperativeFacet extends Facet {
}
public static Intent getIntent(final ObjectMember member, final Method method) {
- final List<ImperativeFacet> imperativeFacets = _Lists.newArrayList();
- final Stream<Facet> allFacets = member.streamFacets();
- allFacets.forEach(facet->{
- final ImperativeFacet imperativeFacet = ImperativeFacet.Util.getImperativeFacet(facet);
- if (imperativeFacet == null) {
- return;
- }
- final List<Method> methods = imperativeFacet.getMethods();
- if (!methods.contains(method)) {
- return;
- }
- imperativeFacets.add(imperativeFacet);
- });
-
+// val imperativeFacets = member.streamFacets()
+// .map(ImperativeFacet.Util::getImperativeFacet)
+// .filter(_NullSafe::isPresent)
+// .filter(imperativeFacet->imperativeFacet.getMethods().contains(method))
+// .collect(Collectors.toList());
+
+ val imperativeFacets1 = member.streamFacets()
+ .map(ImperativeFacet.Util::getImperativeFacet)
+ .filter(_NullSafe::isPresent)
+ .collect(Collectors.toList());
+
+ val imperativeFacets = imperativeFacets1.stream()
+ .filter(imperativeFacet->imperativeFacet.getMethods().contains(method))
+ .collect(Collectors.toList());
+
+
switch(imperativeFacets.size()) {
case 0:
break;
diff --git a/core/plugins/jdo-datanucleus-5/src/test/java/org/apache/isis/wrapper/WrapperFactoryDefaultTest_wrappedObject_transient.java b/core/plugins/jdo-datanucleus-5/src/test/java/org/apache/isis/wrapper/WrapperFactoryDefaultTest_wrappedObject_transient.java
index 708f075..a4fe9f5 100644
--- a/core/plugins/jdo-datanucleus-5/src/test/java/org/apache/isis/wrapper/WrapperFactoryDefaultTest_wrappedObject_transient.java
+++ b/core/plugins/jdo-datanucleus-5/src/test/java/org/apache/isis/wrapper/WrapperFactoryDefaultTest_wrappedObject_transient.java
@@ -22,12 +22,10 @@ package org.apache.isis.wrapper;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
import org.jmock.Expectations;
import org.jmock.auto.Mock;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
@@ -63,9 +61,12 @@ import org.apache.isis.security.authentication.standard.SimpleSession;
import org.apache.isis.unittestsupport.jmocking.JUnitRuleMockery2;
import org.apache.isis.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+import static org.apache.isis.unittestsupport.jmocking.PostponedAction.returnValuePostponed;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
+import lombok.val;
+
/**
* Contract test.
*/
@@ -110,7 +111,6 @@ public class WrapperFactoryDefaultTest_wrappedObject_transient {
private final SimpleSession session = new SimpleSession("tester", Collections.<String>emptyList());
- private List<Facet> facets;
private Method getPasswordMethod;
private Method setPasswordMethod;
@@ -143,6 +143,9 @@ public class WrapperFactoryDefaultTest_wrappedObject_transient {
allowing(mockPersistenceSessionServiceInternal).adapterFor(employeeDO);
will(returnValue(mockEmployeeAdapter));
+
+ allowing(mockPersistenceSessionServiceInternal).adapterFor(passwordValue);
+ will(returnValue(mockPasswordAdapter));
allowing(mockAdapterManager).adapterFor(employeeDO);
will(returnValue(mockEmployeeAdapter));
@@ -162,6 +165,9 @@ public class WrapperFactoryDefaultTest_wrappedObject_transient {
allowing(mockPasswordMember).getIdentifier();
will(returnValue(mockPasswordIdentifier));
+ allowing(mockPasswordIdentifier).toClassAndNameIdentityString();
+ will(returnValue("mocked-class#member"));
+
allowing(mockSpecificationLoader).loadSpecification(Employee.class);
will(returnValue(mockEmployeeSpec));
@@ -201,7 +207,7 @@ public class WrapperFactoryDefaultTest_wrappedObject_transient {
// given
final DisabledFacet disabledFacet = new DisabledFacetAbstractAlwaysEverywhere(mockPasswordMember){};
- facets = Arrays.asList(disabledFacet, new PropertySetterFacetViaSetterMethod(setPasswordMethod, mockPasswordMember));
+ val facets = Arrays.asList(disabledFacet, new PropertySetterFacetViaSetterMethod(setPasswordMethod, mockPasswordMember));
final Consent visibilityConsent = new Allow(new InteractionResult(new PropertyVisibilityEvent(employeeDO, null)));
@@ -212,13 +218,12 @@ public class WrapperFactoryDefaultTest_wrappedObject_transient {
context.checking(new Expectations() {
{
allowing(mockPasswordMember).streamFacets();
- will(returnValue(facets.stream()));
+ will(returnValuePostponed(facets::stream));
allowing(mockPasswordMember).isVisible(mockEmployeeAdapter, InteractionInitiatedBy.USER, Where.ANYWHERE);
will(returnValue(visibilityConsent));
- allowing(mockPasswordMember).isUsable(mockEmployeeAdapter, InteractionInitiatedBy.USER, Where.ANYWHERE
- );
+ allowing(mockPasswordMember).isUsable(mockEmployeeAdapter, InteractionInitiatedBy.USER, Where.ANYWHERE);
will(returnValue(usabilityConsent));
}
});
@@ -229,7 +234,6 @@ public class WrapperFactoryDefaultTest_wrappedObject_transient {
// then should throw exception
}
- @Ignore("TODO - reinstate or replace with integration tests")
@Test
public void canModifyProperty() {
// given
@@ -243,8 +247,7 @@ public class WrapperFactoryDefaultTest_wrappedObject_transient {
allowing(mockPasswordMember).isVisible(mockEmployeeAdapter, InteractionInitiatedBy.USER, Where.ANYWHERE);
will(returnValue(visibilityConsent));
- allowing(mockPasswordMember).isUsable(mockEmployeeAdapter, InteractionInitiatedBy.USER, Where.ANYWHERE
- );
+ allowing(mockPasswordMember).isUsable(mockEmployeeAdapter, InteractionInitiatedBy.USER, Where.ANYWHERE);
will(returnValue(usabilityConsent));
allowing(mockPasswordMember).isAssociationValid(mockEmployeeAdapter, mockPasswordAdapter,
@@ -253,13 +256,16 @@ public class WrapperFactoryDefaultTest_wrappedObject_transient {
}
});
- facets = Arrays.asList((Facet)new PropertySetterFacetViaSetterMethod(setPasswordMethod, mockPasswordMember));
+ val facets1 = Arrays.asList((Facet)new PropertySetterFacetViaSetterMethod(
+ setPasswordMethod, mockPasswordMember));
+
context.checking(new Expectations() {
{
allowing(mockPasswordMember).streamFacets();
- will(returnValue(facets.stream()));
+ will(returnValuePostponed(facets1::stream));
- oneOf(mockPasswordMember).set(mockEmployeeAdapter, mockPasswordAdapter, InteractionInitiatedBy.USER);
+ oneOf(mockPasswordMember)
+ .set(mockEmployeeAdapter, mockPasswordAdapter, InteractionInitiatedBy.USER);
}
});
@@ -268,12 +274,13 @@ public class WrapperFactoryDefaultTest_wrappedObject_transient {
// and given
- facets = Arrays.asList((Facet)new PropertyAccessorFacetViaAccessor(mockOnType, getPasswordMethod, mockPasswordMember
- ));
+ val facets2 = Arrays.asList((Facet)new PropertyAccessorFacetViaAccessor(
+ mockOnType, getPasswordMethod, mockPasswordMember));
+
context.checking(new Expectations() {
{
allowing(mockPasswordMember).streamFacets();
- will(returnValue(facets.stream()));
+ will(returnValuePostponed(facets2::stream));
oneOf(mockPasswordMember).get(mockEmployeeAdapter, InteractionInitiatedBy.USER);
will(returnValue(mockPasswordAdapter));
diff --git a/core/runtime-extensions/src/main/java/org/apache/isis/wrapper/handlers/DomainObjectInvocationHandler.java b/core/runtime-extensions/src/main/java/org/apache/isis/wrapper/handlers/DomainObjectInvocationHandler.java
index d06577b..5785240 100644
--- a/core/runtime-extensions/src/main/java/org/apache/isis/wrapper/handlers/DomainObjectInvocationHandler.java
+++ b/core/runtime-extensions/src/main/java/org/apache/isis/wrapper/handlers/DomainObjectInvocationHandler.java
@@ -22,10 +22,10 @@ package org.apache.isis.wrapper.handlers;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Supplier;
import java.util.stream.Stream;
import org.apache.isis.applib.annotation.Where;
@@ -37,25 +37,22 @@ import org.apache.isis.applib.services.wrapper.WrapperFactory.ExecutionMode;
import org.apache.isis.applib.services.wrapper.WrappingObject;
import org.apache.isis.applib.services.wrapper.events.CollectionAccessEvent;
import org.apache.isis.applib.services.wrapper.events.InteractionEvent;
-import org.apache.isis.applib.services.wrapper.events.ObjectTitleEvent;
import org.apache.isis.applib.services.wrapper.events.PropertyAccessEvent;
import org.apache.isis.applib.services.wrapper.events.UsabilityEvent;
import org.apache.isis.applib.services.wrapper.events.ValidityEvent;
import org.apache.isis.applib.services.wrapper.events.VisibilityEvent;
import org.apache.isis.commons.internal.base._NullSafe;
-import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.commons.internal.collections._Arrays;
import org.apache.isis.commons.internal.collections._Sets;
import org.apache.isis.metamodel.IsisJdoMetamodelPlugin;
import org.apache.isis.metamodel.MetaModelContext;
import org.apache.isis.metamodel.adapter.ObjectAdapter;
import org.apache.isis.metamodel.adapter.ObjectAdapterProvider;
-import org.apache.isis.metamodel.consent.Consent;
import org.apache.isis.metamodel.consent.InteractionInitiatedBy;
import org.apache.isis.metamodel.consent.InteractionResult;
import org.apache.isis.metamodel.facets.ImperativeFacet;
import org.apache.isis.metamodel.facets.ImperativeFacet.Intent;
import org.apache.isis.metamodel.facets.object.mixin.MixinFacet;
-import org.apache.isis.metamodel.interactions.ObjectTitleContext;
import org.apache.isis.metamodel.spec.ObjectSpecification;
import org.apache.isis.metamodel.spec.feature.Contributed;
import org.apache.isis.metamodel.spec.feature.ObjectAction;
@@ -236,12 +233,12 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'; use only the 'invoke' method", memberName));
}
- final ObjectAction objectAction = (ObjectAction) objectMember;
+ val objectAction = (ObjectAction) objectMember;
ObjectAction actualObjectAction;
ObjectAdapter actualTargetAdapter;
- final MixinFacet mixinFacet = targetAdapter.getSpecification().getFacet(MixinFacet.class);
+ val mixinFacet = targetAdapter.getSpecification().getFacet(MixinFacet.class);
if(mixinFacet != null) {
// rather than invoke on a (transient) mixin, instead try to
@@ -268,11 +265,12 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
private static ObjectAction determineMixinAction(
final ObjectAdapter domainObjectAdapter,
final ObjectAction objectAction) {
+
if(domainObjectAdapter == null) {
return null;
}
- final ObjectSpecification specification = domainObjectAdapter.getSpecification();
- final Stream<ObjectAction> objectActions = specification.streamObjectActions(Contributed.INCLUDED);
+ val specification = domainObjectAdapter.getSpecification();
+ val objectActions = specification.streamObjectActions(Contributed.INCLUDED);
return objectActions
.filter(action->action instanceof ObjectActionMixedIn)
@@ -285,7 +283,9 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
}
public InteractionInitiatedBy getInteractionInitiatedBy() {
- return getExecutionMode().shouldEnforceRules()? InteractionInitiatedBy.USER: InteractionInitiatedBy.FRAMEWORK;
+ return getExecutionMode().shouldEnforceRules()
+ ? InteractionInitiatedBy.USER
+ : InteractionInitiatedBy.FRAMEWORK;
}
// see if this is a contributed property/collection/action
@@ -364,9 +364,10 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
resolveIfRequired(targetAdapter);
- final ObjectSpecification targetNoSpec = targetAdapter.getSpecification();
- final ObjectTitleContext titleContext = targetNoSpec.createTitleInteractionContext(getAuthenticationSession(), InteractionInitiatedBy.FRAMEWORK, targetAdapter);
- final ObjectTitleEvent titleEvent = titleContext.createInteractionEvent();
+ val targetNoSpec = targetAdapter.getSpecification();
+ val titleContext = targetNoSpec.createTitleInteractionContext(
+ getAuthenticationSession(), InteractionInitiatedBy.FRAMEWORK, targetAdapter);
+ val titleEvent = titleContext.createInteractionEvent();
notifyListeners(titleEvent);
return titleEvent.getTitle();
}
@@ -378,38 +379,20 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
private Object handleSaveMethod(
final ObjectAdapter targetAdapter, final ObjectSpecification targetNoSpec) {
- if(getExecutionMode().shouldEnforceRules()) {
- if(getExecutionMode().shouldFailFast()) {
- final InteractionResult interactionResult =
- targetNoSpec.isValidResult(targetAdapter, getInteractionInitiatedBy());
- notifyListenersAndVetoIfRequired(interactionResult);
- } else {
- try {
- final InteractionResult interactionResult =
- targetNoSpec.isValidResult(targetAdapter, getInteractionInitiatedBy());
- notifyListenersAndVetoIfRequired(interactionResult);
- } catch(Exception ex) {
- return null;
- }
- }
-
- }
-
- if (getExecutionMode().shouldExecute()) {
+ runValidationTask(()->{
+ val interactionResult =
+ targetNoSpec.isValidResult(targetAdapter, getInteractionInitiatedBy());
+ notifyListenersAndVetoIfRequired(interactionResult);
+ });
+
+ return runExecutionTask(()->{
if (targetAdapter.isTransient()) {
val ps = IsisContext.getPersistenceSession().get();
- if(getExecutionMode().shouldFailFast()) {
- ps.makePersistentInTransaction(targetAdapter);
- } else {
- try {
- ps.makePersistentInTransaction(targetAdapter);
- } catch(Exception ignore) {
- // ignore
- }
- }
+ ps.makePersistentInTransaction(targetAdapter);
}
- }
- return null;
+ return null;
+ });
+
}
// /////////////////////////////////////////////////////////////////
@@ -421,34 +404,28 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
final Object[] args,
final OneToOneAssociation property) {
- if (args.length != 0) {
- throw new IllegalArgumentException("Invoking a 'get' should have no arguments");
- }
+ zeroArgsElseThrow(args, "get");
- if(getExecutionMode().shouldEnforceRules()) {
- if(getExecutionMode().shouldFailFast()) {
- checkVisibility(targetAdapter, property);
- } else {
- try {
- checkVisibility(targetAdapter, property);
- } catch(Exception ex) {
- return null;
-
- }
- }
-
- }
+ runValidationTask(()->{
+ checkVisibility(targetAdapter, property);
+ });
resolveIfRequired(targetAdapter);
+
+ return runExecutionTask(()->{
+
+ val interactionInitiatedBy = getInteractionInitiatedBy();
+ val currentReferencedAdapter = property.get(targetAdapter, interactionInitiatedBy);
- final InteractionInitiatedBy interactionInitiatedBy = getInteractionInitiatedBy();
- final ObjectAdapter currentReferencedAdapter = property.get(targetAdapter, interactionInitiatedBy);
-
- final Object currentReferencedObj = ObjectAdapter.Util.unwrapPojo(currentReferencedAdapter);
+ val currentReferencedObj = ObjectAdapter.Util.unwrapPojo(currentReferencedAdapter);
- final PropertyAccessEvent ev = new PropertyAccessEvent(getDelegate(), property.getIdentifier(), currentReferencedObj);
- notifyListeners(ev);
- return currentReferencedObj;
+ val propertyAccessEvent = new PropertyAccessEvent(
+ getDelegate(), property.getIdentifier(), currentReferencedObj);
+ notifyListeners(propertyAccessEvent);
+ return currentReferencedObj;
+
+ });
+
}
@@ -456,54 +433,36 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
// property - modify
// /////////////////////////////////////////////////////////////////
- private Object handleSetterMethodOnProperty(
- final ObjectAdapter targetAdapter, final Object[] args,
- final OneToOneAssociation property) {
- if (args.length != 1) {
- throw new IllegalArgumentException("Invoking a setter should only have a single argument");
- }
-
- final Object argumentObj = underlying(args[0]);
- if(getExecutionMode().shouldEnforceRules()) {
- if(getExecutionMode().shouldFailFast()) {
- checkVisibility(targetAdapter, property);
- checkUsability(targetAdapter, property);
- } else {
- try {
- checkVisibility(targetAdapter, property);
- checkUsability(targetAdapter, property);
- } catch(Exception ex) {
- return null;
- }
- }
-
- }
-
- final ObjectAdapter argumentAdapter = argumentObj != null ? adapterFor(argumentObj) : null;
+ private Object handleSetterMethodOnProperty(
+ final ObjectAdapter targetAdapter,
+ final Object[] args,
+ final OneToOneAssociation property) {
+
+ val singleArg = singleArgUnderlyingElseNull(args, "setter");
+
+ runValidationTask(()->{
+ checkVisibility(targetAdapter, property);
+ checkUsability(targetAdapter, property);
+ });
+
+ val argumentAdapter = adapterFor(singleArg);
+
resolveIfRequired(targetAdapter);
-
- if(getExecutionMode().shouldEnforceRules()) {
- final InteractionResult interactionResult = property.isAssociationValid(targetAdapter, argumentAdapter, getInteractionInitiatedBy()).getInteractionResult();
+ runValidationTask(()->{
+ val interactionResult = property.isAssociationValid(
+ targetAdapter, argumentAdapter, getInteractionInitiatedBy())
+ .getInteractionResult();
notifyListenersAndVetoIfRequired(interactionResult);
- }
-
- if (getExecutionMode().shouldExecute()) {
- if(getExecutionMode().shouldFailFast()) {
- property.set(targetAdapter, argumentAdapter, getInteractionInitiatedBy());
- } else {
- try {
- property.set(targetAdapter, argumentAdapter, getInteractionInitiatedBy());
- } catch(Exception ignore) {
- // ignore
- }
- }
-
- }
-
- return null;
+ });
+
+ return runExecutionTask(()->{
+ property.set(targetAdapter, argumentAdapter, getInteractionInitiatedBy());
+ return null;
+ });
+
}
@@ -518,44 +477,40 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
final Method method,
final String memberName) {
- if (args.length != 0) {
- throw new IllegalArgumentException("Invoking a 'get' should have no arguments");
- }
+ zeroArgsElseThrow(args, "get");
- if(getExecutionMode().shouldEnforceRules()) {
- if(getExecutionMode().shouldFailFast()) {
- checkVisibility(targetAdapter, collection);
- } else {
- try {
- checkVisibility(targetAdapter, collection);
- } catch(Exception ex) {
- return null;
- }
- }
-
- }
+ runValidationTask(()->{
+ checkVisibility(targetAdapter, collection);
+ });
resolveIfRequired(targetAdapter);
-
- final InteractionInitiatedBy interactionInitiatedBy = getInteractionInitiatedBy();
- final ObjectAdapter currentReferencedAdapter = collection.get(targetAdapter, interactionInitiatedBy);
-
- final Object currentReferencedObj = ObjectAdapter.Util.unwrapPojo(currentReferencedAdapter);
-
- final CollectionAccessEvent ev = new CollectionAccessEvent(getDelegate(), collection.getIdentifier());
-
- if (currentReferencedObj instanceof Collection) {
- final Collection<?> collectionViewObject = lookupWrappingObject(method, memberName,
- (Collection<?>) currentReferencedObj, collection);
- notifyListeners(ev);
- return collectionViewObject;
- } else if (currentReferencedObj instanceof Map) {
- final Map<?, ?> mapViewObject = lookupWrappingObject(memberName, (Map<?, ?>) currentReferencedObj,
- collection);
- notifyListeners(ev);
- return mapViewObject;
- }
- throw new IllegalArgumentException(String.format("Collection type '%s' not supported by framework", currentReferencedObj.getClass().getName()));
+
+ return runExecutionTask(()->{
+
+ val interactionInitiatedBy = getInteractionInitiatedBy();
+ val currentReferencedAdapter = collection.get(targetAdapter, interactionInitiatedBy);
+
+ val currentReferencedObj = ObjectAdapter.Util.unwrapPojo(currentReferencedAdapter);
+
+ val collectionAccessEvent = new CollectionAccessEvent(getDelegate(), collection.getIdentifier());
+
+ if (currentReferencedObj instanceof Collection) {
+ val collectionViewObject = lookupWrappingObject(method, memberName,
+ (Collection<?>) currentReferencedObj, collection);
+ notifyListeners(collectionAccessEvent);
+ return collectionViewObject;
+ } else if (currentReferencedObj instanceof Map) {
+ val mapViewObject = lookupWrappingObject(memberName, (Map<?, ?>) currentReferencedObj,
+ collection);
+ notifyListeners(collectionAccessEvent);
+ return mapViewObject;
+ }
+
+ val msg = String.format("Collection type '%s' not supported by framework", currentReferencedObj.getClass().getName());
+ throw new IllegalArgumentException(msg);
+
+ });
+
}
private Collection<?> lookupWrappingObject(
@@ -586,109 +541,64 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
final Object[] args,
final OneToManyAssociation otma) {
- if (args.length != 1) {
- throw new IllegalArgumentException("Invoking a addTo should only have a single argument");
- }
-
- if(getExecutionMode().shouldEnforceRules()) {
- if(getExecutionMode().shouldFailFast()) {
- checkVisibility(targetAdapter, otma);
- checkUsability(targetAdapter, otma);
- } else {
- try {
- checkVisibility(targetAdapter, otma);
- checkUsability(targetAdapter, otma);
- } catch(Exception ex) {
- return null;
- }
- }
- }
+ val singleArg = singleArgUnderlyingElseThrow(args, "addTo");
+ runValidationTask(()->{
+ checkVisibility(targetAdapter, otma);
+ checkUsability(targetAdapter, otma);
+ });
+
resolveIfRequired(targetAdapter);
-
- final Object argumentObj = underlying(args[0]);
- if (argumentObj == null) {
- throw new IllegalArgumentException("Must provide a non-null object to add");
- }
- final ObjectAdapter argumentNO = adapterFor(argumentObj);
-
- if(getExecutionMode().shouldEnforceRules()) {
- final InteractionResult interactionResult = otma.isValidToAdd(targetAdapter, argumentNO,
+ val argumentAdapter = adapterFor(singleArg);
+
+ runValidationTask(()->{
+ val interactionResult = otma.isValidToAdd(targetAdapter, argumentAdapter,
getInteractionInitiatedBy()).getInteractionResult();
notifyListenersAndVetoIfRequired(interactionResult);
- }
-
- if (getExecutionMode().shouldExecute()) {
- if(getExecutionMode().shouldFailFast()) {
- otma.addElement(targetAdapter, argumentNO, getInteractionInitiatedBy());
- } else {
- try {
- otma.addElement(targetAdapter, argumentNO, getInteractionInitiatedBy());
- } catch(Exception ignore) {
- // ignore
- }
- }
- }
-
- return null;
+ });
+
+ return runExecutionTask(()->{
+ otma.addElement(targetAdapter, argumentAdapter, getInteractionInitiatedBy());
+ return null;
+ });
+
}
+
// /////////////////////////////////////////////////////////////////
// collection - remove from
// /////////////////////////////////////////////////////////////////
+
+
private Object handleCollectionRemoveFromMethod(
final ObjectAdapter targetAdapter,
final Object[] args,
final OneToManyAssociation collection) {
- if (args.length != 1) {
- throw new IllegalArgumentException("Invoking a removeFrom should only have a single argument");
- }
-
- if(getExecutionMode().shouldEnforceRules()) {
- if(getExecutionMode().shouldFailFast()) {
- checkVisibility(targetAdapter, collection);
- checkUsability(targetAdapter, collection);
- } else {
- try {
- checkVisibility(targetAdapter, collection);
- checkUsability(targetAdapter, collection);
- } catch(Exception ex) {
- return null;
- }
- }
-
- }
+
+ val singleArg = singleArgUnderlyingElseThrow(args, "removeFrom");
+ runValidationTask(()->{
+ checkVisibility(targetAdapter, collection);
+ checkUsability(targetAdapter, collection);
+ });
resolveIfRequired(targetAdapter);
+ val argumentAdapter = adapterFor(singleArg);
- final Object argumentObj = underlying(args[0]);
- if (argumentObj == null) {
- throw new IllegalArgumentException("Must provide a non-null object to remove");
- }
- final ObjectAdapter argumentAdapter = adapterFor(argumentObj);
-
- if(getExecutionMode().shouldEnforceRules()) {
- final InteractionResult interactionResult = collection.isValidToRemove(targetAdapter, argumentAdapter,
+ runValidationTask(()->{
+ val interactionResult = collection.isValidToRemove(targetAdapter, argumentAdapter,
getInteractionInitiatedBy()).getInteractionResult();
notifyListenersAndVetoIfRequired(interactionResult);
- }
-
- if (getExecutionMode().shouldExecute()) {
- if(getExecutionMode().shouldFailFast()) {
- collection.removeElement(targetAdapter, argumentAdapter, getInteractionInitiatedBy());
- } else {
- try {
- collection.removeElement(targetAdapter, argumentAdapter, getInteractionInitiatedBy());
- } catch(Exception ignore) {
- // ignore
- }
- }
- }
+ });
+
+ return runExecutionTask(()->{
+ collection.removeElement(targetAdapter, argumentAdapter, getInteractionInitiatedBy());
+ return null;
+ });
- return null;
+
}
// /////////////////////////////////////////////////////////////////
@@ -696,106 +606,71 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
// /////////////////////////////////////////////////////////////////
private Object handleActionMethod(
- final ObjectAdapter targetAdapter, final Object[] args,
+ final ObjectAdapter targetAdapter,
+ final Object[] args,
final ObjectAction objectAction,
final ContributeeMember contributeeMember) {
final ObjectAdapter contributeeAdapter;
final Object[] contributeeArgs;
if(contributeeMember != null) {
- final int contributeeParamPosition = contributeeMember.getContributeeParamPosition();
- final Object contributee = args[contributeeParamPosition];
+ val contributeeParamPosition = contributeeMember.getContributeeParamPosition();
+ val contributee = args[contributeeParamPosition];
+
contributeeAdapter = adapterFor(contributee);
-
- final List<Object> argCopy = _Lists.of(args);
- argCopy.remove(contributeeParamPosition);
- contributeeArgs = argCopy.toArray();
+ contributeeArgs = _Arrays.removeByIndex(args, contributeeParamPosition);
} else {
contributeeAdapter = null;
contributeeArgs = null;
}
+
+ val argAdapters = asObjectAdaptersUnderlying(args);
- if(getExecutionMode().shouldEnforceRules()) {
- if(getExecutionMode().shouldFailFast()) {
- if(contributeeMember != null) {
- checkVisibility(contributeeAdapter, contributeeMember);
- checkUsability(contributeeAdapter, contributeeMember);
- } else {
- checkVisibility(targetAdapter, objectAction);
- checkUsability(targetAdapter, objectAction);
- }
- } else {
- try {
- if(contributeeMember != null) {
- checkVisibility(contributeeAdapter, contributeeMember);
- checkUsability(contributeeAdapter, contributeeMember);
- } else {
- checkVisibility(targetAdapter, objectAction);
- checkUsability(targetAdapter, objectAction);
- }
- } catch(Exception ex) {
- return null;
- }
- }
-
- }
-
- final ObjectAdapter[] argAdapters = asObjectAdaptersUnderlying(args);
-
- if(getExecutionMode().shouldEnforceRules()) {
+ runValidationTask(()->{
if(contributeeMember != null) {
+ checkVisibility(contributeeAdapter, contributeeMember);
+ checkUsability(contributeeAdapter, contributeeMember);
+
if(contributeeMember instanceof ObjectActionContributee) {
- final ObjectActionContributee objectActionContributee = (ObjectActionContributee) contributeeMember;
- final ObjectAdapter[] contributeeArgAdapters = asObjectAdaptersUnderlying(contributeeArgs);
-
+ val objectActionContributee = (ObjectActionContributee) contributeeMember;
+ val contributeeArgAdapters = asObjectAdaptersUnderlying(contributeeArgs);
checkValidity(contributeeAdapter, objectActionContributee, contributeeArgAdapters);
}
// nothing to do for contributed properties or collections
+
} else {
+ checkVisibility(targetAdapter, objectAction);
+ checkUsability(targetAdapter, objectAction);
checkValidity(targetAdapter, objectAction, argAdapters);
}
- }
-
- if (getExecutionMode().shouldExecute()) {
- final InteractionInitiatedBy interactionInitiatedBy = getInteractionInitiatedBy();
-
- final ObjectAdapter mixedInAdapter = null; // if a mixin action, then it will automatically fill in.
-
-
- ObjectAdapter returnedAdapter;
-
- if(getExecutionMode().shouldFailFast()) {
- returnedAdapter = objectAction.execute(
- targetAdapter, mixedInAdapter, argAdapters,
- interactionInitiatedBy);
- } else {
- try {
- returnedAdapter = objectAction.execute(
- targetAdapter, mixedInAdapter, argAdapters,
- interactionInitiatedBy);
- } catch(Exception ignore) {
- // ignore
- returnedAdapter = null;
- }
-
- }
-
+ });
+
+ return runExecutionTask(()->{
+ val interactionInitiatedBy = getInteractionInitiatedBy();
+ val mixedInAdapter = (ObjectAdapter)null; // if a mixin action, then it will automatically fill in.
+ val returnedAdapter = objectAction.execute(
+ targetAdapter, mixedInAdapter, argAdapters,
+ interactionInitiatedBy);
return ObjectAdapter.Util.unwrapPojo(returnedAdapter);
- }
-
- return null;
+
+ });
+
}
- private void checkValidity(final ObjectAdapter targetAdapter, final ObjectAction objectAction, final ObjectAdapter[] argAdapters) {
- final InteractionResult interactionResult = objectAction.isProposedArgumentSetValid(targetAdapter, argAdapters,
+ private void checkValidity(
+ final ObjectAdapter targetAdapter,
+ final ObjectAction objectAction,
+ final ObjectAdapter[] argAdapters) {
+
+ val interactionResult = objectAction.isProposedArgumentSetValid(targetAdapter, argAdapters,
getInteractionInitiatedBy()).getInteractionResult();
notifyListenersAndVetoIfRequired(interactionResult);
}
private ObjectAdapter[] asObjectAdaptersUnderlying(final Object[] args) {
- final ObjectAdapter[] argAdapters = new ObjectAdapter[args.length];
+ val argAdapters = new ObjectAdapter[args.length];
int i = 0;
for (final Object arg : args) {
argAdapters[i++] = adapterFor(underlying(arg));
@@ -810,7 +685,7 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
private Object underlying(final Object arg) {
if (arg instanceof WrappingObject) {
- final WrappingObject argViewObject = (WrappingObject) arg;
+ val argViewObject = (WrappingObject) arg;
return argViewObject.__isis_wrapped();
} else {
return arg;
@@ -830,26 +705,28 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
private void checkVisibility(
final ObjectAdapter targetObjectAdapter,
final ObjectMember objectMember) {
- final Consent visibleConsent = objectMember.isVisible(targetObjectAdapter, getInteractionInitiatedBy(), where);
- final InteractionResult interactionResult = visibleConsent.getInteractionResult();
+
+ val visibleConsent = objectMember.isVisible(targetObjectAdapter, getInteractionInitiatedBy(), where);
+ val interactionResult = visibleConsent.getInteractionResult();
notifyListenersAndVetoIfRequired(interactionResult);
}
private void checkUsability(
final ObjectAdapter targetObjectAdapter,
final ObjectMember objectMember) {
- final InteractionResult interactionResult = objectMember.isUsable(targetObjectAdapter,
- getInteractionInitiatedBy(), where
- ).getInteractionResult();
+
+ val interactionResult = objectMember.isUsable(
+ targetObjectAdapter,
+ getInteractionInitiatedBy(),
+ where)
+ .getInteractionResult();
notifyListenersAndVetoIfRequired(interactionResult);
}
- // /////////////////////////////////////////////////////////////////
- // notify listeners
- // /////////////////////////////////////////////////////////////////
+ // -- NOTIFY LISTENERS
private void notifyListenersAndVetoIfRequired(final InteractionResult interactionResult) {
- final InteractionEvent interactionEvent = interactionResult.getInteractionEvent();
+ val interactionEvent = interactionResult.getInteractionEvent();
notifyListeners(interactionEvent);
if (interactionEvent.isVeto()) {
throw toException(interactionEvent);
@@ -880,13 +757,11 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
throw new IllegalArgumentException("Provided interactionEvent must be a VisibilityEvent, UsabilityEvent or a ValidityEvent");
}
- // /////////////////////////////////////////////////////////////////
- // switching
- // /////////////////////////////////////////////////////////////////
+ // -- SWITCHING
private ObjectMember locateAndCheckMember(final Method method) {
- final ObjectSpecificationDefault objectSpecificationDefault = getJavaSpecificationOfOwningClass(method);
- final ObjectMember member = objectSpecificationDefault.getMember(method);
+ val objectSpecificationDefault = getJavaSpecificationOfOwningClass(method);
+ val member = objectSpecificationDefault.getMember(method);
if (member == null) {
final String methodName = method.getName();
@@ -911,9 +786,7 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
return method.equals(__isis_executionMode);
}
- // /////////////////////////////////////////////////////////////////
- // Specification lookup
- // /////////////////////////////////////////////////////////////////
+ // -- SPECIFICATION LOOKUP
private ObjectSpecificationDefault getJavaSpecificationOfOwningClass(final Method method) {
return getJavaSpecification(method.getDeclaringClass());
@@ -931,9 +804,80 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
return getSpecificationLoader().loadSpecification(type);
}
- // /////////////////////////////////////////////////////////////////
- // Dependencies
- // /////////////////////////////////////////////////////////////////
+ // -- HELPER
+
+ private void runValidationTask(Runnable task) {
+ if(!getExecutionMode().shouldEnforceRules()) {
+ return;
+ }
+ if(getExecutionMode().shouldFailFast()) {
+ task.run();
+ } else {
+ try {
+ task.run();
+ } catch(Exception ex) {
+ // swallow
+ }
+ }
+ }
+
+// private void runExecutionTask(Runnable task) {
+// if(!getExecutionMode().shouldExecute()) {
+// return;
+// }
+// if(getExecutionMode().shouldFailFast()) {
+// task.run();
+// } else {
+// try {
+// task.run();
+// } catch(Exception ex) {
+// // swallow
+// }
+// }
+// }
+
+ private <X> X runExecutionTask(Supplier<X> task) {
+ if(!getExecutionMode().shouldExecute()) {
+ return null;
+ }
+ if(getExecutionMode().shouldFailFast()) {
+ return task.get();
+ } else {
+ try {
+ return task.get();
+ } catch(Exception ex) {
+ // swallow
+ return null;
+ }
+ }
+ }
+
+ private Object singleArgUnderlyingElseThrow(Object[] args, String name) {
+ if (args.length != 1) {
+ throw new IllegalArgumentException("Invoking '" + name + "' should only have a single argument");
+ }
+ val argumentObj = underlying(args[0]);
+ if (argumentObj == null) {
+ throw new IllegalArgumentException("Must provide a non-null object to '" + name +"'");
+ }
+ return argumentObj;
+ }
+
+ private Object singleArgUnderlyingElseNull(Object[] args, String name) {
+ if (args.length != 1) {
+ throw new IllegalArgumentException("Invoking '" + name + "' should only have a single argument");
+ }
+ val argumentObj = underlying(args[0]);
+ return argumentObj;
+ }
+
+ private void zeroArgsElseThrow(Object[] args, String name) {
+ if (args.length != 0) {
+ throw new IllegalArgumentException("Invoking '" + name + "' should have no arguments");
+ }
+ }
+
+ // -- DEPENDENCIES
protected SpecificationLoader getSpecificationLoader() {
return mmContext.getSpecificationLoader();
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/unittestsupport/jmocking/PostponedAction.java b/core/unittestsupport/src/main/java/org/apache/isis/unittestsupport/jmocking/PostponedAction.java
new file mode 100644
index 0000000..b2fa43c
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/unittestsupport/jmocking/PostponedAction.java
@@ -0,0 +1,46 @@
+/*
+ * 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.unittestsupport.jmocking;
+
+import java.util.function.Supplier;
+
+import org.hamcrest.Description;
+import org.jmock.api.Action;
+import org.jmock.api.Invocation;
+
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor(staticName = "returnValuePostponed")
+public class PostponedAction implements Action {
+
+ @NonNull private Supplier<Object> resultSupplier;
+
+ public Object invoke(Invocation invocation) throws Throwable {
+ return resultSupplier.get();
+ }
+
+ public void describeTo(Description description) {
+ description.appendText("returns ");
+ description.appendValue(resultSupplier.get());
+ }
+
+
+
+}