I have a lightweight Java application exposing a web service through Jersey/Grizzly. It's fairly simple as it just sends back JSON content:
#GET
public Response status() {
CacheControl cc = new CacheControl();
cc.setMaxAge(CLIENT_EXPIRY);
cc.setPrivate(true);
ResponseBuilder builder = Response.ok(someJsonString, MediaType.APPLICATION_JSON);
builder.cacheControl(cc);
return builder.build();
}
I would like to perform server-side operations when and only when a client requests this response, but without delaying the response itself.
These other methods will not influence the response, but will use up server resources, and take a significant time. That would make the end user experience less enjoyable if I simply pasted the call in the middle of response building.
What would be a good way to monitor the endpoint activity and trigger a server-side treatment without delaying the response?
Start a new thread that will do the work, and then return the response to the client.
Consider using a thread pull, not to exhaust your server's resources.
This way the client will not wait for response and the task will be executed.
Related
Is it possible to decide at runtime whether a Jersey REST request to an resource endpoint should be handled synchronously or asynchronously? Let's take a simple example.
The synchronous version:
#Path("resource")
public class Resource {
#GET
#Produces({MediaType.TEXT_PLAIN})
public Response get() {
return Response.ok("Hello there!").build();
}
}
The asynchronous version:
#Path("resource")
public class Resource {
#GET
#Produces({MediaType.TEXT_PLAIN})
public void get(#Suspended final AsyncResponse r) {
r.resume(Response.ok("Hello there!").build()); // usually called somewhere from another thread
}
}
Depending on certain parameters, I would like to decide at runtime whether the GET request should be handled synchronously or asynchronously. The URL of the resource endpoint (http://server/resource) must be the same in both cases. Is this possible?
Of course, as you can see in the example above, the synchronous version can be faked in an asynchronous manner by simply calling AsyncResponse.resume(...). However, I would to avoid the overhead of creating the asynchronous response.
A step back
The JAX-RS Asynchronous Server API is all about how the container will manage the request. But it will still hold the request and won't affect the client experience.
Quoting the Jersey documentation about the Asynchronous Server API:
Note that the use of server-side asynchronous processing model will
not improve the request processing time perceived by the client. It
will however increase the throughput of the server, by releasing the
initial request processing thread back to the I/O container while the
request may still be waiting in a queue for processing or the
processing may still be running on another dedicated thread. The
released I/O container thread can be used to accept and process new
incoming request connections.
The approaches described below won't bring any benefits to your client.
Using a custom header
You could have different URLs for sync and async methods and create a pre-matching filter, which is executed before the request matching is started.
To do it, implement ContainerRequestFilter, annotate it with #PreMatching and, based on your conditions (headers, parameters, etc), change the requested URI:
#Provider
#PreMatching
public class PreMatchingFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (requestContext.getHeaders().get("X-Use-Async") != null) {
requestContext.setRequestUri(yourNewURI);
}
}
}
Have a look at the ContainerRequestContext API.
Using a custom media type
I haven't tested the following solution, but it should work. You can keep the same URL for both sync and async methods, just accepting a different content type for each method.
For example:
Sync method: #Consumes("application/vnd.example.sync+text")
Async method: #Consumes("application/vnd.example.async+text")
And use the PreMatchingFilter to change the Content-Type header based on your conditions, like the following:
if (useSync) {
requestContext.getHeaders().putSingle(
HttpHeaders.CONTENT_TYPE, "application/vnd.example.sync+text");
} else {
requestContext.getHeaders().putSingle(
HttpHeaders.CONTENT_TYPE, "application/vnd.example.async+text");
}
According to the documentation, ContainerRequestContext#getHeaders() returns a mutable map with the request headers.
You could use a custom MediaType...you can for example put #Produces("simple") on your simple get method and #Produces("asynch") on your asynchronous get method. In your client you then can set the Accept Header of your call to "simple" or "asynch" depending on what you need.
I have developed a asynchronous JAX-RS web method using Apache CXF API. The webmethod takes a custom type as parameter as in
#POST
#Path("/query")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(value={MediaType.APPLICATION_JSON , "application/CustomType"})
public void getQueryResults(#Suspended final AsyncResponse asyncResponse, CustomType conf)
I had implemented(Service as well as Client side) a Custom MessageBodyReader and MessageBodyWriter to take care of serializing my 'CustomType'.
On the Client side i ergister the imlpemented ones as
Client client = ClientBuilder.newClient().register(CacheConfigReader.class).register(CacheConfigWriter.class);
I make a async request to the service that has
asyncResponse.resume(result); // result is a string
On the client
Future<String> future = asyncInvoker.post(entity, String.class);
My observation is that randomly the response is empty though on the server logs am able to see non-empty result. upon debugging i find that there are two threads that invoke
JaxrsClientCallback . handleResponse()
One of them with the actual result and another empty. Based on what executes first the result is the actual string or empty. The trace of the call contains invocation from phase interceptor chain.
This occurs only when i register the client with Custom reader and writers. When I set the request body with a json only one thread handles the response.
Can someone shed light on why the addition of MessageBodyReaders / Writers causes this issue ?
I'm writing a REST service with Spring Web 4.0.5 and one of called methods is sending e-mail (with javax mail). Sending mail takes some time, but I would like to be able to send HTTP response (no matter what response, e.g. 200) BEFORE this method finishes - so before the mail is sent. Is it even possible? Preferably without multithreading?
#RestController
#RequestMapping(value = "/mails", produces = "application/json")
public class RestMailService{
#Autowired
MailService mailService;
#RequestMapping(value="/test", method = RequestMethod.GET)
public void sendMail(){
mailService.sendMail();
}
}
I believe all possible solutions include multithreading. The thread will be either directly started by you or hidden behind messaging or something similar.
if you were to go with multi-threading after all please use some Executor instead of below suggested new Thread(...).start()
I would also note that returning HTTP 200 before the operation finishes may somewhat confuse the user as the code suggests the operation was successful where in fact the operation maybe didn't even start yet.
HTTP controller (Spring) gets a requests and making a blocking call. Once the blocking call is over the controls get back to the controller.
At this point I don't want to send a response back to the client but rather wait for another event(e.g., completion of some processing) to happen. As soon as that event happens I need a way to collect the data from the event and then return the HTTP response with this data.
HTTPController doSomething( HTTPRequest )
{
makeBlockingCall();
waitForEventToHappen();
collectDataFromEvent();
return HTTPResponse();
}
You wouldn't do that from within one request, because that would probably time out eventually and until then wouldn't provide feedback to the customer that something is happening.
Instead, you would immediately show a page that asynchronously (probably per AJAX) polls the server to see whether the result is already available.
So the first request will return an id that will be used in the second (AJAX) request to lookup the result.
I'm trying to set up a servlet that I can use to call webservices asynchronously. For the most part, it is working fine. I have a servlet with a doGet method and a js that calls it. I have a callback method in the js that the servlet correctly calls when it has finished doing its thing.
The complication is that one of the web services I am calling is also asynchronous, and I would like to be able to essentially call the js callback method a second time after the asynchronous ws callback has finished. For example, if you have a status field, when you call the synchronous web service, it immediately updates to "Beginning Synchronous Call" and then when the servlet callback arrives it changes to the callback value, which is the result of the web service.
When you call the asynchronous web service, the update field immediately updates to "Beginning Asynchronous Call", and shortly receives the first callback from the servlet indicating that the web service has been requested, so we update the field to "Processing Web Service" or whatever. The problem is that once the web service finishes and calls back to the servlet, I can't seem to figure out how to send the result to the js callback method.
I'm pretty new at AJAX and servlets, so maybe this is a horrible way to accomplish what I want.
The web services are both being called in the Servlet, mostly using Netbeans auto-generated WS calls. The WS calls themselves work fine, but once I get the result of the asynchronous WS, I am stuck inside of the handleResponse method of the webservice callback and no longer have any reference to the response element for the document I want to update.
I tried to store the original response variable as a static member variable and use it in the handleResponse method like so:
javax.xml.ws.AsyncHandler<WsClients.Op11Response> asyncHandler = new javax.xml.ws.AsyncHandler<WsClients.Op11Response>() {
public void handleResponse(javax.xml.ws.Response<WsClients.Op11Response> asyncResponse) {
try {
storedResponse.setContentType("text/xml");
String returnString = asyncResponse.get().getReturn();
storedResponse.getWriter().write("<returnData><content>"
+ returnString + "</content></returnData>");
} catch (Exception ex) {
}
}
};
This will not compile with a debugger attached and does not seem to be able to assign a reference anyway.
Is there a better way to do this?
The nature of HTTP is that you cannot send anything back to the client unless client requested this information either by polling or by keeping the connection open.
The operation to start the asynchronous call ends immediately and you need to return from the servlet doGet method (while technically you can stay in the servlet call until your async call finishes I wouldn't recommend that as it ties up the server resources. It is generally a good practice to return from the servlet as soon as you can).
The best course of action would be:
Have internal data structure (e.g. HashMap with appropriate synchronization) to hold the asynchronous calls that are executing.
When you start a new call, assign it pseudo-random key and return it from the initial call.
Using the above key, have browser-side javascript AJAX calls periodically poll the status of the call and display the results.
Do not forget to clean up finished or stale calls (for example by running a timer thread).
When you comfortable with the polling implementation in step 3 above, you may want to consider Comet, a.k.a. long poll to replace client-side polling.
Servlet cannot send response again. HTTP protocol is synchronous, and only client can initiate a request-response exchange.
For async updates you need to perform polling from the client side to the server side, and accumulate messages on the server side (in the sessions) until client picks them up or they expire.