/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * http://glassfish.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.xml.ws.api.pipe; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import javax.xml.ws.Holder; import javax.xml.ws.WebServiceException; import com.sun.istack.NotNull; import com.sun.xml.ws.api.message.Packet; import com.sun.xml.ws.api.pipe.EngineTest.InlineExecutor; import com.sun.xml.ws.api.pipe.EngineTest.SimpleCompletionCallback; import com.sun.xml.ws.api.pipe.EngineTest.TestTube; import com.sun.xml.ws.api.pipe.EngineTest.TubeCall; import com.sun.xml.ws.api.pipe.EngineTest.TubeCallType; import com.sun.xml.ws.api.pipe.Fiber.CompletionCallback; import com.sun.xml.ws.api.pipe.Fiber.Listener; import com.sun.xml.ws.api.pipe.helper.AbstractFilterTubeImpl; import com.sun.xml.ws.api.server.Container; import com.sun.xml.ws.api.server.ContainerResolver; import junit.framework.TestCase; public class FiberTest extends TestCase { private Engine engine = null; private Engine threadPoolEngine = null; private Container testContainer = null; private SimpleInlineExecutor executor = null; public void setUp() { testContainer = new Container() {}; String id = "engine1"; executor = new SimpleInlineExecutor(); engine = new Engine(id, testContainer, executor); threadPoolEngine = new Engine(id, testContainer); } private static class SimpleInlineExecutor extends InlineExecutor { private ThreadLocal<Boolean> isInExecutor = new ThreadLocal<Boolean>() { @Override protected Boolean initialValue() { return Boolean.FALSE; } }; @Override public void execute(Runnable command) { isInExecutor.set(Boolean.TRUE); try { super.execute(command); } finally { isInExecutor.set(Boolean.FALSE); } } public Boolean isInExecutor() { return isInExecutor.get(); } } @SuppressWarnings("deprecation") private static class TestListener implements Listener { @Override public void fiberSuspended(Fiber fiber) { } @Override public void fiberResumed(Fiber fiber) { } } @SuppressWarnings("deprecation") public void testAddRemoveListener() { Fiber fiber = engine.createFiber(); Listener testListener = new TestListener(); List<Listener> listeners = fiber.getCurrentListeners(); assertNotNull(listeners); assertTrue(listeners.isEmpty()); fiber.addListener(testListener); listeners = fiber.getCurrentListeners(); assertNotNull(listeners); assertEquals(1, listeners.size()); assertTrue(listeners.contains(testListener)); Listener testListener2 = new TestListener(); fiber.addListener(testListener2); listeners = fiber.getCurrentListeners(); assertNotNull(listeners); assertEquals(2, listeners.size()); assertTrue(listeners.contains(testListener2)); fiber.removeListener(testListener); listeners = fiber.getCurrentListeners(); assertNotNull(listeners); assertEquals(1, listeners.size()); assertTrue(listeners.contains(testListener2)); } public void testStartTubePacketCompletionCallback() { final Holder<Boolean> isInExecutor = new Holder<Boolean>(Boolean.FALSE); TestTube testTube = new TestTube() { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { isInExecutor.value = executor.isInExecutor(); return super.processRequest(request); } }; Packet request = new Packet(); SimpleCompletionCallback callback = new SimpleCompletionCallback(); Fiber fiber = engine.createFiber(); assertNotNull(fiber); fiber.start(testTube, request, callback); assertEquals(request, callback.response); assertNull(callback.error); List<TubeCall> calls = testTube.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); assertTrue(isInExecutor.value); } public void testStartTubePacketCompletionCallbackBoolean() { final Holder<Boolean> isInExecutor = new Holder<Boolean>(Boolean.FALSE); TestTube testTube = new TestTube() { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { isInExecutor.value = executor.isInExecutor(); return super.processRequest(request); } }; Packet request = new Packet(); SimpleCompletionCallback callback = new SimpleCompletionCallback(); Fiber fiber = engine.createFiber(); assertNotNull(fiber); fiber.start(testTube, request, callback, true); assertEquals(request, callback.response); assertNull(callback.error); List<TubeCall> calls = testTube.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); assertFalse(isInExecutor.value); } ////////////////////////////// // NextAction / Flow-controlling tests static class FilterTestTube extends AbstractFilterTubeImpl { private String tubeName = null; private List<TubeCall> calls = new ArrayList<TubeCall>(); public FilterTestTube(Tube next) { super(next); } public FilterTestTube(FilterTestTube that, TubeCloner cloner) { super(that, cloner); } public List<TubeCall> getCalls() { return calls; } @Override @NotNull public NextAction processRequest(@NotNull Packet request) { Container c = ContainerResolver.getDefault().getContainer(); calls.add(new TubeCall(TubeCallType.REQUEST, request, c)); return doInvoke(next, request); } @Override @NotNull public NextAction processResponse(@NotNull Packet response) { Container c = ContainerResolver.getDefault().getContainer(); calls.add(new TubeCall(TubeCallType.RESPONSE, response, c)); return doReturnWith(response); } @Override @NotNull public NextAction processException(@NotNull Throwable t) { Packet packet = Fiber.current().getPacket(); Container c = ContainerResolver.getDefault().getContainer(); calls.add(new TubeCall(TubeCallType.EXCEPTION, packet, c)); return doThrow(t); } @Override public void preDestroy() { } @Override public FilterTestTube copy(TubeCloner cloner) { return new FilterTestTube(this, cloner); } public void setName(String tubeName) { this.tubeName = tubeName; } //Useful for debugging public String toString() { return tubeName; } } public void testNextActionInvoke() { TestTube tubeC = new TestTube(); FilterTestTube tubeB = new FilterTestTube(tubeC); FilterTestTube tubeA = new FilterTestTube(tubeB); Packet request = new Packet(); SimpleCompletionCallback callback = new SimpleCompletionCallback(); Fiber fiber = engine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback, true); assertEquals(request, callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); } public void testNextActionInvokeAndForget() { TestTube tubeC = new TestTube(); FilterTestTube tubeB = new FilterTestTube(tubeC) { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); return doInvokeAndForget(next, request); } }; FilterTestTube tubeA = new FilterTestTube(tubeB); Packet request = new Packet(); SimpleCompletionCallback callback = new SimpleCompletionCallback(); Fiber fiber = engine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback, true); assertEquals(request, callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); } public void testNextActionReturn() { TestTube tubeC = new TestTube(); FilterTestTube tubeB = new FilterTestTube(tubeC) { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); return doReturnWith(request); } }; FilterTestTube tubeA = new FilterTestTube(tubeB); Packet request = new Packet(); SimpleCompletionCallback callback = new SimpleCompletionCallback(); Fiber fiber = engine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback, true); assertEquals(request, callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); } public void testNextActionThrow() { TestTube tubeC = new TestTube() { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); throw new RuntimeException(); } }; FilterTestTube tubeB = new FilterTestTube(tubeC); FilterTestTube tubeA = new FilterTestTube(tubeB); Packet request = new Packet(); SimpleCompletionCallback callback = new SimpleCompletionCallback(); Fiber fiber = engine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback, true); assertNull(callback.response); assertTrue(callback.error instanceof RuntimeException); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.EXCEPTION, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.EXCEPTION, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); } public void testNextActionAbortResponse() { TestTube tubeC = new TestTube() { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); NextAction na = new NextAction(); na.abortResponse(request); return na; } }; FilterTestTube tubeB = new FilterTestTube(tubeC); FilterTestTube tubeA = new FilterTestTube(tubeB); Packet request = new Packet(); SimpleCompletionCallback callback = new SimpleCompletionCallback(); Fiber fiber = engine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback, true); assertEquals(request, callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); } public void testNextActionThrowAbortResponse() { TestTube tubeC = new TestTube() { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); NextAction na = new NextAction(); na.throwExceptionAbortResponse(new RuntimeException()); return na; } }; FilterTestTube tubeB = new FilterTestTube(tubeC); FilterTestTube tubeA = new FilterTestTube(tubeB); Packet request = new Packet(); SimpleCompletionCallback callback = new SimpleCompletionCallback(); Fiber fiber = engine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback, true); assertNull(callback.response); assertTrue(callback.error instanceof RuntimeException); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); } ////////////////////////////// // Suspend / resume tests public void testSuspendNextRunnableResume() throws InterruptedException { final Semaphore atSuspend = new Semaphore(0); final Semaphore checkCompleted = new Semaphore(0); final Semaphore atEnd = new Semaphore(0); final TestTube tubeC = new TestTube(); final FilterTestTube tubeB = new FilterTestTube(tubeC) { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); return doSuspend(next, new Runnable() { @Override public void run() { atSuspend.release(); try { checkCompleted.acquire(); } catch (InterruptedException e) { } } }); } }; final FilterTestTube tubeA = new FilterTestTube(tubeB); final Packet request = new Packet(); final SimpleCompletionCallback callback = new SimpleCompletionCallback() { @Override public void onCompletion(@NotNull Packet response) { super.onCompletion(response); atEnd.release(); } @Override public void onCompletion(@NotNull Throwable error) { super.onCompletion(error); atEnd.release(); } }; final Fiber fiber = threadPoolEngine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback); if (!atSuspend.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(0, atEnd.availablePermits()); // ensure test thread really blocked // thread is suspended assertNull(callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); checkCompleted.release(); fiber.resume(request); if (!atEnd.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(request, callback.response); assertNull(callback.error); calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); } public void testSuspendRunnableResume() throws InterruptedException { final Semaphore atSuspend = new Semaphore(0); final Semaphore checkCompleted = new Semaphore(0); final Semaphore atEnd = new Semaphore(0); final TestTube tubeC = new TestTube(); final FilterTestTube tubeB = new FilterTestTube(tubeC) { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); return doSuspend(new Runnable() { @Override public void run() { atSuspend.release(); try { checkCompleted.acquire(); } catch (InterruptedException e) { } } }); } }; final FilterTestTube tubeA = new FilterTestTube(tubeB); final Packet request = new Packet(); final SimpleCompletionCallback callback = new SimpleCompletionCallback() { @Override public void onCompletion(@NotNull Packet response) { super.onCompletion(response); atEnd.release(); } @Override public void onCompletion(@NotNull Throwable error) { super.onCompletion(error); atEnd.release(); } }; final Fiber fiber = threadPoolEngine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback); if (!atSuspend.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(0, atEnd.availablePermits()); // ensure test thread really blocked // thread is suspended assertNull(callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); checkCompleted.release(); fiber.resume(request); if (!atEnd.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(request, callback.response); assertNull(callback.error); calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); } public void testSuspendNextRunnableResumeThrowable() throws InterruptedException { final Semaphore atSuspend = new Semaphore(0); final Semaphore checkCompleted = new Semaphore(0); final Semaphore atEnd = new Semaphore(0); final TestTube tubeC = new TestTube(); final FilterTestTube tubeB = new FilterTestTube(tubeC) { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); return doSuspend(next, new Runnable() { @Override public void run() { atSuspend.release(); try { checkCompleted.acquire(); } catch (InterruptedException e) { } } }); } }; final FilterTestTube tubeA = new FilterTestTube(tubeB); final Packet request = new Packet(); final SimpleCompletionCallback callback = new SimpleCompletionCallback() { @Override public void onCompletion(@NotNull Packet response) { super.onCompletion(response); atEnd.release(); } @Override public void onCompletion(@NotNull Throwable error) { super.onCompletion(error); atEnd.release(); } }; final Fiber fiber = threadPoolEngine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback); if (!atSuspend.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(0, atEnd.availablePermits()); // ensure test thread really blocked // thread is suspended assertNull(callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); checkCompleted.release(); fiber.resume(new RuntimeException()); if (!atEnd.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertNull(callback.response); assertTrue(callback.error instanceof RuntimeException); calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.EXCEPTION, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.EXCEPTION, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); } public void testSuspendNextRunnableResumeThrowablePacket() throws InterruptedException { final Semaphore atSuspend = new Semaphore(0); final Semaphore checkCompleted = new Semaphore(0); final Semaphore atEnd = new Semaphore(0); final TestTube tubeC = new TestTube(); final FilterTestTube tubeB = new FilterTestTube(tubeC) { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); return doSuspend(next, new Runnable() { @Override public void run() { atSuspend.release(); try { checkCompleted.acquire(); } catch (InterruptedException e) { } } }); } }; final FilterTestTube tubeA = new FilterTestTube(tubeB); final Packet request = new Packet(); final SimpleCompletionCallback callback = new SimpleCompletionCallback() { @Override public void onCompletion(@NotNull Packet response) { super.onCompletion(response); atEnd.release(); } @Override public void onCompletion(@NotNull Throwable error) { super.onCompletion(error); atEnd.release(); } }; final Fiber fiber = threadPoolEngine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback); if (!atSuspend.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(0, atEnd.availablePermits()); // ensure test thread really blocked // thread is suspended assertNull(callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); checkCompleted.release(); Packet secondPacket = new Packet(); fiber.resume(new RuntimeException(), secondPacket); if (!atEnd.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertNull(callback.response); assertTrue(callback.error instanceof RuntimeException); calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.EXCEPTION, secondCall.callType); assertEquals(testContainer, secondCall.container); assertEquals(secondPacket, secondCall.packet); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.EXCEPTION, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); } public void testSuspendNextRunnableResumeAndReturn() throws InterruptedException { final Semaphore atSuspend = new Semaphore(0); final Semaphore checkCompleted = new Semaphore(0); final Semaphore atEnd = new Semaphore(0); final TestTube tubeC = new TestTube(); final FilterTestTube tubeB = new FilterTestTube(tubeC) { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); return doSuspend(next, new Runnable() { @Override public void run() { atSuspend.release(); try { checkCompleted.acquire(); } catch (InterruptedException e) { } } }); } }; final FilterTestTube tubeA = new FilterTestTube(tubeB); final Packet request = new Packet(); final SimpleCompletionCallback callback = new SimpleCompletionCallback() { @Override public void onCompletion(@NotNull Packet response) { super.onCompletion(response); atEnd.release(); } @Override public void onCompletion(@NotNull Throwable error) { super.onCompletion(error); atEnd.release(); } }; final Fiber fiber = threadPoolEngine.createFiber(); assertNotNull(fiber); fiber.start(tubeA, request, callback); if (!atSuspend.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(0, atEnd.availablePermits()); // ensure test thread really blocked // thread is suspended assertNull(callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); checkCompleted.release(); fiber.resumeAndReturn(request, false); if (!atEnd.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(request, callback.response); assertNull(callback.error); calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); } public void testCancel() throws InterruptedException { final Semaphore atResponse = new Semaphore(0); final Semaphore mayProceed = new Semaphore(0); TestTube tubeC = new TestTube(); FilterTestTube tubeB = new FilterTestTube(tubeC) { @Override @NotNull public NextAction processResponse(@NotNull Packet response) { try { atResponse.release(); mayProceed.acquire(); } catch (InterruptedException e) { throw new WebServiceException(e); } return super.processResponse(response); } }; final FilterTestTube tubeA = new FilterTestTube(tubeB); final Packet request = new Packet(); final SimpleCompletionCallback callback = new SimpleCompletionCallback(); final Fiber fiber = engine.createFiber(); assertNotNull(fiber); Thread testThread = new Thread(new Runnable() { @Override public void run() { fiber.start(tubeA, request, callback, true); } }); testThread.start(); if (!atResponse.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); fiber.cancel(false); mayProceed.release(); testThread.join(3 * 60 * 1000); assertNull(callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); } private static class TestFiberContextSwitchInterceptor implements FiberContextSwitchInterceptor { private String name = null; public TestFiberContextSwitchInterceptor(String name) { this.name = name; } public String toString() { return name; } private int callCount = 0; public int getCallCount() { return callCount; } @Override public <R, P> R execute(Fiber f, P p, Work<R, P> work) { callCount++; return work.execute(p); } } public void testAddInterceptorDuringSuspendResume() throws InterruptedException { final Semaphore atSuspend = new Semaphore(0); final Semaphore checkCompleted = new Semaphore(0); final Semaphore atEnd = new Semaphore(0); final TestTube tubeC = new TestTube(); final FilterTestTube tubeB = new FilterTestTube(tubeC) { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); return doSuspend(new Runnable() { @Override public void run() { atSuspend.release(); try { checkCompleted.acquire(); } catch (InterruptedException e) { } } }); } }; tubeB.setName("tubeB"); final FilterTestTube tubeA = new FilterTestTube(tubeB); tubeA.setName("tubeA"); final Packet request = new Packet(); final SimpleCompletionCallback callback = new SimpleCompletionCallback() { @Override public void onCompletion(@NotNull Packet response) { super.onCompletion(response); atEnd.release(); } @Override public void onCompletion(@NotNull Throwable error) { super.onCompletion(error); atEnd.release(); } }; final Fiber fiber = threadPoolEngine.createFiber(); assertNotNull(fiber); TestFiberContextSwitchInterceptor interceptor1 = new TestFiberContextSwitchInterceptor("interceptor1"); fiber.addInterceptor(interceptor1); fiber.start(tubeA, request, callback); if (!atSuspend.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(0, atEnd.availablePermits()); // ensure test thread really blocked // thread is suspended assertEquals(1, interceptor1.getCallCount()); assertNull(callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); checkCompleted.release(); TestFiberContextSwitchInterceptor interceptor2 = new TestFiberContextSwitchInterceptor("interceptor2"); fiber.addInterceptor(interceptor2); fiber.resume(request); if (!atEnd.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(2, interceptor1.getCallCount()); assertEquals(1, interceptor2.getCallCount()); assertEquals(request, callback.response); assertNull(callback.error); calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); } private static final Map<String,Integer> fiberNameToThreadLocalValueMap = new ConcurrentHashMap<String,Integer>(); private static final Integer retainMeInteger = new Integer(1001); private static class MyFiberContextSwitchInterceptor implements FiberContextSwitchInterceptor { private String name = null; public MyFiberContextSwitchInterceptor(String name) { this.name = name; } public String toString() { return name; } private int callCount = 0; public int getCallCount() { return callCount; } @Override public <R, P> R execute(Fiber f, P p, Work<R, P> work) { callCount++; Integer threadLocalValue = fiberNameToThreadLocalValueMap.get(f.toString()); //Set thread-local on the current thread when Fiber is switching context //so that it is available on the thread during further processing ThreadLocalHelper.set(threadLocalValue); try { return work.execute(p); } finally { //When current thread goes back to the thread pool, clean it of our thread-local ThreadLocalHelper.unset(); } } } static class MyCompletionCallback implements CompletionCallback { public Packet response = null; public Throwable error = null; private String fiberName = null; public void setFiberName(String name) { this.fiberName = name; } @Override public void onCompletion(@NotNull Packet response) { this.response = response; } @Override public void onCompletion(@NotNull Throwable error) { this.error = error; } } public void testThreadLocalPropagationWithFiberContextSwitch() throws InterruptedException { final Semaphore atSuspend = new Semaphore(0); final Semaphore checkCompleted = new Semaphore(0); final Semaphore atEnd = new Semaphore(0); final TestTube tubeC = new TestTube(); final FilterTestTube tubeB = new FilterTestTube(tubeC) { @Override @NotNull public NextAction processRequest(@NotNull Packet request) { super.processRequest(request); //Before suspend, make sure thread-local is available thanks to MyFiberContextSwitchInterceptor.execute Integer threadLocalInteger = ThreadLocalHelper.get(); assertNotNull(threadLocalInteger); assertEquals(retainMeInteger, threadLocalInteger); return doSuspend(new Runnable() { @Override public void run() { atSuspend.release(); try { checkCompleted.acquire(); } catch (InterruptedException e) { } } }); } @Override @NotNull public NextAction processResponse(@NotNull Packet response) { super.processResponse(response); //After resume, make sure thread-local is available thanks to MyFiberContextSwitchInterceptor.execute Integer threadLocalInteger = ThreadLocalHelper.get(); assertNotNull(threadLocalInteger); assertEquals(retainMeInteger, threadLocalInteger); return doReturnWith(response); } }; tubeB.setName("testThreadLocalPropagationWithFiberContextSwitch.tubeB"); final FilterTestTube tubeA = new FilterTestTube(tubeB); tubeA.setName("testThreadLocalPropagationWithFiberContextSwitch.tubeA"); final Packet request = new Packet(); final MyCompletionCallback callback = new MyCompletionCallback() { @Override public void onCompletion(@NotNull Packet response) { super.onCompletion(response); if (super.fiberName != null) fiberNameToThreadLocalValueMap.remove(super.fiberName); atEnd.release(); } @Override public void onCompletion(@NotNull Throwable error) { super.onCompletion(error); if (super.fiberName != null) fiberNameToThreadLocalValueMap.remove(super.fiberName); atEnd.release(); } }; final Fiber fiber = threadPoolEngine.createFiber(); assertNotNull(fiber); //Put away some value (say, Transaction context gotten from the thread that is going to invoke fiber.start) before fiber.start //Post fiber.start, processing thread comes from a thread pool, no guarantee of same thread in force //This entry will be cleaned up when SimpleCompletionCallback callback method is called at last fiberNameToThreadLocalValueMap.put(fiber.toString(), retainMeInteger); //callback has to remember Fiber name so that it can remove the entry from fiberNameToThreadLocalValueMap having Fiber name as the key callback.setFiberName(fiber.toString()); MyFiberContextSwitchInterceptor interceptor1 = new MyFiberContextSwitchInterceptor("testThreadLocalPropagationWithFiberContextSwitch.interceptor1"); fiber.addInterceptor(interceptor1); fiber.start(tubeA, request, callback); if (!atSuspend.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); assertEquals(0, atEnd.availablePermits()); // ensure test thread really blocked // thread is suspended assertEquals(1, interceptor1.getCallCount()); assertNull(callback.response); assertNull(callback.error); List<TubeCall> calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); TubeCall firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(1, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); checkCompleted.release(); fiber.resume(request); if (!atEnd.tryAcquire(3, TimeUnit.MINUTES)) fail("timeout"); //Two context switches, 1) when fiber.start was done and 2) when fiber was suspended assertEquals(2, interceptor1.getCallCount()); assertEquals(request, callback.response); assertNull(callback.error); calls = tubeA.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); TubeCall secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeB.getCalls(); assertNotNull(calls); assertEquals(2, calls.size()); firstCall = calls.get(0); assertNotNull(firstCall); assertEquals(TubeCallType.REQUEST, firstCall.callType); assertEquals(testContainer, firstCall.container); secondCall = calls.get(1); assertNotNull(secondCall); assertEquals(TubeCallType.RESPONSE, secondCall.callType); assertEquals(testContainer, secondCall.container); calls = tubeC.getCalls(); assertNotNull(calls); assertEquals(0, calls.size()); } }