Skip to content

Commit

Permalink
Merge pull request #20889 from njr-11/18669-test-getContextService-fr…
Browse files Browse the repository at this point in the history
…om-executor-definitions

test getContextService from executor definitions
  • Loading branch information
njr-11 authored Apr 22, 2022
2 parents f7cdc28 + 79d3ba2 commit 74fd263
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1149,14 +1149,143 @@ public void testForcedCompletionOfCopies() throws Exception {
assertTrue(copy6.isCancelled());
}

/**
* Verify that it is possible to obtain the nested ContextService of a ManagedExecutorService
* that is configured as a ManagedExecutorDefinition, and that when withContextCapture is invoked on this ContextService,
* the resulting CompletableFuture is backed by the ManagedExecutorService, subject to its concurrency
* constraints (maxAsync=1) and runs tasks under the context propagation settings of its nested ContextService.
*/
@Test
public void testGetContextServiceFromManagedExecutorDefinition() throws Exception {
Executor bean = InitialContext.doLookup("java:global/ConcurrencyTestApp/ConcurrencyTestEJB/ExecutorBean!java.util.concurrent.Executor");
assertNotNull(bean);
bean.execute(() -> {
CountDownLatch blocker = new CountDownLatch(1);
CountDownLatch blocking = new CountDownLatch(1);
Callable<Boolean> blockerTask = () -> {
blocking.countDown();
return blocker.await(TIMEOUT_NS, TimeUnit.NANOSECONDS);
};

try {
ManagedExecutorService executor = InitialContext.doLookup("java:comp/concurrent/executor8");
ContextService contextSvc = executor.getContextService();

CompletableFuture<String> stage1 = new CompletableFuture<String>();

CompletableFuture<String> stage1copy = contextSvc.withContextCapture(stage1);

// block the managed executor's single maxAsync slot
Future<Boolean> blockerFuture1 = executor.submit(blockerTask);
assertTrue(blocking.await(TIMEOUT_NS, TimeUnit.NANOSECONDS));

CompletableFuture<Object> stage2 = stage1copy.thenApplyAsync(jndiName -> {
try {
return InitialContext.doLookup(jndiName);
} catch (NamingException x) {
throw new CompletionException(x);
}
});

stage1.complete("java:comp/concurrent/executor8");

// copied stage completes,
assertEquals("java:comp/concurrent/executor8", stage1copy.get(TIMEOUT_NS, TimeUnit.NANOSECONDS));

// but the async stage must be blocked from running on the executor,
try {
Object result = stage2.get(1, TimeUnit.SECONDS);
fail("Dependent stage of withContextCapture stage should be blocked from asynchronous execution " +
"due to both maxAsync slots of the executor being used up. Instead: " + result);
} catch (TimeoutException x) {
// expected
}

blocker.countDown();

Object result;
assertNotNull(result = stage2.get(TIMEOUT_NS, TimeUnit.NANOSECONDS));
assertTrue(result.toString(), result instanceof ManagedExecutorService);

assertEquals(true, blockerFuture1.get(TIMEOUT_NS, TimeUnit.NANOSECONDS));
} catch (ExecutionException | InterruptedException | NamingException | TimeoutException x) {
throw new EJBException(x);
} finally {
blocker.countDown();
}
});
}

/**
* Verify that it is possible to obtain the nested ContextService of a ManagedScheduledExecutorService
* that is configured as a ManagedScheduledExecutorDefinition, and that when withContextCapture is invoked on this ContextService,
* the resulting CompletableFuture is backed by the ManagedScheduledExecutorService, subject to its concurrency
* constraints (maxAsync=2) and runs tasks under the context propagation settings of its nested ContextService.
*/
@Test
public void testGetContextServiceFromManagedScheduledExecutorDefinition() throws Exception {
ManagedScheduledExecutorService executor = InitialContext.doLookup("java:comp/concurrent/executor6");
ContextService contextSvc = executor.getContextService();

CompletableFuture<String> stage1 = new CompletableFuture<String>();

CompletableFuture<String> stage1copy = contextSvc.withContextCapture(stage1);

CountDownLatch blocker = new CountDownLatch(1);
CountDownLatch blocking = new CountDownLatch(2);
Callable<Boolean> blockerTask = () -> {
blocking.countDown();
return blocker.await(TIMEOUT_NS, TimeUnit.NANOSECONDS);
};
try {
// block both of the managed executor's maxAsync slots
Future<Boolean> blockerFuture1 = executor.submit(blockerTask);
Future<Boolean> blockerFuture2 = executor.submit(blockerTask);
assertTrue(blocking.await(TIMEOUT_NS, TimeUnit.NANOSECONDS));

CompletableFuture<Object> stage2 = stage1copy.thenApplyAsync(jndiName -> {
try {
return InitialContext.doLookup(jndiName);
} catch (NamingException x) {
throw new CompletionException(x);
}
});

stage1.complete("java:comp/concurrent/executor6");

// copied stage completes,
assertEquals("java:comp/concurrent/executor6", stage1copy.get(TIMEOUT_NS, TimeUnit.NANOSECONDS));

// but the async stage must be blocked from running on the executor,
try {
Object result = stage2.get(1, TimeUnit.SECONDS);
fail("Dependent stage of withContextCapture stage should be blocked from asynchronous execution " +
"due to both maxAsync slots of the executor being used up. Instead: " + result);
} catch (TimeoutException x) {
// expected
}

blocker.countDown();

Object result;
assertNotNull(result = stage2.get(TIMEOUT_NS, TimeUnit.NANOSECONDS));
assertTrue(result.toString(), result instanceof ManagedScheduledExecutorService);

assertEquals(true, blockerFuture1.get(TIMEOUT_NS, TimeUnit.NANOSECONDS));
assertEquals(true, blockerFuture2.get(TIMEOUT_NS, TimeUnit.NANOSECONDS));
} finally {
blocker.countDown();
}
}

/**
* Verify that it is possible to obtain the nested ContextService of a ManagedExecutorService
* that is configured in server.xml, and that when withContextCapture is invoked on this ContextService,
* the resulting CompletableFuture is backed by the ManagedExecutorService, subject to its concurrency
* constraints, and runs tasks under the context propagation settings of its nested ContextService.
*/
@Test
public void testGetContextService1WithContextCapture() throws Exception {
public void testGetContextServiceFromServerXMLWithContextCapture() throws Exception {
ContextService contextSvc = executor1.getContextService();

CompletableFuture<String> stage1 = new CompletableFuture<String>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
@ContextServiceDefinition(name = "java:app/concurrent/ThreadNameContext",
propagated = { "SyncToOSThread", SECURITY, APPLICATION })

//TODO move to web.xml and include properties?
@ContextServiceDefinition(name = "java:module/concurrent/zosWLMContext",
propagated = { "Classification" },
cleared = { TRANSACTION, "SyncToOSThread", SECURITY },
Expand Down Expand Up @@ -244,4 +243,54 @@ public void testPropagateSimulatedZOSWLMContext() throws Exception {
Enclave.clear();
}
}

/**
* Configure vendor properties for PropagateOrNew and with different default transaction
* classes for zosWLMContext and verify that the fake context type that we are using to simulate it
* propagates or creates the new context on the thread.
*/
// TODO If the spec ever adds a way for vendor properties to be supplied to context types, this test could help provide coverage.
// @Test
public void testVendorPropertiesSimulatedZOSWLMContext() throws Exception {
// Instead of testing the real z/OS WLM context behavior,
// the fake context provider updates the state of a mock Enclave class.
ContextService contextSvc = InitialContext.doLookup("java:comp/concurrent/zosWLMContextPropagateOrNew");

String originalName = Thread.currentThread().getName();
try {
Enclave.setTransactionClass("TX_CLASS_D");

Supplier<String> txClassSupplier = contextSvc.contextualSupplier(Enclave::getTransactionClass);

Enclave.setTransactionClass("TX_CLASS_E");

assertEquals("TX_CLASS_D", txClassSupplier.get());

assertEquals("TX_CLASS_E", Enclave.getTransactionClass());

// Propagate the absence of context:

Enclave.clear();

txClassSupplier = contextSvc.contextualSupplier(Enclave::getTransactionClass);

Enclave.setTransactionClass("TX_CLASS_F");

assertEquals("DEFAULT_TX", txClassSupplier.get());

assertEquals("TX_CLASS_F", Enclave.getTransactionClass());

// Long running task:

@SuppressWarnings("unchecked")
Supplier<String> longRunningTxSupplier = contextSvc.createContextualProxy(Enclave::getTransactionClass,
Collections.singletonMap(ManagedTask.LONGRUNNING_HINT, "true"),
Supplier.class);
assertEquals("DAEMON_TX", longRunningTxSupplier.get());

assertEquals("TX_CLASS_F", Enclave.getTransactionClass());
} finally {
Enclave.clear();
}
}
}

0 comments on commit 74fd263

Please sign in to comment.