Index: src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java (date 1539782846000) +++ src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java (date 1608218062197) @@ -1224,6 +1224,7 @@ } unmarshaller.finish(); } catch (IOException | ClassNotFoundException ex) { + discardResult(); throw new EJBException("Failed to read response", ex); } return result; Index: src/test/java/org/jboss/ejb/client/test/InterruptRuningCallTestCase.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- src/test/java/org/jboss/ejb/client/test/InterruptRuningCallTestCase.java (date 1608218084003) +++ src/test/java/org/jboss/ejb/client/test/InterruptRuningCallTestCase.java (date 1608218084003) @@ -0,0 +1,143 @@ +package org.jboss.ejb.client.test; + +import org.jboss.ejb.client.test.common.DummyServer; +import org.jboss.ejb.client.test.common.Echo; +import org.jboss.ejb.client.test.common.EchoBean; +import org.jboss.logging.Logger; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.wildfly.naming.client.WildFlyInitialContextFactory; +import org.wildfly.naming.client.WildFlyRootContext; +import org.wildfly.naming.client.util.FastHashtable; + +import javax.naming.NamingException; +import java.util.Arrays; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.*; +import java.util.stream.Collectors; + + +public class InterruptRuningCallTestCase { + private static final Logger logger = Logger.getLogger(InterruptRuningCallTestCase.class); + + private static final String APP_NAME = "my-foo-app"; + private static final String MODULE_NAME = "my-bar-module"; + private static final String DISTINCT_NAME = ""; + + private static final String SERVER_NAME = "test-server"; + + private static DummyServer server; + + @BeforeClass + public static void beforeTest() throws Exception { + // start a server + server = new DummyServer("localhost", 6999, SERVER_NAME); + server.start(); + logger.info("Started server ..."); + + String longResponse = generateLongResponse(131072 * 10); + server.register(APP_NAME, MODULE_NAME, DISTINCT_NAME, EchoBean.class.getSimpleName(), new EchoBean(longResponse)); + logger.info("Registered module ..."); + } + + @AfterClass + public static void afterTest() { + server.unregister(APP_NAME, MODULE_NAME, DISTINCT_NAME, EchoBean.class.getName()); + logger.info("Unregistered module ..."); + + try { + server.stop(); + } catch (Throwable t) { + logger.info("Could not stop server", t); + } + logger.info("Stopped server ..."); + } + + @Test + public void testInterruptingLongRunningRequests() throws Exception + { + String longResponse = generateLongResponse(131072 * 10); + + server.register(APP_NAME, MODULE_NAME, DISTINCT_NAME, EchoBean.class.getSimpleName(), new EchoBean(longResponse)); + + FastHashtable props = new FastHashtable<>(); + props.put("java.naming.factory.initial", WildFlyInitialContextFactory.class.getName()); + props.put("java.naming.provider.url", "remote://localhost:6999"); + + WildFlyRootContext context = new WildFlyRootContext(props); + + ExecutorService executorService = Executors.newFixedThreadPool(1); + Future future = null; + + for (int i = 0; i < 10; i++) + { + if (future != null) { + future.cancel(true); + } + + CountDownLatch latch = new CountDownLatch(1); + future = executorService.submit(whoAreYouCallable(context, latch)); + + latch.await(5, TimeUnit.SECONDS); + Thread.sleep(10); + } + + future.get(); + + Map stackTraces = Thread.getAllStackTraces(); + long stakedThreads = countThreadsStackedInOutboundMessageAccept(stackTraces); + Assert.assertEquals("Threads are stacked in OutboundMessage$1.accept", 0, stakedThreads); + } + + private long countThreadsStackedInOutboundMessageAccept(Map stackTraces) { + return stackTraces.entrySet().stream() + .filter(e -> e.getKey().getName().startsWith("Remoting \"test-server\" task-")) + .filter(e -> Arrays.stream(e.getValue()) + .anyMatch(s -> s.getClassName().contains("OutboundMessage") && s.getMethodName().contains("accept"))) + .peek(e -> logger.info(e.getKey() + " stacked at:\n" + Arrays.stream(e.getValue()).map(String::valueOf).collect(Collectors.joining("\n\t")))) + .count(); + } + + private Callable whoAreYouCallable(WildFlyRootContext context, CountDownLatch latch) { + return () -> { + Object echo = lookupBean(context); + if (echo != null) { + + try { + latch.countDown(); + Thread.yield(); + ((Echo) echo).whoAreYou(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + else + { + logger.error("Failed to lookup the remote bean. Invalid test setup"); + } + return "done"; + }; + } + + private Object lookupBean(WildFlyRootContext context) { + try { + return context.lookup("ejb:" + APP_NAME + "/" + MODULE_NAME + "/" + EchoBean.class.getSimpleName() + "!" + Echo.class.getName() + "?stateful"); + } catch (NamingException e) { + e.printStackTrace(); + } + return null; + } + + private static String generateLongResponse(int size) { + StringBuilder stringBuilder = new StringBuilder("generated long test: "); + String str = new Random().ints(size, 32, 125) + .mapToObj(i -> String.valueOf((char) i)) + .collect(Collectors.joining()); + stringBuilder.append(str); + return stringBuilder.toString(); + } +}