Chapter 48. Returning Information to the Consumer Abstract RESTful requests require that at least an HTTP response code be returned to the consumer. In many cases, a request can be satisfied by returning a plain JAXB object or a GenericEntity object. When the resource method needs to return additional metadata along with the response entity, JAX-RS resource methods can return a Response object containing any needed HTTP headers or other metadata. 48.1. Return Types The information returned to the consumer determines the exact type of object a resource method returns. This may seem obvious, but the mapping between Java return objects and what is returned to a RESTful consumer is not one-to-one. At a minimum, RESTful consumers need to be returned a valid HTTP return code in addition to any response entity body. The mapping of the data contained within a Java object to a response entity is effected by the MIME types a consumer is willing to accept. To address the issues involved in mapping Java object to RESTful response messages, resource methods are allowed to return four types of Java constructs: Section 48.2, "Returning plain Java constructs" return basic information with HTTP return codes determined by the JAX-RS runtime. Section 48.2, "Returning plain Java constructs" return complex information with HTTP return codes determined by the JAX-RS runtime. Section 48.3, "Fine tuning an application's responses" return complex information with a programmatically determined HTTP return status. The Response object also allows HTTP headers to be specified. Section 48.4, "Returning entities with generic type information" return complex information with HTTP return codes determined by the JAX-RS runtime. The GenericEnitity object provides more information to the runtime components serializing the data. 48.2. Returning plain Java constructs Overview In many cases a resource class can return a standard Java type, a JAXB object, or any object for which the application has an entity provider. In these cases the runtime determines the MIME type information using the Java class of the object being returned. The runtime also determines the appropriate HTTP return code to send to the consumer. Returnable types Resource methods can return void or any Java type for which an entity writer is provided. By default, the runtime has providers for the following: the Java primitives the Number representations of the Java primitives JAXB objects the section called "Natively supported types" lists all of the return types supported by default. the section called "Custom writers" describes how to implement a custom entity writer. MIME types The runtime determines the MIME type of the returned entity by first checking the resource method and resource class for a @Produces annotation. If it finds one, it uses the MIME type specified in the annotation. If it does not find one specified by the resource implementation, it relies on the entity providers to determine the proper MIME type. By default the runtime assign MIME types as follows: Java primitives and their Number representations are assigned a MIME type of application/octet-stream . JAXB objects are assigned a MIME type of application/xml . Applications can use other mappings by implementing custom entity providers as described in the section called "Custom writers" . Response codes When resource methods return plain Java constructs, the runtime automatically sets the response's status code if the resource method completes without throwing an exception. The status code is set as follows: 204 (No Content)-the resource method's return type is void 204 (No Content)-the value of the returned entity is null 200 (OK)-the value of the returned entity is not null If an exception is thrown before the resource method completes the return status code is set as described in Chapter 50, Handling Exceptions . 48.3. Fine tuning an application's responses 48.3.1. Basics of building responses Overview RESTful services often need more precise control over the response returned to a consumer than is allowed when a resource method returns a plain Java construct. The JAX-RS Response class allows a resource method to have some control over the return status sent to the consumer and to specify HTTP message headers and cookies in the response. Response objects wrap the object representing the entity that is returned to the consumer. Response objects are instantiated using the ResponseBuilder class as a factory. The ResponseBuilder class also has many of the methods used to manipulate the response's metadata. For instance the ResonseBuilder class contains the methods for setting HTTP headers and cache control directives. Relationship between a response and a response builder The Response class has a protected constructor, so they cannot be instantiated directly. They are created using the ResponseBuilder class enclosed by the Response class. The ResponseBuilder class is a holder for all of the information that will be encapsulated in the response created from it. The ResponseBuilder class also has all of the methods responsible for setting HTTP header properties on the message. The Response class does provide some methods that ease setting the proper response code and wrapping the entity. There are methods for each of the common response status codes. The methods corresponding to status that include an entity body, or required metadata, include versions that allow for directly setting the information into the associated response builder. The ResponseBuilder class' build() method returns a response object containing the information stored in the response builder at the time the method is invoked. After the response object is returned, the response builder is returned to a clean state. Getting a response builder There are two ways to get a response builder: Using the static methods of the Response class as shown in Getting a response builder using the Response class . Getting a response builder using the Response class When getting a response builder this way you do not get access to an instance you can manipulate in multiple steps. You must string all of the actions into a single method call. Using the Apache CXF specific ResponseBuilderImpl class. This class allows you to work directly with a response builder. However, it requires that you manually set all of the response builders information manually. Example 48.1, "Getting a response builder using the ResponseBuilderImpl class" shows how Getting a response builder using the Response class could be rewritten using the ResponseBuilderImpl class. Example 48.1. Getting a response builder using the ResponseBuilderImpl class Note You could also simply assign the ResponseBuilder returned from a Response class' method to a ResponseBuilderImpl object. More information For more information about the Response class see the Response class' Javadoc . For more information about the ResponseBuilder class see the ResponseBuilder class' Javadoc . For more information on the Apache CXF ResponseBuilderIml class see the ResponseBuilderImpl Javadoc . 48.3.2. Creating responses for common use cases Overview The Response class provides shortcut methods for handling the more common responses that a RESTful service will need. These methods handle setting the proper headers using either provided values or default values. They also handle populating the entity body when appropriate. Creating responses for successful requests When a request is successfully processed the application needs to send a response to acknowledge that the request has been fulfilled. That response may contain an entity. The most common response when successfully completing a response is OK . An OK response typically contains an entity that corresponds to the request. The Response class has an overloaded ok() method that sets the response status to 200 and adds a supplied entity to the enclosed response builder. There are five versions of the ok() method. The most commonly used variant are: Response.ok() -creates a response with a status of 200 and an empty entity body. Response.ok(java.lang.Object entity) -creates a response with a status of 200 , stores the supplied object in the responses entity body, and determines the entities media type by introspecting the object. Creating a response with an 200 response shows an example of creating a response with an OK status. Creating a response with an 200 response For cases where the requester is not expecting an entity body, it may be more appropriate to send a 204 No Content status instead of an 200 OK status. The Response.noContent() method will create an appropriate response object. Creating a response with a 204 status shows an example of creating a response with an 204 status. Creating a response with a 204 status Creating responses for redirection The Response class provides methods for handling three of the redirection response statuses. 303 See Other The 303 See Other status is useful when the requested resource needs to permanently redirect the consumer to a new resource to process the request. The Response classes seeOther() method creates a response with a 303 status and places the new resource URI in the message's Location field. The seeOther() method takes a single parameter that specifies the new URI as a java.net.URI object. 304 Not Modified The 304 Not Modified status can be used for different things depending on the nature of the request. It can be used to signify that the requested resource has not changed since a GET request. It can also be used to signify that a request to modify the resource did not result in the resource being changed. The Response classes notModified() methods creates a response with a 304 status and sets the modified date property on the HTTP message. There are three versions of the notModified() method: notModified notModified javax.ws.rs.core.Entity tag notModified java.lang.String tag 307 Temporary Redirect The 307 Temporary Redirect status is useful when the requested resource needs to direct the consumer to a new resource, but wants the consumer to continue using this resource to handle future requests. The Response classes temporaryRedirect() method creates a response with a 307 status and places the new resource URI in the message's Location field. The temporaryRedirect() method takes a single parameter that specifies the new URI as a java.net.URI object. Creating a response with a 304 status shows an example of creating a response with an 304 status. Creating a response with a 304 status Creating responses to signal errors The Response class provides methods to create responses for two basic processing errors: serverError -creates a response with a status of 500 Internal Server Error . notAcceptable java.util.List<javax.ws.rs.core.Variant> variants -creates a response with a 406 Not Acceptable status and an entity body containing a list of acceptable resource types. Creating a response with a 500 status shows an example of creating a response with an 500 status. Creating a response with a 500 status 48.3.3. Handling more advanced responses Overview The Response class methods provide short cuts for creating responses for common cases. When you need to address more complicated cases such as specifying cache control directives, adding custom HTTP headers, or sending a status not handled by the Response class, you need to use the ResponseBuilder classes methods to populate the response before using the build() method to generate the response object. As discussed in the section called "Getting a response builder" , you can use the Apache CXF ResponseBuilderImpl class to create a response builder instance that can be manipulated directly. Adding custom headers Custom headers are added to a response using the ResponseBuilder class' header() method. The header() method takes two parameters: name -a string specifying the name of the header value -a Java object containing the data stored in the header You can set multiple headers on the message by calling the header() method repeatedly. Adding a header to a response shows code for adding a header to a response. Adding a header to a response Adding a cookie Custom headers are added to a response using the ResponseBuilder class' cookie() method. The cookie() method takes one or more cookies. Each cookie is stored in a javax.ws.rs.core.NewCookie object. The easiest of the NewCookie class' contructors to use takes two parameters: name -a string specifying the name of the cookie value -a string specifying the value of the cookie You can set multiple cookies by calling the cookie() method repeatedly. Adding a cookie to a response shows code for adding a cookie to a response. Adding a cookie to a response Warning Calling the cookie() method with a null parameter list erases any cookies already associated with the response. Setting the response status When you want to return a status other than one of the statuses supported by the Response class' helper methods, you can use the ResponseBuilder class' status() method to set the response's status code. The status() method has two variants. One takes an int that specifies the response code. The other takes a Response.Status object to specify the response code. The Response.Status class is an enumeration enclosed in the Response class. It has entries for most of the defined HTTP response codes. Adding a header to a response shows code for setting the response status to 404 Not Found . Adding a header to a response Setting cache control directives The ResponseBuilder class' cacheControl() method allows you to set the cache control headers on the response. The cacheControl() method takes a javax.ws.rs.CacheControl object that specifies the cache control directives for the response. The CacheControl class has methods that correspond to all of the cache control directives supported by the HTTP specification. Where the directive is a simple on or off value the setter method takes a boolean value. Where the directive requires a numeric value, such as the max-age directive, the setter takes an int value. Adding a header to a response shows code for setting the no-store cache control directive. Adding a header to a response 48.4. Returning entities with generic type information Overview There are occasions where the application needs more control over the MIME type of the returned object or the entity provider used to serialize the response. The JAX-RS javax.ws.rs.core.GenericEntity<T> class provides finer control over the serializing of entities by providing a mechanism for specifying the generic type of the object representing the entity. Using a GenericEntity<T> object One of the criteria used for selecting the entity provider that serializes a response is the generic type of the object. The generic type of an object represents the Java type of the object. When a common Java type or a JAXB object is returned, the runtime can use Java reflection to determine the generic type. However, when a JAX-RS Response object is returned, the runtime cannot determine the generic type of the wrapped entity and the actual Java class of the object is used as the Java type. To ensure that the entity provider is provided with correct generic type information, the entity can be wrapped in a GenericEntity<T> object before being added to the Response object being returned. Resource methods can also directly return a GenericEntity<T> object. In practice, this approach is rarely used. The generic type information determined by reflection of an unwrapped entity and the generic type information stored for an entity wrapped in a GenericEntity<T> object are typically the same. Creating a GenericEntity<T> object There are two ways to create a GenericEntity<T> object: Create a subclass of the GenericEntity<T> class using the entity being wrapped. Creating a GenericEntity<T> object using a subclass shows how to create a GenericEntity<T> object containing an entity of type List<String> whose generic type will be available at runtime. Creating a GenericEntity<T> object using a subclass The subclass used to create a GenericEntity<T> object is typically anonymous. Create an instance directly by supplying the generic type information with the entity. Example 48.2, "Directly instantiating a GenericEntity<T> object" shows how to create a response containing an entity of type AtomicInteger . Example 48.2. Directly instantiating a GenericEntity<T> object 48.5. Asynchronous Response 48.5.1. Asynchronous Processing on the Server Overview The purpose of asynchronous processing of invocations on the server side is to enable more efficient use of threads and, ultimately, to avoid the scenario where client connection attempts are refused because all of the server's request threads are blocked. When an invocation is processed asynchronously, the request thread is freed up almost immediately. Note Note that even when asynchronous processing is enabled on the server side, a client will still remain blocked until it receives a response from the server. If you want to see asynchronous behaviour on the client side, you must implement client-side asynchronous processing. See Section 49.6, "Asynchronous Processing on the Client" . Basic model for asynchronous processing Figure 48.1, "Threading Model for Asynchronous Processing" shows an overview of the basic model for asynchronous processing on the server side. Figure 48.1. Threading Model for Asynchronous Processing In outline, a request is processed as follows in the asynchronous model: An asynchronous resource method is invoked within a request thread (and receives a reference to an AsyncResponse object, which will be needed later to send back the response). The resource method encapsulates the suspended request in a Runnable object, which contains all of the information and processing logic required to process the request. The resource method pushes the Runnable object onto the blocking queue of the executor thread pool. The resource method can now return, thus freeing up the request thread. When the Runnable object gets to the top of the queue, it is processed by one of the threads in the executor thread pool. The encapsulated AsyncResponse object is then used to send the response back to the client. Thread pool implementation with Java executor The java.util.concurrent API is a powerful API that enables you to create a complete thread pool implementation very easily. In the terminology of the Java concurrency API, a thread pool is called an executor . It requires only a single line of code to create a complete working thread pool, including the working threads and the blocking queue that feeds them. For example, to create a complete working thread pool like the Executor Thread Pool shown in Figure 48.1, "Threading Model for Asynchronous Processing" , create a java.util.concurrent.Executor instance, as follows: This constructor creates a new thread pool with five threads, fed by a single blocking queue with which can hold up to 10 Runnable objects. To submit a task to the thread pool, call the executor.execute method, passing in a reference to a Runnable object (which encapsulates the asynchronous task). Defining an asynchronous resource method To define a resource method that is asynchronous, inject an argument of type javax.ws.rs.container.AsyncResponse using the @Suspended annotation and make sure that the method returns void . For example: Note that the resource method must return void , because the injected AsyncResponse object will be used to return the response at a later time. AsyncResponse class The javax.ws.rs.container.AsyncResponse class provides an a abstract handle on an incoming client connection. When an AsyncResponse object is injected into a resource method, the underlying TCP client connection is initially in a suspended state. At a later time, when you are ready to return the response, you can re-activate the underlying TCP client connection and pass back the response, by calling resume on the AsyncResponse instance. Alternatively, if you need to abort the invocation, you could call cancel on the AsyncResponse instance. Encapsulating a suspended request as a Runnable In the asynchronous processing scenario shown in Figure 48.1, "Threading Model for Asynchronous Processing" , you push the suspended request onto a queue, from where it can be processed at a later time by a dedicated thread pool. In order for this approach to work, however, you need to have some way of encapsulating the suspended request in an object. The suspended request object needs to encapsulate the following things: Parameters from the incoming request (if any). The AsyncResponse object, which provides a handle on the incoming client connection and a way of sending back the response. The logic of the invocation. A convenient way to encapsulate these things is to define a Runnable class to represent the suspended request, where the Runnable.run() method encapsulates the logic of the invocation. The most elegant way to do this is to implement the Runnable as a local class, as shown in the following example. Example of asynchronous processing To implement the asynchronous processing scenario, the implementation of the resource method must pass a Runnable object (representing the suspended request) to the executor thread pool. In Java 7 and 8, you can exploit some novel syntax to define the Runnable class as a local class, as shown in the following example: Note how the resource method arguments, id and response , are passed straight into the definition of the Runnable local class. This special syntax enables you to use the resource method arguments directly in the Runnable.run() method, without having to define corresponding fields in the local class. Important In order for this special syntax to work, the resource method parameters must be declared as final (which implies that they must not be changed in the method implementation). 48.5.2. Timeouts and Timeout Handlers Overview The asynchronous processing model also provides support for imposing timeouts on REST invocations. By default, a timeout results in a HTTP error response being sent back to the client. But you also have the option of registering a timeout handler callback, which enables you to customize the response to a timeout event. Example of setting a timeout without a handler To define a simple invocation timeout, without specifying a timeout handler, call the setTimeout method on the AsyncResponse object, as shown in the following example: Note that you can specify the timeout value using any time unit from the java.util.concurrent.TimeUnit class. The preceding example does not show the code for sending the request to the executor thread pool. If you just wanted to test the timeout behaviour, you could include just the call to async.SetTimeout in the resource method body, and the timeout would be triggered on every invocation. The AsyncResponse.NO_TIMEOUT value represents an infinite timeout. Default timeout behaviour By default, if the invocation timeout is triggered, the JAX-RS runtime raises a ServiceUnavailableException exception and sends back a HTTP error response with the status 503 . TimeoutHandler interface If you want to customize the timeout behaviour, you must define a timeout handler, by implementing the TimeoutHandler interface: When you override the handleTimeout method in your implementation class, you can choose between the following approaches to dealing with the timeout: Cancel the response, by calling the asyncResponse.cancel method. Send a response, by calling the asyncResponse.resume method with the response value. Extend the waiting period, by calling the asyncResponse.setTimeout method. (For example, to wait for a further 10 seconds, you could call asyncResponse.setTimeout(10, TimeUnit.SECONDS) ). Example of setting a timeout with a handler To define an invocation timeout with a timeout handler, call both the setTimeout method and the setTimeoutHandler method on the AsyncResponse object, as shown in the following example: Where this example registers an instance of the CancelTimeoutHandlerImpl timeout handler to handle the invocation timeout. Using a timeout handler to cancel the response The CancelTimeoutHandlerImpl timeout handler is defined as follows: The effect of calling cancel on the AsyncResponse object is to send a HTTP 503 ( Service unavailable ) error response to the client. You can optionally specify an argument to the cancel method (either an int or a java.util.Date value), which would be used to set a Retry-After: HTTP header in the response message. Clients often ignore the Retry-After: header, however. Dealing with a cancelled response in the Runnable instance If you have encapsulated a suspended request as a Runnable instance, which is queued for processing in an executor thread pool, you might find that the AsyncResponse has been cancelled by the time the thread pool gets around to processing the request. For this reason, you ought to add some code to your Runnable instance, which enables it to cope with a cancelled AsyncResponse object. For example: 48.5.3. Handling Dropped Connections Overview It is possible to add a callback to deal with the case where the client connection is lost. ConnectionCallback interface To add a callback for dropped connections, you must implement the javax.ws.rs.container.ConnectionCallback interface, which is defined as follows: Registering a connection callback After implementing a connection callback, you must register it with the current AsyncResponse object, by calling one of the register methods. For example, to register a connection callback of type, MyConnectionCallback : Typical scenario for connection callback Typically, the main reason for implementing a connection callback would be to free up resources associated with the dropped client connection (where you could use the AsyncResponse instance as the key to identify the resources that need to be freed). 48.5.4. Registering Callbacks Overview You can optionally add a callback to an AsyncResponse instance, in order to be notified when the invocation has completed. There are two alternative points in the processing when this callback can be invoked, either: After the request processing is finished and the response has already been sent back to the client, or After the request processing is finished and an unmapped Throwable has been propagated to the hosting I/O container. CompletionCallback interface To add a completion callback, you must implement the javax.ws.rs.container.CompletionCallback interface, which is defined as follows: Usually, the throwable argument is null . However, if the request processing resulted in an unmapped exception, throwable contains the unmapped exception instance. Registering a completion callback After implementing a completion callback, you must register it with the current AsyncResponse object, by calling one of the register methods. For example, to register a completion callback of type, MyCompletionCallback :

Commands:
import javax.ws.rs.core.Response; Response r = Response.ok().build();
import javax.ws.rs.core.Response; import org.apache.cxf.jaxrs.impl.ResponseBuilderImpl; ResponseBuilderImpl builder = new ResponseBuilderImpl(); builder.status(200); Response r = builder.build();
import javax.ws.rs.core.Response; import demo.jaxrs.server.Customer; Customer customer = new Customer("Jane", 12); return Response.ok(customer).build();
import javax.ws.rs.core.Response; return Response.noContent().build();
import javax.ws.rs.core.Response; return Response.notModified().build();
import javax.ws.rs.core.Response; return Response.serverError().build();
import javax.ws.rs.core.Response; import org.apache.cxf.jaxrs.impl.ResponseBuilderImpl; ResponseBuilderImpl builder = new ResponseBuilderImpl(); builder.header("username", "joe"); Response r = builder.build();
import javax.ws.rs.core.Response; import javax.ws.rs.core.NewCookie; NewCookie cookie = new NewCookie("username", "joe"); Response r = Response.ok().cookie(cookie).build();
import javax.ws.rs.core.Response; import org.apache.cxf.jaxrs.impl.ResponseBuilderImpl; ResponseBuilderImpl builder = new ResponseBuilderImpl(); builder.status(404); Response r = builder.build();
import javax.ws.rs.core.Response; import javax.ws.rs.core.CacheControl; import org.apache.cxf.jaxrs.impl.ResponseBuilderImpl; CacheControl cache = new CacheControl(); cache.setNoCache(true); ResponseBuilderImpl builder = new ResponseBuilderImpl(); builder.cacheControl(cache); Response r = builder.build();
import javax.ws.rs.core.GenericEntity; List<String> list = new ArrayList<String>(); GenericEntity<List<String>> entity = new GenericEntity<List<String>>(list) {}; Response response = Response.ok(entity).build();
import javax.ws.rs.core.GenericEntity; AtomicInteger result = new AtomicInteger(12); GenericEntity<AtomicInteger> entity = new GenericEntity<AtomicInteger>(result, result.getClass().getGenericSuperclass()); Response response = Response.ok(entity).build();
Executor executor = new ThreadPoolExecutor( 5, // Core pool size 5, // Maximum pool size 0, // Keep-alive time TimeUnit.SECONDS, // Time unit new ArrayBlockingQueue<Runnable>(10) // Blocking queue );
// Java import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.container.Suspended; @Path("/bookstore") public class BookContinuationStore { @GET @Path("{id}") public void handleRequestInPool(@PathParam("id") String id, @Suspended AsyncResponse response) { } }
// Java package org.apache.cxf.systest.jaxrs; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.ws.rs.GET; import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.container.CompletionCallback; import javax.ws.rs.container.ConnectionCallback; import javax.ws.rs.container.Suspended; import javax.ws.rs.container.TimeoutHandler; import org.apache.cxf.phase.PhaseInterceptorChain; @Path("/bookstore") public class BookContinuationStore { private Map<String, String> books = new HashMap<String, String>(); private Executor executor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10)); public BookContinuationStore() { init(); } @GET @Path("{id}") public void handleRequestInPool(final @PathParam("id") String id, final @Suspended AsyncResponse response) { executor.execute(new Runnable() { public void run() { // Retrieve the book data for 'id' // which is presumed to be a very slow, blocking operation // bookdata = // Re-activate the client connection with 'resume' // and send the 'bookdata' object as the response response.resume(bookdata); } }); } }
// Java // Java import java.util.concurrent.TimeUnit; import javax.ws.rs.GET; import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.container.Suspended; import javax.ws.rs.container.TimeoutHandler; @Path("/bookstore") public class BookContinuationStore { @GET @Path("/books/defaulttimeout") public void getBookDescriptionWithTimeout(@Suspended AsyncResponse async) { async.setTimeout(2000, TimeUnit.MILLISECONDS); // Optionally, send request to executor queue for processing // } }
// Java package javax.ws.rs.container; public interface TimeoutHandler { public void handleTimeout(AsyncResponse asyncResponse); }
// Java import javax.ws.rs.GET; import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.container.Suspended; import javax.ws.rs.container.TimeoutHandler; @Path("/bookstore") public class BookContinuationStore { @GET @Path("/books/cancel") public void getBookDescriptionWithCancel(@PathParam("id") String id, @Suspended AsyncResponse async) { async.setTimeout(2000, TimeUnit.MILLISECONDS); async.setTimeoutHandler(new CancelTimeoutHandlerImpl()); // Optionally, send request to executor queue for processing // } }
// Java import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.container.TimeoutHandler; @Path("/bookstore") public class BookContinuationStore { private class CancelTimeoutHandlerImpl implements TimeoutHandler { @Override public void handleTimeout(AsyncResponse asyncResponse) { asyncResponse.cancel(); } } }
// Java @Path("/bookstore") public class BookContinuationStore { private void sendRequestToThreadPool(final String id, final AsyncResponse response) { executor.execute(new Runnable() { public void run() { if ( !response.isCancelled() ) { // Process the suspended request // } } }); } }
// Java package javax.ws.rs.container; public interface ConnectionCallback { public void onDisconnect(AsyncResponse disconnected); }
asyncResponse.register(new MyConnectionCallback());
// Java package javax.ws.rs.container; public interface CompletionCallback { public void onComplete(Throwable throwable); }
asyncResponse.register(new MyCompletionCallback());