Skip to content

Commit

Permalink
Issue #14587 default binding for concurrency extension, and other tests
Browse files Browse the repository at this point in the history
  • Loading branch information
njr-11 committed Oct 29, 2020
1 parent a31565a commit aa6a9f8
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 14 deletions.
Expand Up @@ -46,6 +46,7 @@
<AD id="ConcurrencyPolicy.target" type="String" ibm:final="true" default="(service.pid=${concurrencyPolicyRef})" name="internal" description="internal use only"/>
<AD id="contextServiceRef" type="String" ibm:type="pid" ibm:reference="com.ibm.ws.context.service" default="DefaultContextService" cardinality="1" name="%contextServiceRef" description="%contextServiceRef.desc"/>
<AD id="ContextService.target" type="String" ibm:final="true" default="(service.pid=${contextServiceRef})" name="internal" description="internal use only"/>
<AD id="creates.objectClass" type="String" ibm:final="true" ibm:variable="io.openliberty.concurrency.mes.objectClasses" default="java.util.concurrent.ExecutorService, javax.enterprise.concurrent.ManagedExecutorService, org.eclipse.microprofile.context.ManagedExecutor" cardinality="10" name="internal" description="internal use only"/>
<AD id="javaCompDefaultName" type="String" required="false" name="internal" description="internal use only" />
<AD id="jndiName" type="String" required="false" ibm:unique="jndiName" name="%jndiName" description="%jndiName.desc"/>
<AD id="longRunningPolicyRef" type="String" ibm:type="pid" ibm:reference="com.ibm.ws.concurrency.policy.concurrencyPolicy" required="false" cardinality="1" name="%longRunningPolicy" description="%longRunningPolicy.desc"/>
Expand All @@ -65,6 +66,7 @@
<AD id="ConcurrencyPolicy.target" type="String" ibm:final="true" default="(service.pid=${concurrencyPolicyRef})" name="internal" description="internal use only"/>
<AD id="contextServiceRef" type="String" ibm:type="pid" ibm:reference="com.ibm.ws.context.service" default="DefaultContextService" cardinality="1" name="%contextServiceRef" description="%contextServiceRef.desc"/>
<AD id="ContextService.target" type="String" ibm:final="true" default="(service.pid=${contextServiceRef})" name="internal" description="internal use only"/>
<AD id="creates.objectClass" type="String" ibm:final="true" ibm:variable="io.openliberty.concurrency.mses.objectClasses" default="java.util.concurrent.ExecutorService, java.util.concurrent.ScheduledExecutorService, javax.enterprise.concurrent.ManagedExecutorService, javax.enterprise.concurrent.ManagedScheduledExecutorService" cardinality="10" name="internal" description="internal use only"/>
<AD id="javaCompDefaultName" type="String" required="false" name="internal" description="internal use only" />
<AD id="jndiName" type="String" required="false" ibm:unique="jndiName" name="%jndiName" description="%jndiName.desc"/>
<AD id="longRunningPolicyRef" type="String" ibm:type="pid" ibm:reference="com.ibm.ws.concurrency.policy.concurrencyPolicy" required="false" cardinality="1" name="%longRunningPolicy" description="%longRunningPolicy.desc"/>
Expand Down
Expand Up @@ -80,10 +80,7 @@
@Component(configurationPid = "com.ibm.ws.concurrent.managedExecutorService", configurationPolicy = ConfigurationPolicy.REQUIRE,
service = { ExecutorService.class, ManagedExecutor.class, ManagedExecutorService.class, //
ResourceFactory.class, ApplicationRecycleComponent.class },
reference = @Reference(name = "ApplicationRecycleCoordinator", service = ApplicationRecycleCoordinator.class),
property = { "creates.objectClass=java.util.concurrent.ExecutorService",
"creates.objectClass=javax.enterprise.concurrent.ManagedExecutorService",
"creates.objectClass=org.eclipse.microprofile.context.ManagedExecutor" })
reference = @Reference(name = "ApplicationRecycleCoordinator", service = ApplicationRecycleCoordinator.class))
public class ManagedExecutorServiceImpl implements ExecutorService, //
ManagedExecutor, ManagedExecutorService, CompletionStageExecutor, //
ResourceFactory, ApplicationRecycleComponent, WSManagedExecutorService {
Expand Down
Expand Up @@ -40,11 +40,7 @@
service = { ExecutorService.class, ManagedExecutorService.class, //
ResourceFactory.class, ApplicationRecycleComponent.class, //
ScheduledExecutorService.class, ManagedScheduledExecutorService.class },
reference = @Reference(name = "ApplicationRecycleCoordinator", service = ApplicationRecycleCoordinator.class),
property = { "creates.objectClass=java.util.concurrent.ExecutorService",
"creates.objectClass=java.util.concurrent.ScheduledExecutorService",
"creates.objectClass=javax.enterprise.concurrent.ManagedExecutorService",
"creates.objectClass=javax.enterprise.concurrent.ManagedScheduledExecutorService" })
reference = @Reference(name = "ApplicationRecycleCoordinator", service = ApplicationRecycleCoordinator.class))
public class ManagedScheduledExecutorServiceImpl extends ManagedExecutorServiceImpl implements ManagedScheduledExecutorService {

private static final TraceComponent tc = Tr.register(ManagedScheduledExecutorServiceImpl.class);
Expand Down
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-bnd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://websphere.ibm.com/xml/ns/javaee"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-web-bnd_1_0.xsd" version="1.0">

<resource-ref name="java:module/env/wm/executorRef" binding-name="wm/executor"/>

</web-bnd>
Expand Up @@ -9,5 +9,16 @@
<res-type>test.concurrent.work.WorkManager</res-type>
<lookup-name>wm/scheduledExecutor</lookup-name>
</resource-ref>


<resource-ref>
<res-ref-name>java:module/env/wm/executorRef</res-ref-name>
<res-type>test.concurrent.work.WorkManager</res-type>
<!-- see ibm-web-bnd.xml file for binding -->
</resource-ref>

<resource-ref>
<res-ref-name>wm/executor</res-ref-name>
<res-type>test.concurrent.work.WorkManager</res-type>
<!-- uses default binding -->
</resource-ref>
</web-app>
Expand Up @@ -15,7 +15,9 @@
import java.util.concurrent.CompletionException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Phaser;
import java.util.concurrent.ScheduledExecutorService;
Expand All @@ -38,6 +40,7 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

Expand Down Expand Up @@ -66,13 +69,29 @@ public class WorkTestServlet extends FATServlet {
@Resource(lookup = "wm/scheduledExecutor", name = "java:app/env/wm/scheduledExecutorServiceRef")
private ScheduledExecutorService scheduledExecutorService;

@Resource(lookup = "java:comp/DefaultManagedExecutorService")
private WorkManager wmDefaultExecutor;

@Resource(lookup = "java:comp/DefaultManagedScheduledExecutorService")
private WorkManager wmDefaultScheduledExecutor;

@Resource(lookup = "wm/executor", name = "java:global/env/wm/wmExecutorRef")
private WorkManager wmExecutor;

@Resource(lookup = "wm/scheduledExecutor")
private WorkManager wmScheduledExecutor;

// Defined in web.xml: java:comp/env/wm/scheduledExecutorRef
@Resource(name = "wm/scheduledExecutor")
private WorkManager wmScheduledExecutorDefaultBinding;

// Defined in web.xml:
// java:comp/env/wm/executor
// java:module/env/wm/executorRef
// java:comp/env/wm/scheduledExecutorRef

// The following is not supported. This pattern only works for EE spec-defined default resources.
// @Resource
// private WorkManager workManager;

/**
* Direct lookup of a configured managedExecutorService as a WorkManager.
Expand Down Expand Up @@ -113,6 +132,66 @@ public void testDirectLookupManagedScheduledExecutorServiceAsWorkManager(HttpSer
assertNotNull(result.get(TIMEOUT_NS, TimeUnit.NANOSECONDS));
}

/**
* Inject the default instance java:comp/env/DefaultManagedExecutorService as a WorkManager.
*/
@Test
public void testInjectDefaultManagedExecutorAsWorkManager(HttpServletRequest request, HttpServletResponse response) throws Exception {
assertNotNull(wmDefaultExecutor);
assertTrue(wmDefaultExecutor.toString(), wmDefaultExecutor instanceof ExecutorService);
assertTrue(wmDefaultExecutor.toString(), wmDefaultExecutor instanceof ManagedExecutorService);

// Use as both WorkManager and ManagedExecutorService
CountDownLatch workCompleted = new CountDownLatch(1);
Work work = () -> workCompleted.countDown();

Future<WorkItem> future = ((ManagedExecutorService) wmDefaultExecutor).submit(() -> {
return wmDefaultExecutor.schedule(work);
});

assertTrue(workCompleted.await(TIMEOUT_NS, TimeUnit.NANOSECONDS));

WorkItem workItem = future.get(TIMEOUT_NS, TimeUnit.NANOSECONDS);

for (long start = System.nanoTime(); workItem.getResult() == null && System.nanoTime() - start < TIMEOUT_NS;)
Thread.sleep(POLL_INTERVAL);

assertEquals(work, workItem.getResult());
}

/**
* Inject the default instance java:comp/env/DefaultManagedScheduledExecutorService as a WorkManager.
*/
@Test
public void testInjectDefaultManagedScheduledExecutorAsWorkManager(HttpServletRequest request, HttpServletResponse response) throws Exception {
assertNotNull(wmDefaultScheduledExecutor);
assertTrue(wmDefaultScheduledExecutor.toString(), wmDefaultScheduledExecutor instanceof ScheduledExecutorService);
assertTrue(wmDefaultScheduledExecutor.toString(), wmDefaultScheduledExecutor instanceof ManagedScheduledExecutorService);

// Use as both WorkManager and ManagedScheduledExecutorService
AtomicReference<WorkManager> result = new AtomicReference<>();
Work work = () -> {
try {
// Requires JEE metadata context of the application:
result.set(InitialContext.doLookup("java:module/env/wm/executorServiceRef"));
} catch (NamingException x) {
throw new CompletionException(x);
}
};

Future<WorkItem> future = ((ManagedScheduledExecutorService) wmDefaultScheduledExecutor).schedule(() -> {
return wmDefaultScheduledExecutor.schedule(work);
}, 100, TimeUnit.MILLISECONDS);

WorkItem workItem = future.get(TIMEOUT_NS, TimeUnit.NANOSECONDS);

for (long start = System.nanoTime(); workItem.getResult() == null && System.nanoTime() - start < TIMEOUT_NS;)
Thread.sleep(POLL_INTERVAL);

assertEquals(work, workItem.getResult());
assertNotNull(result.get());
}

/**
* Inject a configured managedExecutorService and cast it to WorkManager
*/
Expand Down Expand Up @@ -324,6 +403,21 @@ public void testLookUpDefaultManagedScheduledExecutorServiceAsWorkManager(HttpSe
assertTrue(result.toString(), result instanceof WorkManager);
}

/**
* Inject a WorkManager where the resource reference specifies only the type (WorkManager) and name,
* from which the default binding implies the managedScheduledExecutorService instance to use.
*/
@Test
public void testWorkManagerInjectionWithDefaultBinding(HttpServletRequest request, HttpServletResponse response) throws Exception {
WorkManager wm = wmScheduledExecutorDefaultBinding;
assertNotNull(wm);
assertTrue(wm.toString(), wm instanceof ExecutorService);
assertTrue(wm.toString(), wm instanceof ManagedExecutorService);

// Verify we received the correct instance rather than the default one
assertTrue(wm.toString(), wm.toString().endsWith(" wm/scheduledExecutor"));
}

/**
* Perform a lookup of a managedExecutorService using an annotatively-defined resource reference
* that specifies the resource type as WorkManager.
Expand Down Expand Up @@ -433,4 +527,87 @@ public void testWorkManagerResourceReferenceLookupOfManagedScheduledExecutorServ
assertEquals(blockingWork, blockingItem2.getResult());
assertEquals(3, workStarted.getPhase());
}

/**
* Perform a lookup using a deployment descriptor defined resource reference that specifies the
* resource type as WorkManager and uses binding-name rather than lookup-name to identify the
* managedExecutorService instance to use.
*/
@Test
public void testWorkManagerResourceReferenceLookupWithBindingToManagedExecutorService(HttpServletRequest request, HttpServletResponse response) throws Exception {
WorkManager wm = InitialContext.doLookup("java:module/env/wm/executorRef");
assertNotNull(wm);
assertTrue(wm.toString(), wm instanceof ExecutorService);
assertTrue(wm.toString(), wm instanceof ManagedExecutorService);

// Verify we received the correct instance
assertTrue(wm.toString(), wm.toString().endsWith(" wm/executor"));
}

/**
* Perform a lookup using a deployment descriptor defined resource reference that specifies the
* resource type as WorkManager and relies on default bindings rather than binding-name or lookup-name
* to identify the managedExecutorService instance to use.
*/
@Test
public void testWorkManagerResourceReferenceLookupWithDefaultBinding(HttpServletRequest request, HttpServletResponse response) throws Exception {
WorkManager wm = InitialContext.doLookup("java:comp/env/wm/executor");
assertNotNull(wm);
assertTrue(wm.toString(), wm instanceof ExecutorService);
assertTrue(wm.toString(), wm instanceof ManagedExecutorService);

// Verify we received the correct instance rather than the default one
assertTrue(wm.toString(), wm.toString().endsWith(" wm/executor"));

LinkedBlockingQueue<String> threadNames = new LinkedBlockingQueue<>();
Exchanger<String> status = new Exchanger<>();
Work work1 = () -> {
try {
threadNames.add(Thread.currentThread().getName());
assertEquals("waiting for work to start", status.exchange("work started", TIMEOUT_NS, TimeUnit.NANOSECONDS));
assertEquals("waiting for work to complete", status.exchange("work completed", TIMEOUT_NS, TimeUnit.NANOSECONDS));
} catch (InterruptedException | TimeoutException x) {
throw new CompletionException(x);
}
};
WorkItem item1 = wm.schedule(work1);

// The next 2 scheduled work items should be stuck in queue:
Work work2 = () -> threadNames.add(Thread.currentThread().getName());
WorkItem item2 = wm.schedule(work2);

assertEquals("work started", status.exchange("waiting for work to start", TIMEOUT_NS, TimeUnit.NANOSECONDS));

Work work3 = () -> threadNames.add(Thread.currentThread().getName());
WorkItem item3 = wm.schedule(work3);

// No more work should fit in the queue
Work work4 = () -> threadNames.add(Thread.currentThread().getName());
try {
WorkItem item4 = wm.schedule(work4);
fail(item4 + " exceeded the maxQueueSize of 2 if the following are null: " //
+ item1.getResult() + ", " + item2.getResult() + ", " + item3.getResult());
} catch (WorkRejectedException x) {
// expected
}

// no work should have completed yet
assertNull(item1.getResult());
assertNull(item2.getResult());
assertNull(item3.getResult());

assertEquals("work completed", status.exchange("waiting for work to complete", TIMEOUT_NS, TimeUnit.NANOSECONDS));

// all work should complete now, and should have run on the Liberty thread pool
String threadName;
assertNotNull(threadName = threadNames.poll(TIMEOUT_NS, TimeUnit.NANOSECONDS));
assertTrue(threadName, threadName.startsWith("Default Executor-thread-"));
assertNotNull(threadName = threadNames.poll(TIMEOUT_NS, TimeUnit.NANOSECONDS));
assertTrue(threadName, threadName.startsWith("Default Executor-thread-"));
assertNotNull(threadName = threadNames.poll(TIMEOUT_NS, TimeUnit.NANOSECONDS));
assertTrue(threadName, threadName.startsWith("Default Executor-thread-"));

// work item 4 should not have run
assertNull(threadNames.poll());
}
}
Expand Up @@ -10,4 +10,10 @@
-->
<server>
<concurrencyService extensionProvider.component.name="test.concurrent.work.wm.WorkManagerProvider"/>

<variable name="io.openliberty.concurrency.mes.objectClasses"
value="java.util.concurrent.ExecutorService, javax.enterprise.concurrent.ManagedExecutorService, org.eclipse.microprofile.context.ManagedExecutor, test.concurrent.work.WorkManager"/>

<variable name="io.openliberty.concurrency.mses.objectClasses"
value="java.util.concurrent.ExecutorService, java.util.concurrent.ScheduledExecutorService, javax.enterprise.concurrent.ManagedExecutorService, javax.enterprise.concurrent.ManagedScheduledExecutorService, test.concurrent.work.WorkManager"/>
</server>
Expand Up @@ -36,12 +36,25 @@
*/
public class SchedulingWorkManagerImpl extends ManagedScheduledExecutorExtension implements WorkManager {
private final WSManagedExecutorService executor;
private final String toString;

SchedulingWorkManagerImpl(WSManagedExecutorService executor, ResourceInfo resourceInfo) {
super(executor, resourceInfo);
this.executor = executor;

// Generate toString output from which tests can check that the proper managed executor was used:
// WorkManager@12345678 java:module/env/wm/executorRef of ManagedExecutor@9abcdef0 wm/executor
// or
// direct lookup @87654321 of ManagedExecutor@9abcdef0 wm/executor
String type = resourceInfo == null ? "direct lookup " : resourceInfo.getType().substring(resourceInfo.getType().lastIndexOf('.'));
StringBuilder s = new StringBuilder(type).append('@').append(Integer.toHexString(System.identityHashCode(this)));
if (resourceInfo != null)
s.append(' ').append(resourceInfo.getName());
s.append(" of ").append(executor.toString());
toString = s.toString();
}

@Override
public WorkItem schedule(Work work) throws WorkRejectedException {
try {
Future<Work> future = ((ExecutorService) executor).submit(() -> {
Expand All @@ -53,4 +66,9 @@ public WorkItem schedule(Work work) throws WorkRejectedException {
throw new WorkRejectedException(x);
}
}

@Override
public final String toString() {
return toString;
}
}

0 comments on commit aa6a9f8

Please sign in to comment.