package org.jboss.resteasy.test.asynch; import java.io.ByteArrayInputStream; import java.io.IOException; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.Future; import javax.ws.rs.client.AsyncInvoker; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.junit.Arquillian; import org.jboss.resteasy.category.ExpectedFailing; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.jboss.resteasy.test.asynch.resource.JaxrsAsyncServletApp; import org.jboss.resteasy.test.asynch.resource.JaxrsAsyncServletAsyncResponseBlockingQueue; import org.jboss.resteasy.test.asynch.resource.JaxrsAsyncServletJaxrsResource; import org.jboss.resteasy.test.asynch.resource.JaxrsAsyncServletPrintingErrorHandler; import org.jboss.resteasy.test.asynch.resource.JaxrsAsyncServletResource; import org.jboss.resteasy.test.asynch.resource.JaxrsAsyncServletServiceUnavailableExceptionMapper; import org.jboss.resteasy.test.asynch.resource.JaxrsAsyncServletTimeoutHandler; import org.jboss.resteasy.test.asynch.resource.JaxrsAsyncServletXmlData; import org.jboss.resteasy.util.HttpResponseCodes; import org.jboss.resteasy.utils.PortProviderUtil; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; /** * @tpSubChapter Asynchronous RESTEasy * @tpChapter Integration tests * @tpTestCaseDetails Test for asyncHttpServlet module. Check stage URL * property. * @tpSince RESTEasy 3.0.16 */ @RunWith(Arquillian.class) @RunAsClient public class ComprehensiveJaxrsTest { protected static final Logger logger = LogManager.getLogger(ComprehensiveJaxrsTest.class.getName()); @Deployment public static Archive<?> createTestArchive() { WebArchive war = ShrinkWrap.create(WebArchive.class, AsyncServletTest.class.getSimpleName() + ".war"); war.addClasses(JaxrsAsyncServletXmlData.class, JaxrsAsyncServletAsyncResponseBlockingQueue.class, JaxrsAsyncServletJaxrsResource.class, JaxrsAsyncServletApp.class, JaxrsAsyncServletPrintingErrorHandler.class, JaxrsAsyncServletTimeoutHandler.class, JaxrsAsyncServletResource.class, JaxrsAsyncServletServiceUnavailableExceptionMapper.class, JaxrsAsyncServletXmlData.class); war.addAsWebInfResource(AsyncPostProcessingTest.class.getPackage(), "JaxrsAsyncServletWeb.xml", "web.xml"); return war; } private String generateURL(String path) { return PortProviderUtil.generateURL(path, AsyncServletTest.class.getSimpleName()); } protected Client client; @Before public void beforeTest() { client = new ResteasyClientBuilder().connectionPoolSize(10).build(); } @After public void afterTest() { client.close(); } protected static String objectsToString(Object... objects) { StringBuilder sb = new StringBuilder(); for (Object o : objects) { sb.append(o).append(" "); } return sb.toString().trim(); } public static void logMsg(Object... msg) { logger.info(objectsToString(msg)); } protected static void checkEquals(Object expected, Object actual, Object... msg) { Assert.assertEquals(objectsToString(msg), expected, actual); } public static final TimeZone findTimeZoneInDate(String date) { StringBuilder sb = new StringBuilder(); StringBuilder dateBuilder = new StringBuilder(date.trim()).reverse(); int index = 0; char c; while ((c = dateBuilder.charAt(index++)) != ' ') { sb.append(c); } TimeZone timezone = TimeZone.getTimeZone(sb.reverse().toString()); return timezone; } public static final DateFormat createDateFormat(TimeZone timezone) { SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); sdf.setTimeZone(timezone); return sdf; } private void suspendResumeTestInternal() throws Exception { invokeClear(); String expectedResponse = "Expected response"; Future<Response> suspend = invokeRequest("suspend"); Future<Response> resume = invokeRequest("resume?stage=0", expectedResponse); checkString(resume, JaxrsAsyncServletResource.TRUE); checkString(suspend, expectedResponse); } private void cancelVoidTestInternal() throws Exception { invokeClear(); Future<Response> suspend = invokeRequest("suspend"); Future<Response> cancel = invokeRequest("cancelvoid?stage=0"); checktStatus(getResponse(suspend), Status.SERVICE_UNAVAILABLE); checkString(cancel, JaxrsAsyncServletResource.TRUE); } private void setTimeoutTestInternal() throws Exception { invokeClear(); logMsg("here 1"); Future<Response> suspend = invokeRequest("suspend"); logMsg("here 2"); Future<Response> setTimeout = invokeRequest("settimeout?stage=0", 200); logMsg("here 3"); checktStatus(getResponse(setTimeout), Status.NO_CONTENT); logMsg("here 4"); // WebApplication exception with 503 is caught by // ServiceUnavailableExceptionMapper Response fromMapper = getResponse(suspend); logMsg("here 5"); checktStatus(fromMapper, Status.REQUEST_TIMEOUT); String entity = fromMapper.readEntity(String.class); checkContains(entity, 503); logMsg("Found expected status 503"); } private void cancelDateTestInternal() throws Exception { long milis = (System.currentTimeMillis() / 1000) * 1000 + 20000; invokeClear(); Future<Response> suspend = invokeRequest("suspend"); Future<Response> cancel = invokeRequest("canceldate?stage=0", milis); Response response = getResponse(suspend); checktStatus(response, Status.SERVICE_UNAVAILABLE); checkString(cancel, JaxrsAsyncServletResource.TRUE); String header = response.getHeaderString(HttpHeaders.RETRY_AFTER); TimeZone timezone = findTimeZoneInDate(header); Date retry = null; try { retry = createDateFormat(timezone).parse(header); } catch (ParseException e) { throw new Exception(e); } checkEquals(new Date(milis), retry, "Unexpected", HttpHeaders.RETRY_AFTER, "header value received", retry.getTime(), "expected", milis); logMsg("Found expected", HttpHeaders.RETRY_AFTER, "=", header); } private void cancelIntTestInternal() throws Exception { String seconds = "20"; invokeClear(); Future<Response> suspend = invokeRequest("suspend"); Future<Response> cancel = invokeRequest("cancelretry?stage=0", seconds); Response response = getResponse(suspend); checktStatus(response, Status.SERVICE_UNAVAILABLE); checkString(cancel, JaxrsAsyncServletResource.TRUE); String retry = response.getHeaderString(HttpHeaders.RETRY_AFTER); checkEquals(seconds, retry, "Unexpected", HttpHeaders.RETRY_AFTER, "header value received", retry, "expected", seconds); logMsg("Found expected", HttpHeaders.RETRY_AFTER, "=", retry); } /** * @tpTestDetails Complex test. Check stage=0 and stage=1 values. * @tpSince RESTEasy 3.0.16 */ @Test public void cancelVoidTest() throws Exception { cancelVoidTestInternal(); } @Test public void cancelVoidOnResumedTest() throws Exception { suspendResumeTestInternal(); Future<Response> cancel = invokeRequest("cancelvoid?stage=1"); checkString(cancel, JaxrsAsyncServletResource.FALSE); } @Test public void cancelVoidOnCanceledTest() throws Exception { cancelVoidTestInternal(); Future<Response> cancel = invokeRequest("cancelvoid?stage=1"); checkString(cancel, JaxrsAsyncServletResource.TRUE); } @Test public void resumeCanceledTest() throws Exception { cancelVoidTestInternal(); Future<Response> resumeCanceled = invokeRequest("resume?stage=1", ""); checkString(resumeCanceled, JaxrsAsyncServletResource.FALSE); } @Test public void cancelIntTest() throws Exception { cancelIntTestInternal(); } @Test public void cancelIntOnResumedTest() throws Exception { suspendResumeTestInternal(); Future<Response> cancel = invokeRequest("cancelretry?stage=1", "20"); checkString(cancel, JaxrsAsyncServletResource.FALSE); } @Test public void cancelIntOnCanceledTest() throws Exception { cancelVoidTestInternal(); Future<Response> cancel = invokeRequest("cancelretry?stage=1", "20"); checkString(cancel, JaxrsAsyncServletResource.TRUE); } @Test public void resumeCanceledIntTest() throws Exception { cancelIntTestInternal(); Future<Response> resume = invokeRequest("resume?stage=1", ""); checkString(resume, JaxrsAsyncServletResource.FALSE); } @Test public void cancelDateTest() throws Exception { cancelDateTestInternal(); } @Test public void cancelDateOnResumedTest() throws Exception { suspendResumeTestInternal(); Future<Response> cancel = invokeRequest("canceldate?stage=1", System.currentTimeMillis()); checkString(cancel, JaxrsAsyncServletResource.FALSE); } @Test public void cancelDateOnCanceledTest() throws Exception { cancelVoidTestInternal(); Future<Response> cancel = invokeRequest("canceldate?stage=1", System.currentTimeMillis()); checkString(cancel, JaxrsAsyncServletResource.TRUE); } @Test public void resumeCanceledDateTest() throws Exception { cancelDateTestInternal(); Future<Response> resumeResumed = invokeRequest("resume?stage=1", ""); checkString(resumeResumed, JaxrsAsyncServletResource.FALSE); } @Test public void isCanceledWhenCanceledTest() throws Exception { cancelVoidTestInternal(); Future<Response> is = invokeRequest("iscanceled?stage=1"); checkString(is, JaxrsAsyncServletResource.TRUE); } @Test public void isCanceledWhenSuspendedTest() throws Exception { invokeClear(); invokeRequest("suspend"); Future<Response> is = invokeRequest("iscanceled?stage=0"); checkString(is, JaxrsAsyncServletResource.FALSE); } @Test public void isCanceledWhenResumedTest() throws Exception { suspendResumeTestInternal(); Future<Response> is = invokeRequest("iscanceled?stage=1"); checkString(is, JaxrsAsyncServletResource.FALSE); } @Test public void isDoneWhenResumedTest() throws Exception { suspendResumeTestInternal(); Future<Response> is = invokeRequest("isdone?stage=1"); checkString(is, JaxrsAsyncServletResource.TRUE); } @Test public void isDoneWhenSuspendedTest() throws Exception { invokeClear(); invokeRequest("suspend"); Future<Response> is = invokeRequest("isdone?stage=0"); checkString(is, JaxrsAsyncServletResource.FALSE); } @Test public void isDoneWhenCanceledTest() throws Exception { cancelVoidTestInternal(); Future<Response> is = invokeRequest("isdone?stage=1"); checkString(is, JaxrsAsyncServletResource.TRUE); } @Test @Category({ExpectedFailing.class}) // [RESTEASY-1446] FIXME public void isDoneWhenTimedOutTest() throws Exception { setTimeoutTestInternal(); Future<Response> is = invokeRequest("isdone?stage=1"); checkString(is, JaxrsAsyncServletResource.TRUE); } @Test public void isSuspendedWhenSuspendedTest() throws Exception { invokeClear(); invokeRequest("suspend"); Future<Response> is = invokeRequest("issuspended?stage=0"); checkString(is, JaxrsAsyncServletResource.TRUE); } @Test public void isSuspendedWhenCanceledTest() throws Exception { cancelVoidTestInternal(); Future<Response> is = invokeRequest("issuspended?stage=1"); checkString(is, JaxrsAsyncServletResource.FALSE); } @Test public void isSuspendedWhenResumedTest() throws Exception { suspendResumeTestInternal(); Future<Response> is = invokeRequest("issuspended?stage=1"); checkString(is, JaxrsAsyncServletResource.FALSE); } @Test public void suspendResumeTest() throws Exception { suspendResumeTestInternal(); } @Test public void resumeAnyJavaObjectInputStreamTest() throws Exception { invokeClear(); String expectedResponse = "Expected response"; Future<Response> suspend = invokeRequest("suspend"); Future<Response> resume = invokeRequest("resume?stage=0", new ByteArrayInputStream(expectedResponse.getBytes())); checkString(resume, JaxrsAsyncServletResource.TRUE); checkString(suspend, expectedResponse); } @Test public void resumeResumedTest() throws Exception { suspendResumeTestInternal(); // resume & store Future<Response> resumeResumed = invokeRequest("resume?stage=1", ""); checkString(resumeResumed, JaxrsAsyncServletResource.FALSE); } @Test public void resumeWithCheckedExceptionTest() throws Exception { invokeClear(); Future<Response> suspend = invokeRequest("suspend"); Future<Response> resume = invokeRequest("resumechecked?stage=0"); checkString(resume, JaxrsAsyncServletResource.TRUE); checkException(suspend, IOException.class); } @Test public void resumeWithRuntimeExceptionTest() throws Exception { invokeClear(); Future<Response> suspend = invokeRequest("suspend"); Future<Response> resume = invokeRequest("resumeruntime?stage=0"); checkString(resume, JaxrsAsyncServletResource.TRUE); checkException(suspend, RuntimeException.class); } @Test public void resumeWithExceptionReturnsFalseWhenResumedTest() throws Exception { suspendResumeTestInternal(); Future<Response> resume = invokeRequest("resumechecked?stage=1"); checkString(resume, JaxrsAsyncServletResource.FALSE); } @Test @Category({ExpectedFailing.class}) // [RESTEASY-1446] FIXME public void setTimeoutTest() throws Exception { setTimeoutTestInternal(); } @Test @Category({ExpectedFailing.class}) // [RESTEASY-1446] FIXME public void updateTimeoutTest() throws Exception { invokeClear(); Future<Response> suspend = invokeRequest("suspend"); Future<Response> setTimeout = invokeRequest("settimeout?stage=0", 600000); checktStatus(getResponse(setTimeout), Status.NO_CONTENT); checkFalse(suspend.isDone(), "Suspended AsyncResponse already received"); setTimeout = invokeRequest("settimeout?stage=1", 200); checktStatus(getResponse(setTimeout), Status.NO_CONTENT); // WebApplication exception with 503 is caught by // ServiceUnavailableExceptionMapper Response fromMapper = getResponse(suspend); checktStatus(fromMapper, Status.REQUEST_TIMEOUT); String entity = fromMapper.readEntity(String.class); checkContains(entity, HttpResponseCodes.SC_SERVICE_UNAVAILABLE); logMsg("Found expected status 503"); } @Test public void handleTimeOutWaitsForeverTest() throws Exception { String responseMsg = "handleTimeOutWaitsForeverTest"; invokeClear(); Future<Response> suspend = invokeRequest("suspend"); Future<Response> setTimeout = invokeRequest("timeouthandler?stage=0", 1); Future<Response> resume = invokeRequest("resume?stage=1", responseMsg); checktStatus(getResponse(setTimeout), Status.NO_CONTENT); checkString(resume, JaxrsAsyncServletResource.TRUE); checkString(suspend, responseMsg); } @Test public void handleTimeoutCancelsTest() throws Exception { invokeClear(); Future<Response> suspend = invokeRequest("suspend"); Future<Response> setTimeout = invokeRequest("timeouthandler?stage=0", 2); checktStatus(getResponse(setTimeout), Status.NO_CONTENT); checktStatus(getResponse(suspend), Status.SERVICE_UNAVAILABLE); Future<Response> resume = invokeRequest("issuspended?stage=1"); checkString(resume, JaxrsAsyncServletResource.FALSE); } @Test public void handleTimeoutResumesTest() throws Exception { invokeClear(); Future<Response> suspend = invokeRequest("suspend"); Future<Response> setTimeout = invokeRequest("timeouthandler?stage=0", 3); checktStatus(getResponse(setTimeout), Status.NO_CONTENT); checkString(suspend, JaxrsAsyncServletResource.RESUMED); Future<Response> resume = invokeRequest("issuspended?stage=1"); checkString(resume, JaxrsAsyncServletResource.FALSE); } // } protected String getAbsoluteUrl() { return generateURL("/resource"); } private void invokeClear() throws Exception { Response response = client.target(getAbsoluteUrl()).path("clear").request().get(); Assert.assertEquals(HttpResponseCodes.SC_NO_CONTENT, response.getStatus()); } private Future<Response> invokeRequest(String resource) { AsyncInvoker async = createAsyncInvoker(resource); Future<Response> future = async.get(); return future; } private <T> Future<Response> invokeRequest(String resource, T entity) { AsyncInvoker async = createAsyncInvoker(resource); Future<Response> future = async.post(Entity.entity(entity, MediaType.TEXT_PLAIN_TYPE)); return future; } private WebTarget createWebTarget(String resource) { Client client = ClientBuilder.newClient(); WebTarget target = client.target(generateURL("/resource/" + resource)); return target; } private AsyncInvoker createAsyncInvoker(String resource) { WebTarget target = createWebTarget(resource); AsyncInvoker async = target.request().async(); return async; } private static Response getResponse(Future<Response> future) throws Exception { Response response = future.get(); return response; } private static void checktStatus(Response response, Response.Status status) throws Exception { checkEquals(response.getStatus(), status.getStatusCode(), "Unexpected status code received", response.getStatus(), "expected was", status); logMsg("Found expected status", status); } private static void checkString(Future<Response> future, String check) throws Exception { Response response = getResponse(future); checktStatus(response, Status.OK); String content = response.readEntity(String.class); checkEquals(check, content, "Unexpected response content", content); logMsg("Found expected string", check); } private static void checkException(Future<Response> future, Class<? extends Throwable> e) throws Exception { String clazz = e.getName(); Response response = getResponse(future); checktStatus(response, Response.Status.NOT_ACCEPTABLE); checkContainsString(response.readEntity(String.class), clazz, clazz, "not thrown"); logMsg(clazz, "has been thrown as expected"); } public static void checkContainsString(String string, String substring, Object... message) throws Exception { checkTrue(string.contains(substring), message); } public static <T> void checkContains(T text, T subtext, Object... message) throws Exception { checkContainsString(text.toString(), subtext.toString(), message); } public static void checkTrue(boolean condition, Object... message) { if (!condition) { Assert.fail(objectsToString(message)); } } public static void checkFalse(boolean condition, Object... message) throws Exception { checkTrue(!condition, message); } }