/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.axis2.jaxws.sample;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.axis2.jaxws.TestLogger;
import org.apache.axis2.jaxws.framework.AbstractTestCase;
import org.apache.axis2.jaxws.sample.parallelasync.common.CallbackHandler;
import org.apache.axis2.jaxws.sample.parallelasync.server.AsyncPort;
import org.apache.axis2.jaxws.sample.parallelasync.server.AsyncService;
import org.test.parallelasync.CustomAsyncResponse;
import org.test.parallelasync.SleepResponse;
import org.test.parallelasync.WakeUpResponse;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Response;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* Tests using Asynchronous Callback and an Executor in a multithreaded environment.
*
* Note there are other basic JAX-WS async tests:
* @see ParallelAsyncTests
*
*/
public class AsyncExecutorTests extends AbstractTestCase {
private static final String DOCLITWR_ASYNC_ENDPOINT =
"http://localhost:6060/axis2/services/AsyncService.DocLitWrappedPortImplPort";
// used for logging
private String myClassName = "AsyncExecutorTests";
public static Test suite() {
return getTestSetup(new TestSuite(AsyncExecutorTests.class));
}
/**
* @testStrategy Check that the web service is up and running
* before running any other tests
*/
public void testService_isAlive() throws Exception {
final String MESSAGE = "testServiceAlive";
String title = myClassName + " : " + getName() + " : ";
AsyncPort port = getPort((Executor)null);
String req1base = "sleepAsync";
String req2base = "remappedAsync";
String request1 = null;
String request2 = null;
for (int i = 0; i < 10; i++) {
request1 = req1base + "_" + i;
request2 = req2base + "_" + i;
TestLogger.logger.debug(title + "iteration [" + i + "] using request1 [" + request1 +
"] request2 [" + request2 + "]");
// submit request #1 to the server-side web service that
// the web service will keep until we ask for it
Response<SleepResponse> resp1 = port.sleepAsync(request1);
// submit request #2 to the server that essentially processes
// without delay
Response<CustomAsyncResponse> resp2 = port.remappedAsync(request2);
// wait until the response for request #2 is done
waitBlocking(resp2);
// check the waiting request #1
String asleep = port.isAsleep(request1);
//System.out.println(title+"iteration ["+i+"] port.isAsleep(request1 ["+request1+"]) = ["+asleep+"]");
// wakeup the waiting request #1
String wake = port.wakeUp(request1);
//System.out.println(title+"iteration ["+i+"] port.wakeUp(request1 ["+request1+"]) = ["+wake+"]");
// wait until the response for request #1 is done
waitBlocking(resp1);
// get the responses
String req1_result = null;
String req2_result = null;
try {
req1_result = resp1.get().getMessage();
req2_result = resp2.get().getResponse();
} catch (Exception e) {
TestLogger.logger.debug(
title + "iteration [" + i + "] using request1 [" + request1 +
"] request2 [" + request2 + "] : got exception [" +
e.getClass().getName() + "] [" + e.getMessage() + "] ");
e.printStackTrace();
fail(e.toString());
}
// check status on request #1
assertEquals("sleepAsync did not sleep as expected", request1, asleep);
assertEquals("sleepAsync did not return expected response ", request1, req1_result);
// check status on request #2
assertEquals("remappedAsync did not return expected response", request2, req2_result);
// Calling get() again should return the same object as the first call to get()
assertEquals("sleepAsync did not return expected response ", request1, resp1.get().getMessage());
assertEquals("remappedAsync did not return expected response", request2, resp2.get().getResponse());
}
// check the callback operation
CallbackHandler<SleepResponse> sleepCallbackHandler = new CallbackHandler<SleepResponse>();
request1 = req1base + "_with_Callback";
//System.out.println(title+" port.sleepAsync("+request1+", callbackHander) being submitted....");
Future<?> sr = port.sleepAsync(request1, sleepCallbackHandler);
// wait a bit for the server to process the request ...
Thread.sleep(500);
// check the waiting request
String asleepWithCallback = port.isAsleep(request1);
//System.out.println(title+" port.isAsleep("+request1+") = ["+asleepWithCallback+"]");
// wakeup the waiting request
String wake = port.wakeUp(request1);
//System.out.println(title+" port.wakeUp("+request1+") = ["+wake+"]");
// wait a bit..
Thread.sleep(500);
// get the response
String req_cb_result = null;
try {
SleepResponse sleepResp = sleepCallbackHandler.get();
if (sleepResp != null) {
req_cb_result = sleepResp.getMessage();
TestLogger.logger.debug(
title + " request [" + request1 + "] : result [" + req_cb_result + "] ");
}
} catch (Exception ex) {
TestLogger.logger.debug(title + " request [" + request1 + "] : got exception [" +
ex.getClass().getName() + "] [" + ex.getMessage() + "] ");
ex.printStackTrace();
fail(ex.toString());
}
// check status on request
assertEquals("sleepAsync with callback did not sleep as expected", request1, req_cb_result);
}
/**
* @testStrategy Test that a client on one thread can issue an AsyncCallback call and exit
* immediately and not affect the request being sent to the service and the response
* being received by an executor
*/
public void testMultithreadedCallback() {
TestThreadMonitor monitor = startupTestThreads();
// Make sure neither thread encountered an error and that both of them were released
// and run to completion
assertNull("Receiver thread encountered an exception", monitor.receiverException);
assertNull("Sender thread encountered an exception", monitor.senderException);
assertTrue("Sender thread not run to completion", monitor.senderThreadExitTime != 0);
assertTrue("Receiver thread not run to completion", monitor.receiverThreadRequestReceivedTime != 0);
// Make sure the sender thread completed before the receiver thread found the
// request had been received by the service endpoint
assertTrue("Sender thread did not exit before Receiver thread verified request",
monitor.senderThreadExitTime < monitor.receiverThreadRequestReceivedTime);
}
private static int THREAD_TIMEOUT = 900000;
private TestThreadMonitor startupTestThreads() {
TestThreadMonitor monitor = new TestThreadMonitor();
Thread sendClientRequestThread = new Thread(new SendClientRequest(monitor));
Thread receiveClientResponseThread = new Thread(new ReceiveClientResponse(monitor));
try {
sendClientRequestThread.start();
receiveClientResponseThread.start();
sendClientRequestThread.join(THREAD_TIMEOUT);
receiveClientResponseThread.join(THREAD_TIMEOUT);
} catch (Exception e) {
fail("Threads didn't get started: " + e.toString());
}
return monitor;
}
private AsyncService getService(Executor ex) {
AsyncService service = new AsyncService();
if (ex!= null)
service.setExecutor(ex);
if (service.getExecutor() == null) {
TestLogger.logger.debug(myClassName + " : getService() : executor is null");
}
else {
TestLogger.logger.debug(myClassName + " : getService() : executor is available ");
}
return service;
}
private AsyncPort getPort(AsyncService service) {
AsyncPort port = service.getAsyncPort();
assertNotNull("Port is null", port);
Map<String, Object> rc = ((BindingProvider) port).getRequestContext();
rc.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, DOCLITWR_ASYNC_ENDPOINT);
return port;
}
/**
* Auxiliary method used for obtaining a proxy pre-configured with a
* specific Executor
*/
private AsyncPort getPort(Executor ex) {
AsyncService service = getService(ex);
AsyncPort port = service.getAsyncPort();
assertNotNull("Port is null", port);
Map<String, Object> rc = ((BindingProvider) port).getRequestContext();
rc.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
DOCLITWR_ASYNC_ENDPOINT);
return port;
}
private void waitBlocking(Future<?> monitor){
while (!monitor.isDone()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
/**
* Object used to synchronize and communicate between the thread that sends the request and
* the thread that processes the response.
*/
class TestThreadMonitor {
String request;
CallbackHandler<SleepResponse> callbackHandler;
Future<?> futureResponse;
boolean requestSent = false;
Exception senderException = null;
Exception receiverException = null;
long senderThreadExitTime = 0;
long receiverThreadRequestReceivedTime = 0;
}
/**
* Thread to send a JAXWS AsyncCallback request and then exit immediately. Another thread will
* verify that the request was received by the service and process the response. Note that
* the sleep operation invoked by this thread will add the String it receives to a list and
* then suspend processing until a wakeup operation is issued on the service. That
* wakeup will be issued by a different thread in this test.
* @see {@link ReceiveClientResponse}
*/
class SendClientRequest implements Runnable {
private TestThreadMonitor monitor;
SendClientRequest(TestThreadMonitor monitor) {
this.monitor = monitor;
}
public void run() {
try {
sendClientRequest();
monitor.senderThreadExitTime = System.currentTimeMillis();
TestLogger.logger.debug("Send Client Thread sent request and now returning");
} catch (Exception e) {
monitor.senderException = e;
TestLogger.logger.error("Send thread caught exception", e);
} catch (AssertionError e) {
monitor.senderException = new Exception(e);
TestLogger.logger.error("Send thread assertion failure", e);
}
}
public void sendClientRequest() throws Exception {
String title = myClassName + " : SendClientRequest : ";
AsyncService service = new AsyncService();
AsyncPort port = getPort(service);
// Setup an executor for response processing to run on a different thread
ExecutorService ex = Executors.newSingleThreadExecutor();
service.setExecutor(ex);
// submit a request to the server that will wait until we ask for it
// Note that this thread is not going to ask for it; another thread will do that.
CallbackHandler<SleepResponse> sleepCallbackHandler1 = new CallbackHandler<SleepResponse>();
monitor.callbackHandler = sleepCallbackHandler1;
monitor.request = "sleepAsync_sendClientRequest";
String request1 = monitor.request;
TestLogger.logger.debug(title + " port.sleepAsync(" + request1 +
", callbackHander1) #1 being submitted....");
Future<?> sr1 = port.sleepAsync(request1, sleepCallbackHandler1);
monitor.futureResponse = sr1;
synchronized(monitor) {
monitor.requestSent = true;
monitor.notifyAll();
}
TestLogger.logger.debug(
title + " port.sleepAsync(" + request1 + ", callbackHander1) #1 .....submitted.");
// Return from this thread immediately
}
}
static void withRetry(Runnable runnable) throws InterruptedException {
int retries = 0;
while (true) {
try {
runnable.run();
return;
} catch (AssertionError ex) {
if (retries++ > 60) {
throw ex;
} else {
Thread.sleep(500);
}
}
}
}
/**
* Thread that verifies the request sent by a different thread was recieved by the service and
* then verify the response. Another thread will have previously sent the request. Note that
* the sleep operation invoked by the other thread will have added the String it receives to
* a list and then suspend processing until a wakeup operation is issued by this thread.
* @see {@link SendClientRequest}
*/
class ReceiveClientResponse implements Runnable {
private TestThreadMonitor monitor;
ReceiveClientResponse(TestThreadMonitor monitor) {
this.monitor = monitor;
}
public void run() {
try {
// Wait until the sender thread has sent the request
synchronized(monitor) {
while (!monitor.requestSent) {
monitor.wait();
}
}
TestLogger.logger.debug("Receiver thread released by sender thread to start working");
receiveResponse();
TestLogger.logger.debug("Receiver thread done and about to exit");
} catch (Exception e) {
monitor.receiverException = e;
TestLogger.logger.error("Receive thread caught exception", e);
} catch (AssertionError e) {
monitor.receiverException = new Exception(e);
TestLogger.logger.error("Receive thread assertion failure", e);
}
}
private void receiveResponse() throws Exception {
final String title = myClassName + " : ReceiveClientResponse : ";
final String request1 = monitor.request;
AsyncService service = new AsyncService();
final AsyncPort port = getPort(service);
withRetry(new Runnable() {
public void run() {
// check the waiting request
TestLogger.logger.debug(title + " port.isAsleep(" + request1 + ") #1 being submitted....");
String asleepWithCallback1 = port.isAsleep(request1);
TestLogger.logger.debug(
title + " port.isAsleep(" + request1 + ") #1 = [" + asleepWithCallback1 + "]");
assertEquals(request1, asleepWithCallback1);
}
});
// wakeup the waiting request
TestLogger.logger.debug(title + " port.wakeUp(request1) #1 being submitted....");
String wake1 = port.wakeUp(request1);
monitor.receiverThreadRequestReceivedTime = System.currentTimeMillis();
assertEquals(request1, wake1);
TestLogger.logger.debug(title + " port.wakeUp(" + request1 + ") #1 = [" + wake1 + "]");
final Future<?> sr1 = monitor.futureResponse;
CallbackHandler<SleepResponse> sleepCallbackHandler1 = monitor.callbackHandler;
withRetry(new Runnable() {
public void run() {
// check the Future
assertTrue("Response is not done!", sr1.isDone());
}
});
// try to get the response
try {
SleepResponse sleepResp1 = sleepCallbackHandler1.get();
if (sleepResp1 != null) {
TestLogger.logger.debug(title + " request [" + request1 +
"] #1: sleepResponse [NOT NULL] from callback handler");
String result1 = sleepResp1.getMessage();
assertEquals(request1, result1);
TestLogger.logger.debug(
title + " request [" + request1 + "] #1: result [" + result1 + "] ");
}
else {
TestLogger.logger.debug(title + " request [" + request1 +
"] #1: sleepResponse [NULL] from callback handler");
// see what the Future says
TestLogger.logger.debug(
title + " request [" + request1 + "] #1: ....check Future response...");
Object futureResult = sr1.get();
TestLogger.logger.debug(
title + " request [" + request1 + "] #1: ....Future response [" +
futureResult + "]...");
}
} catch (Exception exc) {
TestLogger.logger.debug(title + " request [" + request1 + "] : got exception [" +
exc.getClass().getName() + "] [" + exc.getMessage() + "] ");
monitor.receiverException = exc;
}
}
}
}