@@ -1149,14 +1149,143 @@ public void testForcedCompletionOfCopies() throws Exception {
11491149 assertTrue (copy6 .isCancelled ());
11501150 }
11511151
1152+ /**
1153+ * Verify that it is possible to obtain the nested ContextService of a ManagedExecutorService
1154+ * that is configured as a ManagedExecutorDefinition, and that when withContextCapture is invoked on this ContextService,
1155+ * the resulting CompletableFuture is backed by the ManagedExecutorService, subject to its concurrency
1156+ * constraints (maxAsync=1) and runs tasks under the context propagation settings of its nested ContextService.
1157+ */
1158+ @ Test
1159+ public void testGetContextServiceFromManagedExecutorDefinition () throws Exception {
1160+ Executor bean = InitialContext .doLookup ("java:global/ConcurrencyTestApp/ConcurrencyTestEJB/ExecutorBean!java.util.concurrent.Executor" );
1161+ assertNotNull (bean );
1162+ bean .execute (() -> {
1163+ CountDownLatch blocker = new CountDownLatch (1 );
1164+ CountDownLatch blocking = new CountDownLatch (1 );
1165+ Callable <Boolean > blockerTask = () -> {
1166+ blocking .countDown ();
1167+ return blocker .await (TIMEOUT_NS , TimeUnit .NANOSECONDS );
1168+ };
1169+
1170+ try {
1171+ ManagedExecutorService executor = InitialContext .doLookup ("java:comp/concurrent/executor8" );
1172+ ContextService contextSvc = executor .getContextService ();
1173+
1174+ CompletableFuture <String > stage1 = new CompletableFuture <String >();
1175+
1176+ CompletableFuture <String > stage1copy = contextSvc .withContextCapture (stage1 );
1177+
1178+ // block the managed executor's single maxAsync slot
1179+ Future <Boolean > blockerFuture1 = executor .submit (blockerTask );
1180+ assertTrue (blocking .await (TIMEOUT_NS , TimeUnit .NANOSECONDS ));
1181+
1182+ CompletableFuture <Object > stage2 = stage1copy .thenApplyAsync (jndiName -> {
1183+ try {
1184+ return InitialContext .doLookup (jndiName );
1185+ } catch (NamingException x ) {
1186+ throw new CompletionException (x );
1187+ }
1188+ });
1189+
1190+ stage1 .complete ("java:comp/concurrent/executor8" );
1191+
1192+ // copied stage completes,
1193+ assertEquals ("java:comp/concurrent/executor8" , stage1copy .get (TIMEOUT_NS , TimeUnit .NANOSECONDS ));
1194+
1195+ // but the async stage must be blocked from running on the executor,
1196+ try {
1197+ Object result = stage2 .get (1 , TimeUnit .SECONDS );
1198+ fail ("Dependent stage of withContextCapture stage should be blocked from asynchronous execution " +
1199+ "due to both maxAsync slots of the executor being used up. Instead: " + result );
1200+ } catch (TimeoutException x ) {
1201+ // expected
1202+ }
1203+
1204+ blocker .countDown ();
1205+
1206+ Object result ;
1207+ assertNotNull (result = stage2 .get (TIMEOUT_NS , TimeUnit .NANOSECONDS ));
1208+ assertTrue (result .toString (), result instanceof ManagedExecutorService );
1209+
1210+ assertEquals (true , blockerFuture1 .get (TIMEOUT_NS , TimeUnit .NANOSECONDS ));
1211+ } catch (ExecutionException | InterruptedException | NamingException | TimeoutException x ) {
1212+ throw new EJBException (x );
1213+ } finally {
1214+ blocker .countDown ();
1215+ }
1216+ });
1217+ }
1218+
1219+ /**
1220+ * Verify that it is possible to obtain the nested ContextService of a ManagedScheduledExecutorService
1221+ * that is configured as a ManagedScheduledExecutorDefinition, and that when withContextCapture is invoked on this ContextService,
1222+ * the resulting CompletableFuture is backed by the ManagedScheduledExecutorService, subject to its concurrency
1223+ * constraints (maxAsync=2) and runs tasks under the context propagation settings of its nested ContextService.
1224+ */
1225+ @ Test
1226+ public void testGetContextServiceFromManagedScheduledExecutorDefinition () throws Exception {
1227+ ManagedScheduledExecutorService executor = InitialContext .doLookup ("java:comp/concurrent/executor6" );
1228+ ContextService contextSvc = executor .getContextService ();
1229+
1230+ CompletableFuture <String > stage1 = new CompletableFuture <String >();
1231+
1232+ CompletableFuture <String > stage1copy = contextSvc .withContextCapture (stage1 );
1233+
1234+ CountDownLatch blocker = new CountDownLatch (1 );
1235+ CountDownLatch blocking = new CountDownLatch (2 );
1236+ Callable <Boolean > blockerTask = () -> {
1237+ blocking .countDown ();
1238+ return blocker .await (TIMEOUT_NS , TimeUnit .NANOSECONDS );
1239+ };
1240+ try {
1241+ // block both of the managed executor's maxAsync slots
1242+ Future <Boolean > blockerFuture1 = executor .submit (blockerTask );
1243+ Future <Boolean > blockerFuture2 = executor .submit (blockerTask );
1244+ assertTrue (blocking .await (TIMEOUT_NS , TimeUnit .NANOSECONDS ));
1245+
1246+ CompletableFuture <Object > stage2 = stage1copy .thenApplyAsync (jndiName -> {
1247+ try {
1248+ return InitialContext .doLookup (jndiName );
1249+ } catch (NamingException x ) {
1250+ throw new CompletionException (x );
1251+ }
1252+ });
1253+
1254+ stage1 .complete ("java:comp/concurrent/executor6" );
1255+
1256+ // copied stage completes,
1257+ assertEquals ("java:comp/concurrent/executor6" , stage1copy .get (TIMEOUT_NS , TimeUnit .NANOSECONDS ));
1258+
1259+ // but the async stage must be blocked from running on the executor,
1260+ try {
1261+ Object result = stage2 .get (1 , TimeUnit .SECONDS );
1262+ fail ("Dependent stage of withContextCapture stage should be blocked from asynchronous execution " +
1263+ "due to both maxAsync slots of the executor being used up. Instead: " + result );
1264+ } catch (TimeoutException x ) {
1265+ // expected
1266+ }
1267+
1268+ blocker .countDown ();
1269+
1270+ Object result ;
1271+ assertNotNull (result = stage2 .get (TIMEOUT_NS , TimeUnit .NANOSECONDS ));
1272+ assertTrue (result .toString (), result instanceof ManagedScheduledExecutorService );
1273+
1274+ assertEquals (true , blockerFuture1 .get (TIMEOUT_NS , TimeUnit .NANOSECONDS ));
1275+ assertEquals (true , blockerFuture2 .get (TIMEOUT_NS , TimeUnit .NANOSECONDS ));
1276+ } finally {
1277+ blocker .countDown ();
1278+ }
1279+ }
1280+
11521281 /**
11531282 * Verify that it is possible to obtain the nested ContextService of a ManagedExecutorService
11541283 * that is configured in server.xml, and that when withContextCapture is invoked on this ContextService,
11551284 * the resulting CompletableFuture is backed by the ManagedExecutorService, subject to its concurrency
11561285 * constraints, and runs tasks under the context propagation settings of its nested ContextService.
11571286 */
11581287 @ Test
1159- public void testGetContextService1WithContextCapture () throws Exception {
1288+ public void testGetContextServiceFromServerXMLWithContextCapture () throws Exception {
11601289 ContextService contextSvc = executor1 .getContextService ();
11611290
11621291 CompletableFuture <String > stage1 = new CompletableFuture <String >();
0 commit comments