/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 * 
 * $Id: JAXRSClient.java 70650 2013-03-13 17:25:18Z supol $ 
 */

package com.sun.ts.tests.jaxrs.api.client.invocation;

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import javax.ws.rs.ProcessingException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.InvocationCallback;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import com.sun.ts.tests.jaxrs.common.JAXRSCommonClient;

/*
 * @class.setup_props: webServerHost;
 *                     webServerPort;
 *                     ts_home;
 */
/*
 * The misuse of GenericType<Response> is on purpose to check the behavior 
 * corresponding to javadoc  
 */
public class JAXRSClient extends JAXRSCommonClient {

	private static final long serialVersionUID = -7647322937577043460L;

	/**
	 * Entry point for different-VM execution. It should delegate to method
	 * run(String[], PrintWriter, PrintWriter), and this method should not
	 * contain any test configuration.
	 */
	public static void main(String[] args) {
		new JAXRSClient().run(args);
	}

	/* Run test */

	/*
	 * @testName: invokePlainTest
	 * @assertion_ids: JAXRS:JAVADOC:512; 
	 * @test_Strategy: Synchronously invoke the request and receive a 
	 * 					response back.
	 */
	public void invokePlainTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder();
		Invocation invocation = builder.buildGet();
		Response r = invocation.invoke();
		assertContains(r, Request.GET.name());
	}

	/*
	 * @testName: invokeThrowsExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:512; 
	 * @test_Strategy: throws ProcessingException in case the invocation failed.
	 * 					
	 */
	public void invokeThrowsExceptionTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder(null);
		Invocation invocation = builder.buildGet();
		try {
			invocation.invoke();
		} catch (ProcessingException ie) {
			// everything is fine
			logMsg("ProcessingException has been thrown");
			return;
		}
		throw new Fault("ProcessingException has NOT been thrown");
	}

	/*
	 * @testName: invokeClassTest
	 * @assertion_ids: JAXRS:JAVADOC:514; 
	 * @test_Strategy: Synchronously invoke the request and receive a 
	 * 					response of the specified type back.
	 */
	public void invokeClassTest() throws Fault {
		Entity<String> entity = createEntity("invokeClassTest");
		Invocation.Builder builder = createInvocationBuilder();
		Invocation invocation = builder.buildPost(entity);
		String r = invocation.invoke(String.class);
		assertContains(r, Request.POST.name());
		assertContains(r, "invokeClassTest");
	}

	/*
	 * @testName: invokeClassThrowsExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:514; 
	 * @test_Strategy: throws ProcessingException in case the invocation failed.
	 * 					
	 */
	public void invokeClassThrowsExceptionTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder(null);
		Invocation invocation = builder.buildGet();
		try {
			invocation.invoke(String.class);
		} catch (ProcessingException ie) {
			// everything is fine
			logMsg("ProcessingException has been thrown");
			return;
		}
		throw new Fault("ProcessingException has NOT been thrown");
	}

	/*
	 * @testName: invokeStringThrowsWebApplicationExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:514; 
	 * @test_Strategy: in case the response status code of the response returned 
	 * 					by the server is not SUCCESSFUL 
	 */
	public void invokeStringThrowsWebApplicationExceptionTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Invocation invocation = builder.buildGet();
		try {
			invocation.invoke(String.class);
		} catch (WebApplicationException ie) {
			// everything is fine
			logMsg("WebApplicationException has been thrown");
			return;
		}
		throw new Fault("WebApplicationException has NOT been thrown");
	}

	/*
	 * @testName: invokeResponseThrowsNoWebApplicationExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:514; 
	 * @test_Strategy: in case the response status code of the response returned 
	 * 					by the server is not SUCCESSFUL and the response type is 
	 * 					not Response
	 */
	public void invokeResponseThrowsNoWebApplicationExceptionTest()
			throws Fault {
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Invocation invocation = builder.buildGet();
		Response r = invocation.invoke(Response.class);
		assertStatus(r, Status.NOT_ACCEPTABLE);
		logMsg("Returned unexpected response with status code", r.getStatus());
	}

	/*
	 * @testName: invokeGenericTypeTest
	 * @assertion_ids: JAXRS:JAVADOC:517; 
	 * @test_Strategy: Synchronously invoke the request and receive a 
	 * 					response of the specified generic type back.
	 */
	public void invokeGenericTypeTest() throws Fault {
		GenericType<String> entity = new GenericType<String>() {
		};
		Invocation.Builder builder = createInvocationBuilder();
		Invocation invocation = builder.buildGet();
		String r = invocation.invoke(entity);
		assertContains(r, Request.GET.name());
	}

	/*
	 * @testName: invokeGenericTypeThrowsExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:517; 
	 * @test_Strategy: throws ProcessingException in case the invocation failed.
	 * 					
	 */
	public void invokeGenericTypeThrowsExceptionTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder(null);
		Invocation invocation = builder.buildGet();
		try {
			invocation.invoke(new GenericType<String>() {
			});
		} catch (ProcessingException ie) {
			// everything is fine
			logMsg("ProcessingException has been thrown");
			return;
		}
		throw new Fault("ProcessingException has NOT been thrown");
	}

	/*
	 * @testName: invokeGenericTypeStringThrowsWebApplicationExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:517; 
	 * @test_Strategy: in case the response status code of the response returned 
	 * 					by the server is not SUCCESSFUL
	 */
	public void invokeGenericTypeStringThrowsWebApplicationExceptionTest()
			throws Fault {
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Invocation invocation = builder.buildGet();
		try {
			invocation.invoke(new GenericType<String>() {
			});
		} catch (WebApplicationException e) {
			// everything is fine
			logMsg("WebApplicationException has been thrown");
			return;
		}
		throw new Fault("WebApplicationException has NOT been thrown");
	}

	/*
	 * @testName: invokeAnnonymousGenericTypeResponseThrowsNoWebApplicationExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:517; 
	 * @test_Strategy: in case the response status code of the response returned 
	 * 					by the server is not SUCCESSFUL and response type is not 
	 * 					Response
	 */
	public void invokeAnnonymousGenericTypeResponseThrowsNoWebApplicationExceptionTest()
			throws Fault {
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Invocation invocation = builder.buildGet();
		Response response = invocation.invoke(new GenericType<Response>() {
		});
		assertStatus(response, Status.NOT_ACCEPTABLE);
		logMsg("Response return code is", response.getStatus(), "as expected");
	}

	/*
	 * @testName: invokeExtendedGenericTypeResponseThrowsNoWebApplicationExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:517; 
	 * @test_Strategy: in case the response status code of the response returned 
	 * 					by the server is not SUCCESSFUL and response type is not 
	 * 					Response
	 */
	public void invokeExtendedGenericTypeResponseThrowsNoWebApplicationExceptionTest()
			throws Fault {
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Invocation invocation = builder.buildGet();
		Response response = invocation.invoke(new GenericTypeResponse());
		assertStatus(response, Status.NOT_ACCEPTABLE);
		logMsg("Response return code is", response.getStatus(), "as expected");
	}

	/*
	 * @testName: submitPlainTest
	 * @assertion_ids: JAXRS:JAVADOC:520; 
	 * @test_Strategy: Submit the request for an asynchronous invocation and 
	 * 					receive a future response back.
	 */
	public void submitPlainTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder();
		Invocation invocation = builder.buildGet();
		Future<Response> future = invocation.submit();
		Response response;
		try {
			response = future.get();
			assertContains(response, Request.GET.name());
		} catch (Exception e) {
			throw new Fault(e);
		}
	}

	/*
	 * @testName: submitPlainThrowsProcessingExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:520; 
	 * @test_Strategy: Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw an ExecutionException that 
	 * 					wraps a ProcessingException thrown in case of an invocation 
	 * 					processing failure
	 */
	public void submitPlainThrowsProcessingExceptionTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder(null);
		Invocation invocation = builder.buildGet();		
		Future<Response> future = invocation.submit();
		assertExceptionWithProcessingExceptionIsThrownAndLog(future);
	}

	/*
	 * @testName: submitClassTest
	 * @assertion_ids: JAXRS:JAVADOC:521; 
	 * @test_Strategy: Submit the request for an asynchronous invocation and 
	 * 					receive a future response of the specified type back.
	 */
	public void submitClassTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder();
		Entity<String> entity = createEntity("submitClassTest");
		Invocation invocation = builder.buildPost(entity);
		Future<String> future = invocation.submit(String.class);
		String response;
		try {
			response = future.get();
			assertContains(response, Request.POST.name());
			assertContains(response, "submitClassTest");
		} catch (Exception e) {
			throw new Fault(e);
		}
	}

	/*
	 * @testName: submitStringThrowsProcessingExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:521; 
	 * @test_Strategy: Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw an ExecutionException that 
	 * 					wraps a ProcessingException thrown in case of an invocation 
	 * 					processing failure
	 */
	public void submitStringThrowsProcessingExceptionTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder(null);
		Invocation invocation = builder.buildGet();
		Future<String> future = invocation.submit(String.class);
		assertExceptionWithProcessingExceptionIsThrownAndLog(future);
	}

	/*
	 * @testName: submitStringThrowsWebApplicationExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:521; 
	 * @test_Strategy:Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw a WebApplicationException or 
	 * 					one of its subclasses thrown in case the received 
	 * 					response status code is not successful and the specified 
	 * 					response type is not Response.
	 */
	public void submitStringThrowsWebApplicationExceptionTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Invocation invocation = builder.buildGet();
		Future<String> future = invocation.submit(String.class);
		assertExceptionWithWebApplicationExceptionIsThrownAndLog(future);
	}

	/*
	 * @testName: submitResponseThrowsNoWebApplicationExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:521; 
	 * @test_Strategy: Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw a WebApplicationException or 
	 * 					one of its subclasses thrown in case the received 
	 * 					response status code is not successful and the specified 
	 * 					response type is not Response.
	 */
	public void submitResponseThrowsNoWebApplicationExceptionTest()
			throws Fault {
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Invocation invocation = builder.buildGet();
		Future<Response> future = invocation.submit(Response.class);
		assertStatus(future, Status.NOT_ACCEPTABLE);
	}

	/*
	 * @testName: submitGenericTypeTest
	 * @assertion_ids: JAXRS:JAVADOC:522; 
	 * @test_Strategy: Submit the request for an asynchronous invocation and 
	 * 					receive a future response of the specified 
	 * 					generic type back.
	 */
	public void submitGenericTypeTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder();
		Entity<String> entity = createEntity("submitGenericTypeTest");
		Invocation invocation = builder.buildPost(entity);
		Future<String> future = invocation.submit(new GenericType<String>() {
		});
		String response;
		try {
			response = future.get();
			assertContains(response, Request.POST.name());
			assertContains(response, "submitGenericTypeTest");
		} catch (Exception e) {
			throw new Fault(e);
		}
	}

	/*
	 * @testName: submitGenericTypeThrowsProcessingExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:522; 
	 * @test_Strategy: Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw an ExecutionException that 
	 * 					wraps a ProcessingException thrown in case of an invocation 
	 * 					processing failure
	 */
	public void submitGenericTypeThrowsProcessingExceptionTest() throws Fault {
		Invocation.Builder builder = createInvocationBuilder(null);
		Entity<String> entity = createEntity("submitGenericTypeTest");
		Invocation invocation = builder.buildPost(entity);
		Future<String> future = invocation.submit(new GenericType<String>() {
		});
		assertExceptionWithProcessingExceptionIsThrownAndLog(future);
	}

	/*
	 * @testName: submitGenericTypeStringThrowsWebApplicationExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:522; 
	 * @test_Strategy: Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw a WebApplicationException or 
	 * 					one of its subclasses thrown in case the received 
	 * 					response status code is not successful and the specified 
	 * 					response type is not Response.
	 */
	public void submitGenericTypeStringThrowsWebApplicationExceptionTest()
			throws Fault {
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Entity<String> entity = createEntity("submitGenericTypeTest");
		Invocation invocation = builder.buildPost(entity);
		Future<String> future = invocation
				.submit(new GenericType<String>() {
				});
		assertExceptionWithWebApplicationExceptionIsThrownAndLog(future);
	}

	/*
	 * @testName: submitAnnonymousGenericTypeResponseThrowsNoWebApplicationExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:522; 
	 * @test_Strategy: Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw a WebApplicationException or 
	 * 					one of its subclasses thrown in case the received 
	 * 					response status code is not successful and the specified 
	 * 					response type is not Response.
	 */
	public void submitAnnonymousGenericTypeResponseThrowsNoWebApplicationExceptionTest()
			throws Fault {
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Entity<String> entity = createEntity("submitGenericTypeTest");
		Invocation invocation = builder.buildPost(entity);
		Future<Response> future = invocation
				.submit(new GenericType<Response>() {
				});
		assertStatus(future, Status.NOT_ACCEPTABLE);
	}

	/*
	 * @testName: submitExtendedGenericTypeResponseThrowsNoWebApplicationExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:522; 
	 * @test_Strategy: Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw a WebApplicationException or 
	 * 					one of its subclasses thrown in case the received 
	 * 					response status code is not successful and the specified 
	 * 					response type is not Response.
	 */
	public void submitExtendedGenericTypeResponseThrowsNoWebApplicationExceptionTest()
			throws Fault {
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Entity<String> entity = createEntity("submitGenericTypeTest");
		Invocation invocation = builder.buildPost(entity);
		Future<Response> future = invocation.submit(new GenericTypeResponse());
		assertStatus(future, Status.NOT_ACCEPTABLE);
	}

	/*
	 * @testName: submitInvocationCallbackTest
	 * @assertion_ids: JAXRS:JAVADOC:523; 
	 * @test_Strategy: Submit the request for an asynchronous invocation and 
	 * 					register an InvocationCallback to process the future 
	 * 					result of the invocation.
	 */
	public void submitInvocationCallbackTest() throws Fault {
		InvocationCallback<String> callback = createCallback(String.class);
		Invocation.Builder builder = createInvocationBuilder();
		Invocation invocation = builder.buildGet();
		Future<String> future = invocation.submit(callback);
		String response;
		try {
			response = future.get();
			assertContains(response, Request.GET.name());
		} catch (Exception e) {
			throw new Fault(e);
		}
	}

	/*
	 * @testName: submitInvocationCallbackThrowsProcessingExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:523; 
	 * @test_Strategy: Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw an ExecutionException that 
	 * 					wraps a ProcessingException thrown in case of an invocation 
	 * 					processing failure
	 */
	public void submitInvocationCallbackThrowsProcessingExceptionTest()
			throws Fault {
		InvocationCallback<String> callback = createCallback(String.class);
		Invocation.Builder builder = createInvocationBuilder(null);
		Invocation invocation = builder.buildGet();
		Future<String> future = invocation.submit(callback);
		assertExceptionWithProcessingExceptionIsThrownAndLog(future);
	}

	/*
	 * @testName: submitInvocationCallbackStringThrowsWebAppExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:523; 
	 * @test_Strategy: Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw a WebApplicationException or 
	 * 					one of its subclasses thrown in case the received 
	 * 					response status code is not successful and the specified 
	 * 					response type is not Response.
	 */
	public void submitInvocationCallbackStringThrowsWebAppExceptionTest()
			throws Fault {
		InvocationCallback<String> callback = createCallback(String.class);
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Invocation invocation = builder.buildGet();
		Future<String> future = invocation.submit(callback);
		assertExceptionWithWebApplicationExceptionIsThrownAndLog(future);
	}

	/*
	 * @testName: submitInvocationCallbackResponseThrowsNoWebAppExceptionTest
	 * @assertion_ids: JAXRS:JAVADOC:523; 
	 * @test_Strategy: Note that calling the Future.get() method on the returned 
	 * 					Future instance may throw a WebApplicationException or 
	 * 					one of its subclasses thrown in case the received 
	 * 					response status code is not successful and the specified 
	 * 					response type is not Response.
	 */
	public void submitInvocationCallbackResponseThrowsNoWebAppExceptionTest()
			throws Fault {
		InvocationCallback<Response> callback = createCallback(Response.class);
		Invocation.Builder builder = createInvocationBuilder(createBadResponseFilter());
		Invocation invocation = builder.buildGet();
		Future<Response> future = invocation.submit(callback);
		Response r = null;
		try {
			r = future.get();
		} catch (Exception e) {
			throw new Fault("Unexpected exception caught", e);
		}
		assertStatus(r, Status.NOT_ACCEPTABLE);
		logMsg("Found expected status code", r.getStatus());
	}

	// ///////////////////////////////////////////////////////////////////////

	/**
	 * Simulates server side
	 * 
	 * @return Response containing request method and entity
	 */
	protected ClientRequestFilter createRequestFilter() {
		ClientRequestFilter filter = new ClientRequestFilter() {
			@Override
			public void filter(ClientRequestContext ctx) throws IOException {
				StringBuilder sb = new StringBuilder();
				sb.append(ctx.getMethod()).append(";");
				if (ctx.hasEntity())
					sb.append(ctx.getEntity()).append(";");
				Response r = Response.ok(sb.toString()).build();
				ctx.abortWith(r);
			}
		};
		return filter;
	}

	protected Invocation.Builder createInvocationBuilder(
			ClientRequestFilter filter) {
		Client client = ClientBuilder.newClient();
		if (filter != null)
			client.register(filter);
		WebTarget target = client.target("http://cts.tck:888");
		Invocation.Builder builder = target.request();
		return builder;
	}

	protected Invocation.Builder createInvocationBuilder() {
		return createInvocationBuilder(createRequestFilter());
	}

	protected ClientRequestFilter createBadResponseFilter() {
		ClientRequestFilter filter = new ClientRequestFilter() {
			@Override
			public void filter(ClientRequestContext arg0) throws IOException {
				Response r = Response.status(Status.NOT_ACCEPTABLE).build();
				arg0.abortWith(r);
			}
		};
		return filter;
	}

	@SuppressWarnings("unchecked")
	protected <T> InvocationCallback<T> createCallback(Class<T> clazz) {
		InvocationCallback<T> callback = null;
		if (clazz == String.class)
			callback = (InvocationCallback<T>) new InvocationCallback<String>() {
				@Override
				public void completed(String arg0) {
				}

				@Override
				public void failed(Throwable throwable) {
				}
			};
		else if (clazz == Response.class)
			callback = (InvocationCallback<T>) new InvocationCallback<Response>() {
				@Override
				public void completed(Response arg0) {
				}

				@Override
				public void failed(Throwable throwable) {
				}
			};
		return callback;
	}

	/*
	 * readEntity vs. getEntity (from response) is set here 
	 */
	protected String getEntity(Response r) {
		String entity = r.readEntity(String.class);
		return entity.toLowerCase();
	}

	/**
	 * Create entity instance
	 */
	protected <T> Entity<T> createEntity(T t) {
		return Entity.entity(t, MediaType.WILDCARD_TYPE);
	}

	protected void assertStatus(Response response, Status status) throws Fault {
		assertTrue(response.getStatus() == status.getStatusCode(),
				"Returned unexpected", response.getStatus(), "status code");
	}

	protected void assertContains(Response response, String what) throws Fault {
		assertStatus(response, Status.OK);
		String entity = getEntity(response);
		assertContains(entity, what);
	}

	protected void assertContains(String responseEntity, String what)
			throws Fault {
		assertTrue(responseEntity.toLowerCase().contains(what.toLowerCase()),
				responseEntity, "does not contain expected", what);
		logMsg("Found expected", what);
	}

	// for submit ------------------------------------------------------------

	protected void assertStatus(Future<Response> future, Status status)
			throws Fault {
		Response response;
		try {
			response = future.get();
		} catch (Exception e) {
			throw new Fault(e);
		}
		assertStatus(response, status);
		logMsg("Response cotains expected status", status);
	}

	protected void //
	assertExceptionWithWebApplicationExceptionIsThrownAndLog(Future<?> future)
			throws Fault {
		try {
			future.get();
			throw new Fault("ExecutionException has not been thrown");
		} catch (ExecutionException e) {
			assertWebApplicationExceptionIsCauseAndLog(e);
		} catch (InterruptedException e) {
			throw new Fault("Unexpected exception thrown", e);
		}
	}

	protected void assertExceptionWithProcessingExceptionIsThrownAndLog(
			Future<?> future) throws Fault {
		try {
			future.get();
			throw new Fault("ExecutionException has not been thrown");
		} catch (ExecutionException e) {
			assertProcessingExceptionIsCauseAndLog(e);
		} catch (InterruptedException e) {
			throw new Fault("Unexpected exception thrown", e);
		}
	}

	protected void //
	assertProcessingExceptionIsCauseAndLog(ExecutionException e) throws Fault {
		logMsg("ExecutionException has been thrown as expected", e);
		assertTrue(hasWrapped(e, ProcessingException.class),
				"ExecutionException wrapped", e.getCause(),
				"rather then ProcessingException");
		logMsg("ExecutionException.getCause is ProcessingException as expected");
	}

	protected void //
	assertWebApplicationExceptionIsCauseAndLog(ExecutionException e)
			throws Fault {
		logMsg("ExecutionException has been thrown as expected", e);
		assertTrue(hasWrapped(e, WebApplicationException.class),
				"ExecutionException wrapped", e.getCause(),
				"rather then WebApplicationException");
		logMsg("ExecutionException.getCause is WebApplicationException as expected");
	}

	static boolean //
	hasWrapped(Throwable parent, Class<? extends Throwable> wrapped) {
		while (parent.getCause() != null) {
			if (wrapped.isInstance(parent.getCause()))
				return true;
			parent = parent.getCause();
		}
		return false;
	}
}
