I am making a restful call to the servlet doGet() method. Inside the doGet() method I am making a method call which takes a lot of time for processing due to which the doGet() method times out and cannot send a response back to the application which made the call.
So now I want to send a response back to the application which called the doGet() method immediately the doGet() is invoked.
Can I use Threads like one thread will reply back and the other one will continue with the method invocation. Do i need to use any webservice frame work like Jersey ?
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("in the Do Get");
String tableName = (String)request.getParameter("tableName");
tableName = tableName.replaceAll("[^A-Z0-9_]", "");
System.out.println("setup connections");
SampleService sampleService = new SampleService(new Properties());
Thread t = new Thread(new Runnable() {
public void run() {
// stuff here
sampleService.execute(tableName);
}
});
t.start();
i am making the class to implement the Runnable interface and writing the logic of the method call(the method which takes lot of time to process) inside the run method. Also how to send a response while this one is running
You can use 2 threads so one will respond and terminated and other will do the background job. This approach can be implemented in above code or you can user framework like Jersey. The major benefit of using any frameworks is frameworks hide complexities and provide easy to use and customizable interface (the concept of abstractions)
With Jersey, you can create Asynchronous API using AsyncResponse or you can use #ManagedAsync annotation along with AsyncResponse
One example is below -
#GET
#ManagedAsync
public void getMessage(#Suspended final AsyncResponse asyncResponse) {
System.out.println("1. Registering callback");
asyncResponse.register(new CompletionCallback() {
#Override
public void onComplete(Throwable throwable) {
if (throwable == null) {
// no error
System.out.println(
"4. Request processing is successful");
} else {
System.out.println(
"4. Error occurred in request processing");
}
}
});
System.out.println("2. Executing expensive operation");
String result = expensiveOperation();
System.out.println("3. Notify callback and send response ");
asyncResponse.resume(result);
System.out.println("5. Thread exiting");
}
Sysout statements are just for reference and can be removed.
If you want to do it with Servlet directly, then create one thread, start it and return your current response -
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("in the Do Get");
String tableName = (String)request.getParameter("tableName");
tableName = tableName.replaceAll("[^A-Z0-9_]", "");
System.out.println("setup connections");
SampleService sampleService = new SampleService(new Properties());
// this tread will run in even after response is send
Thread t = new Thread(new Runnable() {
public void run() {
// stuff here
sampleService.execute(tableName);
}
});
t.start();
// send the response
response.setContentType("application/json");
resp.getWriter().write("response JSON here");
return;
}
You can also use Jackson for object mapping to JSON for returning in response.
Related
We are working on a solution which is like this;
Request: (We receive the request via API call and send to third-party via a library we use)
OUR-Client --> OUR-API --> THIRD-PARTY
Response: (This response we receive from third-party asynchronously through a callback method given in the library we are using)
THIRD-PARTY --> OUR-CODE --> OUR-Client
Here is the below code and want to get rid of Thread.sleep() call and make use of the callback to provide response.
----- API Method -------------
#GetMapping
public ResponseEntity<String> getData(#RequestBody String requestId) throws SessionNotFound, InterruptedException {
dataService.get(requestId);
String msg;
long start = System.currentTimeMillis();
do {
// We want to get rid of this sleep() statement and some way to callback here as soon there is message.
Thread.sleep(30);
msg = clientApp.getRespnse(requestId);
} while(msg == null);
return ResponseEntity.ok(msg);
}
------- Service Class and Methods ---------------
#Service
public class DataService {
#Autowired
private ClientApp clientApp;
public void get(String requestId) throws SessionNotFound {
// This method is from the library we use. This only submits the request, response is received on different method.
send(requestId);
}
------- Component Class and Methods ---------------
#Component
public class ClientFixApp {
private Map<String, String> responseMap = new HashMap<>();
// This method is callback from the third party library, whenever there is response this method will get invoked and this message we need to send as response of the API call.
#Override
public void onResponse(String requestId)
throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType {
responseMap.put(msgId, jsonMsg);
}
public String getRespnse(String requestId) {
return responseMap.get(requestId);
}
}
DataService and ClientFixApp are flawed by design (the very fact it is 2 different classes while there must be one, speaks a lot). Truly asynchronous programs must allow to register user procedure as a callack, called when the I/O operation finished (successfully or not). ClientFixApp silently writes the result in a table, leaving for client no other option except polling.
You can use any existing asynchronous http library. For example, below is the code for the library java.net.http included in Java 11 and later. Other libraries have similar functionality.
public static CompletableFuture<HttpResponse<String>> doGet(String uri) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
}
public static void main(String[] args) {
CompletableFuture<HttpResponse<String>> future = doGet("https://postman-echo.com/get");
future.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
}
I am trying to develop sample Rest API application using Spring boot to test my java client library (Which is a conversion of a JS Client). Java client does some async tasks and returns response as callback.
Here is example of how i am calling it from my sample application's service.
#Async
public void initializeUser(InitRequest initRequest) {
String BASE_URL = "http://sample_url";
client.initUser(BASE_URL, initRequest, new ResponseCallback<InitRequest>() {
#Override
public void onSuccess(#NonNull InitRequest arg0) {
// Return arg0 to controller
System.out.println("User Initialized: " + arg0.getId());
}
#Override
public void onError(#NonNull ResponseBody error) {
// Return error to controller
System.out.println("User Initialize failed");
}
#Override
public void validationError(#NonNull String arg0) {
// return validationError to controller
// TODO Auto-generated method stub
}
});
}
Here initUser is method implemented in my client. When i call this method from my controller. controller returns before actual request is finished and i am not able to see any response in postman.
Here is a controller method.
#RequestMapping(method=RequestMethod.POST, value = "/init" )
public void initUser( #RequestBody InitRequest initRequest) {
experimentServices.initializeUser(initRequest);
// wait for request to finish and send response to user
}
Like in javascript i can use promises or async/await to wait till the services gives response before returning to user.
I wanted to know.....
1) How to achieve something similar in Java? (Services returns some data to controller which then goes as a response to end user).
2) Java methods needs specific return response while services might gives Error, ValidationError or Successfull response object. Is there a way to properly handle all this in a single function?
Any links or documentation will help.
Thanks
In your code , the method that you are calling asynchronously is a void method, meaning it won't return anything.In Spring, annotating a method of a bean with #Async will make it execute in a separate thread i.e. the caller will not wait for the completion of the called method.
So , if you want the controller to wait till the execution is completed, If you want to wait for the result I suggest you use Future. You could take the approach where you wrap the actual return in the Future instance.I haven't tried compiling this code , but something like below :
#Async
public Future<T> initializeUser(InitRequest initRequest) {
String BASE_URL = "http://sample_url";
client.initUser(BASE_URL, initRequest, new ResponseCallback<InitRequest>() {
#Override
public void onSuccess(#NonNull InitRequest arg0) {
return new AsyncResult<InitRequest>(arg0);
System.out.println("User Initialized: " + arg0.getId());
}
#Override
public void onError(#NonNull ResponseBody error) {
return new AsyncResult<ResponseBody>(error);
System.out.println("User Initialize failed");
}
#Override
public void validationError(#NonNull String arg0) {
return new AsyncResult<String>(arg0);
// TODO Auto-generated method stub
}
});
}
and in the rest controller to receive this :
#RequestMapping(method=RequestMethod.POST, value = "/init" )
public InitRequest initUser( #RequestBody InitRequest initRequest) {
Future<T> futureResult = experimentServices.initializeUser(initRequest);
// wait for request to finish and send response to user
if (futureResult.isDone()) {
return futureResult.get();
}
}
Best way to do is to avoid busy waiting on server, so use DeferredResult:
private ExecutorService executor = ....
#RequestMapping(method=RequestMethod.POST, value = "/init" )
public DeferredResult<ResponseEntity<InitResponse>> initUser(#RequestBody InitRequest initRequest) {
final DeferredResult<ResponseEntity<InitResponse>> output = new DeferredResult<>(5000L, 5000L);
executor.submit(() -> {
InitResponse response = processAndcreateRespnse(...);
output.setResult(new ResponseEntity<>(response, HttpStatus.OK));
});
return output;
}
How do I get the response out of the public void onResponse function?
Edit: I got Parse Error: "Cannot assign a value to final variable 'res'"
public JSONObject getRestRequest() {
final JSONObject res;
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, this.restPath, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) { // basically I just want to return this response
res = response;
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}
);
return res;
}
}
you cannot do this exactly as you have written because the network request is happening on a separate thread.
First, let's walk through your code so you're clear what's happening:
public JSONObject getRestRequest() { // 1 - your method is invoked by another method and control starts here
final JSONObject res; // 2 - This final (i.e. immutable) field is created
// 3 - You create a new request object - no networking is happening yet
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, this.restPath, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) { // basically I just want to return this response
// 5 - Some time later, after the request completes, this method is invoked
// BUT - you can't assign to res because it's final (immutable)
res = response;
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}
);
// 4 - IMMEDIATELY after creating "request", the value that was created in step 2 (which is null) is returned
return res;
}
So you're trying to execute an asynchronous operation (launching a thread to make a network request and parse the response) synchronously (blocking until it completes to return the result).
So that's your issue. To solve this, you have two options:
1 - Use an asynchronous callback:
public void getRestRequest(final Callback<JSONObject> callback) { // 1 - your method is invoked by another method and control starts here
// Now you're passing in a callback that will be invoked later with the result
// final JSONObject res; // 2 - You no longer need this local variable
// 3 - You create a new request object - no networking is happening yet
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, this.restPath, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) { // basically I just want to return this response
// 5 - Some time later, after the request completes, this method is invoked
// This time, you invoke your callback with the result
callback.onSuccess(response)
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
// 6 - You can also pass back errors to your callback
callback.onError(error);
}
}
);
// 4 - You return IMMEDIATELY after creating "request"
}
This method is more common. You create your callback, pass it in to the function and then just handle the response:
// 1 - Start showing some UI that a request is happening
showProgressDialog();
// 2 - A new callback to handle the network response is created - no request is happening yet
Callback<JSONObject> callback = new Callback<>() {
public void onSuccess(JSONObject response) {
// 4 - Some time later, when the network response finishes, this called
// Handle response
dismissProgressDialog(); // Back on the main thread, so safe to update the UI
}
public void onError(VolleyError error) {
// 5 - Or this is called if the request failed
// Handle error
dismissProgressDialog(); // Back on the main thread, so safe to update the UI
}
}
// 3 - Invoke the network request which will happen in a background thread.
// Meanwhile, the main (UI) thread is not blocked and the progress dialog continues to spin
network.getRestRequest(callback)
Option 2 - Use a RequestFuture.
public JSONObject getRestRequest() { // 1 - your method is invoked by another method and control starts here
// 2 - Initialize a Future to use to synchronously get the result
RequestFuture<JSONObject> future = RequestFuture.newFuture();
// 3 - You create a new request object with the future as the listener - no networking is happening yet
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, this.restPath, future, future);
// 4 - You return the value the future will obtain by making the network request
// THIS IS A BLOCKING CALL SO DON'T DO THIS ON THE MAIN THREAD
// This will also throw an exception if it fails
return future.get();
}
So now you can get the result like you originally intended:
...
JSONObject response = network.getRestRequest()
...
But you cannot do this on the main (UI) thread (Android throws an exception if you try to do networking on the main thread. But if you're already doing work on a separate thread, this is fine.
Hope that helps!
You are trying to get results immediately but request is asynchronous you can call request synchronously
synchronized (this) {
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, "", null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) { // basically I just want to return this response
res = response;
YourClassName.this.notify();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}
);
this.wait();
}
But it will lock your thread until request will be finished
i'm sure that you need to do it asynchronous
please read documentation about multi threading in java
The scenario I am trying to complete is the following:
The client submits a HTTP POST request to the servlet.
The servlet sends a response to the client confirming the request has been received.
The servlet then sends an email notification to the system administrator.
So far I am able to complete the following steps in the order described above, however I run into an issue at the end. If the client makes another HTTP request to the same or another servlet while the email notification method EmailUtility.sendNotificationEmail() is still running, the servlet will not run any further code until this email method is completed (I am using javax.mail to send emails if that matters).
I have tried to use AsyncContext to get around this (which I may be using incorrectly), but unfortunately the issue still remains.
How can I make the EmailUtility.sendNotificationEmail() run in a different thread/asynchronously so that the servlets do not have to wait for this method to complete?
This is my code so far:
//Step 1: Client submits POST request to servlet.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
//Step 2: Servlet sends response to client.
response.getWriter().write("Your request has been received");
response.getOutputStream().flush();
response.getOutputStream().close();
//Step 3: Servlet send email notification.
final AsyncContext acontext = request.startAsync();
acontext.start(new Runnable() {
public void run() {
EmailUtility.sendNotificationEmail();
acontext.complete();
}
});
}
Try something simple, like a thread:
new Thread(new Runnable()
{
#Override
public void run()
{
EmailUtility.sendNotificationEmail();
}
}, "Send E-mail").start();
So I resolved the issue by using ExecutorService as follows:
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
public void run() {
EmailUtility.sendNotificationEmail();
}
});
executorService.shutdown();
I'm unable to figure out how to return a String value from RequestBuilder's sendRequest() method after receiving a response. I referred to a similar question where the suggestion was to use Callback<String, String> callback but I can't figure out how to implement this. The GWT documentation for Callback does not have any examples.
What I have is a class Requester with the method generateRequest() that should make a request with RequestBuilder and return a String when called. The processResponse() method takes the response, parses it and returns a String which I'm storing in output. How can I return this output String when generateRequest() is called from another class?
public String generateRequest() {
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url.getUrl()));
builder.setHeader("Authorization", authHeader);
String title = null;
try {
builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
GWT.log(exception.getMessage());
}
public void onResponseReceived(Request request, Response response) {
String output = processResponse(response);
}
});
} catch (RequestException e) {
GWT.log(e.getMessage());
}
return title;
}
I think you might be misunderstanding something.
You cannot simply return a String because the call is async (i.e. you can't return the String because the String is simply not available yet, at the time you wish to return it).
You could simply wait there until the call result is ready, but this is really terrible practice; that's why you're seeing people suggesting callbacks.
Imagine, this is the code:
statement01;
statement02;
String result = generateRequest(...);
statement04UsingResult;
statement05;
then this will not work, because result will not be available before statement04UsingResult is executed. The Request has not finished yet. (as Andrej already mentioned)
To solve this, split your code:
statement01;
statement02;
generateRequest(...);
create an new void method, wich accepts the result as parameter:
public void newMethod(String result) {
statement04UsingResult;
statement05;
}
and call from inside the onResponseRevieve-method:
builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
GWT.log(exception.getMessage());
}
public void onResponseReceived(Request request, Response response) {
newMethod(processResponse(response));
}
});
Hope that helps.