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 21:22:29 UTC
[isis] branch v2 updated: ISIS-2158 implements ASYNC execution for
the Wrapper
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 3d121aa ISIS-2158 implements ASYNC execution for the Wrapper
3d121aa is described below
commit 3d121aa0eff0ee142304aec8dec88fbf7c37d03e
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Aug 8 23:22:10 2019 +0200
ISIS-2158 implements ASYNC execution for the Wrapper
- for now we are simply utilizing the 'common' ForkJoinPool, but users
might need more control here!
- simple smoketest added to test async execution with the 'wrapper'
---
.../handlers/DomainObjectInvocationHandler.java | 47 ++++++++++++++++++++++
.../commandexecution/BackgroundExecutionTest.java | 31 ++++----------
...ckgroundExecutionTest.java => WrapperTest.java} | 41 +++++++------------
3 files changed, 69 insertions(+), 50 deletions(-)
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 42240a4..ac7cf6f 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
@@ -26,6 +26,8 @@ import java.util.EnumSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ForkJoinPool;
import java.util.function.Supplier;
import java.util.stream.Stream;
@@ -70,7 +72,9 @@ import org.apache.isis.runtime.system.context.IsisContext;
import org.apache.isis.security.authentication.AuthenticationSession;
import lombok.val;
+import lombok.extern.log4j.Log4j2;
+@Log4j2
public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandlerDefault<T> {
private final ProxyContextHandler proxy;
@@ -819,6 +823,10 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
return !getExecutionMode().contains(ExecutionMode.SWALLOW_EXCEPTIONS);
}
+ private boolean shouldExecuteAsync() {
+ return getExecutionMode().contains(ExecutionMode.ASYNC);
+ }
+
private void runValidationTask(Runnable task) {
if(!shouldEnforceRules()) {
return;
@@ -838,6 +846,9 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
if(!shouldExecute()) {
return null;
}
+ if(shouldExecuteAsync()) {
+ return runAsync(task);
+ }
if(shouldFailFast()) {
return task.get();
} else {
@@ -850,6 +861,40 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
}
}
+ /* will always return null*/
+ private <X> X runAsync(Supplier<X> task) {
+ val commonPool = ForkJoinPool.commonPool();
+
+ val transactionService = mmContext.getTransactionService();
+ val authenticationSession = mmContext.getAuthenticationSession();
+ //val transactionLatch = IsisTransactionAspectSupport.transactionLatch();
+
+ Callable<X> asyncTask = ()->{
+
+ try {
+ //transactionLatch.await(); // wait for transaction of the calling thread to complete
+
+ return IsisContext.getSessionFactory().doInSession(
+ ()->transactionService.executeWithinTransaction(task),
+ authenticationSession);
+
+ } catch (Exception e) {
+
+ log.error(
+ String.format("Async execution of action '%s' on type '%s' failed.",
+ __isis_wrappedMethod.getName(),
+ __isis_wrappedMethod.getDeclaringClass()),
+ e);
+ return null;
+ }
+ };
+
+ //unfortunately there is no easy way to make use of this future,
+ //would be nice if users had access to it via the wrapper API
+ val future = commonPool.submit(asyncTask);
+ return null;
+ }
+
private Object singleArgUnderlyingElseThrow(Object[] args, String name) {
if (args.length != 1) {
throw new IllegalArgumentException("Invoking '" + name + "' should only have a single argument");
@@ -888,4 +933,6 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
protected ObjectAdapterProvider getObjectAdapterProvider() {
return mmContext.getObjectAdapterProvider();
}
+
+
}
diff --git a/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java b/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java
index fb99a65..92813a2 100644
--- a/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java
+++ b/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java
@@ -25,11 +25,13 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
+import org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase;
import org.apache.isis.applib.services.background.BackgroundService;
import org.apache.isis.applib.services.factory.FactoryService;
import org.apache.isis.applib.services.repository.RepositoryService;
@@ -89,7 +91,7 @@ class BackgroundExecutionTest {
assertEquals(123d, product.getPrice(), 1E-6);
}
- @Test
+ @Test @Disabled("FIXME[2125] does not trigger domain events")
void testBackgroundService_waitingOnDomainEvent() throws InterruptedException, ExecutionException {
val inventoryManager = facoryService.viewModel(InventoryManager.class);
@@ -107,26 +109,6 @@ class BackgroundExecutionTest {
assertEquals(123d, product.getPrice(), 1E-6);
}
-
- @Test
- void testWrapper_waitingOnDomainEvent() throws InterruptedException, ExecutionException {
-
- val inventoryManager = facoryService.viewModel(InventoryManager.class);
- val product = repository.allInstances(Product.class).get(0);
-
- assertEquals(99d, product.getPrice(), 1E-6);
-
- actionDomainEventListener.prepareLatch();
-
- wrapperFactory.wrap(inventoryManager).updateProductPrice(product, 123);
-
- assertTrue(
- actionDomainEventListener.getCountDownLatch()
- .await(5, TimeUnit.SECONDS));
-
- assertEquals(123d, product.getPrice(), 1E-6);
- }
-
@Service
public static class ActionDomainEventListener {
@@ -134,10 +116,11 @@ class BackgroundExecutionTest {
@Getter private CountDownLatch countDownLatch;
@EventListener(InventoryManager.UpdateProductPriceEvent.class)
- public void hi(InventoryManager.UpdateProductPriceEvent event) {
+ public void onDomainEvent(InventoryManager.UpdateProductPriceEvent event) {
//FIXME[2125] not triggered yet
- System.err.println("!!!!!!!!!!!! event " + event.getEventPhase());
- countDownLatch.countDown();
+ if(event.getEventPhase()==Phase.EXECUTED) {
+ countDownLatch.countDown();
+ }
}
public void prepareLatch() {
diff --git a/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java b/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/WrapperTest.java
similarity index 80%
copy from examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java
copy to examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/WrapperTest.java
index fb99a65..1d32ab3 100644
--- a/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java
+++ b/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/WrapperTest.java
@@ -18,6 +18,7 @@
*/
package org.apache.isis.testdomain.commandexecution;
+import java.util.EnumSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -30,6 +31,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
+import org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase;
import org.apache.isis.applib.services.background.BackgroundService;
import org.apache.isis.applib.services.factory.FactoryService;
import org.apache.isis.applib.services.repository.RepositoryService;
@@ -49,7 +51,7 @@ import lombok.val;
@SpringBootTest(
classes = {
JdoTestDomainModule.class,
- BackgroundExecutionTest.ActionDomainEventListener.class
+ WrapperTest.ActionDomainEventListener.class
},
properties = {
"logging.config=log4j2-test.xml",
@@ -57,7 +59,7 @@ import lombok.val;
// "logging.level.org.apache.isis.jdo.persistence.PersistenceSession5=DEBUG",
// "logging.level.org.apache.isis.jdo.persistence.IsisTransactionJdo=DEBUG",
})
-class BackgroundExecutionTest {
+class WrapperTest {
@Inject FixtureScripts fixtureScripts;
@Inject RepositoryService repository;
@@ -76,21 +78,7 @@ class BackgroundExecutionTest {
}
@Test
- void testBackgroundService_waitingForFixedTime() throws InterruptedException, ExecutionException {
-
- val inventoryManager = facoryService.viewModel(InventoryManager.class);
- val product = repository.allInstances(Product.class).get(0);
-
- assertEquals(99d, product.getPrice(), 1E-6);
-
- backgroundService.execute(inventoryManager).updateProductPrice(product, 123);
-
- Thread.sleep(1000); //TODO fragile test, find another way to sync on the background task
- assertEquals(123d, product.getPrice(), 1E-6);
- }
-
- @Test
- void testBackgroundService_waitingOnDomainEvent() throws InterruptedException, ExecutionException {
+ void testWrapper_waitingOnDomainEvent() throws InterruptedException, ExecutionException {
val inventoryManager = facoryService.viewModel(InventoryManager.class);
val product = repository.allInstances(Product.class).get(0);
@@ -99,7 +87,7 @@ class BackgroundExecutionTest {
actionDomainEventListener.prepareLatch();
- backgroundService.execute(inventoryManager).updateProductPrice(product, 123);
+ wrapperFactory.wrap(inventoryManager).updateProductPrice(product, 123);
assertTrue(
actionDomainEventListener.getCountDownLatch()
@@ -107,9 +95,9 @@ class BackgroundExecutionTest {
assertEquals(123d, product.getPrice(), 1E-6);
}
-
+
@Test
- void testWrapper_waitingOnDomainEvent() throws InterruptedException, ExecutionException {
+ void testWrapper_async_waitingOnDomainEvent() throws InterruptedException, ExecutionException {
val inventoryManager = facoryService.viewModel(InventoryManager.class);
val product = repository.allInstances(Product.class).get(0);
@@ -118,7 +106,8 @@ class BackgroundExecutionTest {
actionDomainEventListener.prepareLatch();
- wrapperFactory.wrap(inventoryManager).updateProductPrice(product, 123);
+ wrapperFactory.wrap(inventoryManager, EnumSet.of(WrapperFactory.ExecutionMode.ASYNC))
+ .updateProductPrice(product, 123);
assertTrue(
actionDomainEventListener.getCountDownLatch()
@@ -126,7 +115,7 @@ class BackgroundExecutionTest {
assertEquals(123d, product.getPrice(), 1E-6);
}
-
+
@Service
public static class ActionDomainEventListener {
@@ -134,10 +123,10 @@ class BackgroundExecutionTest {
@Getter private CountDownLatch countDownLatch;
@EventListener(InventoryManager.UpdateProductPriceEvent.class)
- public void hi(InventoryManager.UpdateProductPriceEvent event) {
- //FIXME[2125] not triggered yet
- System.err.println("!!!!!!!!!!!! event " + event.getEventPhase());
- countDownLatch.countDown();
+ public void onDomainEvent(InventoryManager.UpdateProductPriceEvent event) {
+ if(event.getEventPhase()==Phase.EXECUTED) {
+ countDownLatch.countDown();
+ }
}
public void prepareLatch() {