java HttpClient asynchronous error handling - java

I want to use Java Http Client: java.net.http.HttpClient in asynchronous way.
When I receive http response (any http code) or timeout I want to execute some custom Java code. I struggle to complete the body of error handling.
CompletableFuture<HttpResponse<String>> response =
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApplyAsync(body -> {
System.out.println("Request finished");
return body;
})
.exceptionallyAsync(exception -> {
// WHAT TO RETURN HERE ?
});
The method: exceptionallyAsync returns: CompletableFuture<T>, but I do not know how to complete this method.
Can you please help me to finish this method? I cannot find example in GitHub or Google.

You can use
.exceptionally(this::handlError)
If this is your response handler with a method like:
private Void handlError(Throwable ex) {
System.out.println(ex.toString());
return null;
}

Related

Spring ResponseBodyEmitter doesn't set Content-Type header in a response

I'm writing a kind of a wrapper around my request handlers to make them stream HTTP response. What I've got now is
Handler response wrapper:
#Component
public class ResponseBodyEmitterProcessor {
public ResponseBodyEmitter process(Supplier<?> supplier) {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
Executors.newSingleThreadExecutor()
.execute(() -> {
CompletableFuture<?> future = CompletableFuture.supplyAsync(supplier)
.thenAccept(result -> {
try {
emitter.send(result, MediaType.APPLICATION_JSON_UTF8);
emitter.complete();
} catch (IOException e) {
emitter.completeWithError(e);
}
});
while (!future.isDone()) {
try {
emitter.send("", MediaType.APPLICATION_JSON_UTF8);
Thread.sleep(500);
} catch (IOException | InterruptedException e) {
emitter.completeWithError(e);
}
}
});
return emitter;
}
}
Controller:
#RestController
#RequestMapping("/something")
public class MyController extends AbstractController {
#GetMapping(value = "/anything")
public ResponseEntity<ResponseBodyEmitter> getAnything() {
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(process(() -> {
//long operation
}));
}
What I'm doing is just send empty string every half a second to keep a request alive. It's required for some tool to not shut it down by timeout. The problem here that I don't see any Content-Type header in a response. There's nothing at all, despite I return ResponseEntity from my controller method as it's said in this thread:
https://github.com/spring-projects/spring-framework/issues/18518
Looks like only TEXT_HTML media type is acceptable for streaming. Isn't there a way to stream json at all? I even manually mapped my dtos to json string using objectMapper, but still no luck. Tried with APPLICATION_JSON and APPLICATION_STREAM_JSON - doesn't work. Tried in different browsers - the same result.
I also manually set Content-Type header for ResponseEntity in the controller. Now there's the header in a response, but I'm not sure, if my data is actually streamed. In Chrome I can only see the result of my operation, not intermediate chars that I'm sending (changed them to "a" for test).
I checked the timing of request processing for two options:
Without emitter (just usual controller handler)
With emitter
As I understand Waiting status means: "Waiting for the first byte to appear". Seems like with emitter the first byte appears much earlier - this looks like what I need. Can I consider it as a proof that my solution works?
Maybe there's another way to do it in Spring? What I need is just to notify the browser that a request is still being processed by sending some useless data to it until the actual operation is done - then return the result.
Any help would be really appreciated.
Looking at the source of ResponseBodyEmitter#send it seems that the specified MediaType should have been set in the AbstractHttpMessageConverter#addDefaultHeaders method but only when no other contentType header is already present.
protected void addDefaultHeaders(HttpHeaders headers, T t, MediaType contentType) throws IOException{
if (headers.getContentType() == null) {
MediaType contentTypeToUse = contentType;
// ...
if (contentTypeToUse != null) {
headers.setContentType(contentTypeToUse);
}
}
// ...
}
I would suggest to set a break point there and have a look why the header is not applied. Maybe the #RestController sets a default header.
As a workaround you could try the set the contentType header via an annotation in the MVC controller.
E.g.
#RequestMapping(value = "/something", produces = MediaType.APPLICATION_JSON_UTF8)

How to wait for the HTTP request to get completed in java

I am writing Java code where i am downloading the file from a server and i have to copy the file in my local system when the file download is complete.
I am using the below code:-
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client = builder.readTimeout(600, TimeUnit.SECONDS).writeTimeout(600, TimeUnit.SECONDS)
.connectTimeout(600, TimeUnit.SECONDS).build();
Request downloadRequest = new Request.Builder().url(url + fileName).addHeader("cache-control", "no-cache")
.addHeader("Authorization", token).build();
try {
Response downloadResponse = client.newCall(downloadRequest).execute();
System.out.println(downloadResponse.message());
System.out.println("got response from blob " + downloadResponse.isSuccessful() + " " + fileName);
return downloadResponse;
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
But the request is made asynchronously and before the request is completed then response is returned which is incomplete. Can anyone please help me how can i make a request and wait till the response is completed.
Any help is highly appreciated!
Looks like you're returning the response object (not the response body content).
try something like:
return downloadedResponse.body().string()
My experience with HttpClient is such that the headers return first. The content doesn't necessarily come across the wire unless/until you consume it.
To make a synchronous GET request we need to build a Request object based on a URL and make a Call. After its execution we get back an instance of Response:
#Test
public void whenGetRequest_thenCorrect() throws IOException {
Request request = new Request.Builder()
.url(BASE_URL + "/date")
.build();
Call call = client.newCall(request);
Response response = call.execute();
assertThat(response.code(), equalTo(200));
}
You are already using synchronous method calling.
client.newCall(downloadRequest).execute();
This is a synchronous way of requesting URL. If you want to do the aysynchronous call you need to use enqueue method of Call class.
call.enqueue(new Callback() {
public void onResponse(Call call, Response response)
throws IOException {
// ...
}
public void onFailure(Call call, IOException e) {
fail();
}
});
I think problem is somewhere else. Kindly give more details why you are suspecting the current one as an asynchronous call so that we can do RCA.

JAX RS Client: Catch exception message while making a REST call

I'm using ResteasyClient to make a REST client to my another service A. Say, service A throws an exception CustomException with a message : Got Invalid request.
Here is how I am using the Client:
public Response callServiceA(final String query) {
ResteasyClient client = new ResteasyClientBuilder().build();
String link = "abc.com/serviceA";
ResteasyWebTarget target = client.target(link).path("call");
Form form = new Form();
form.param("query", query);
Response response;
try {
String data =
target.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(form,
MediaType.APPLICATION_FORM_URLENCODED_TYPE),
String.class);
response = Response.ok().entity(data).build();
} catch (Exception e) {
String message = e.getMessage();
e.printStackTrace();
response = Response.serverError().entity(e.getMessage()).build();
} finally{
client.close();
}
return response;
}
However, when I print the stacktrace, I'm unable to find my custom error message. I can just see the message as HTTP 400 Bad Request.
Could you please suggest me how to access the error message?
NOTE: I am able to get the error message when I call the serviceA using the restClient. Hence, I dont think there is an issue with the service.
Don't deserialize the response straight to a String.
String data = ...
.post(Entity.entity(form,
MediaType.APPLICATION_FORM_URLENCODED_TYPE),
String.class);
When you do this (and there is a problem), you just get a client side exception, which doesn't carry information about the response. Instead just get the Response with the overloaded post method
Response response = ...
.post(Entity.entity(form,
MediaType.APPLICATION_FORM_URLENCODED_TYPE));
Then you can get details from the Response, like the status and such. You can get the body with response.readEntity(String.class). This way you don't need to handle any exceptions. Just handle conditions based on the status.
Do note though that the Response above is an inbound Response, which is different from the outbound Response in your current code. So just make sure not to try and send out an inbound Response.
Also see this answer and it's comments for some design ideas.

Using RxJava and Okhttp

I want to request to a url using okhttp in another thread (like IO thread) and get Response in the Android main thread, But I don't know how to create an Observable.
It's easier and safer to use Observable.defer() instead of Observable.create():
final OkHttpClient client = new OkHttpClient();
Observable.defer(new Func0<Observable<Response>>() {
#Override public Observable<Response> call() {
try {
Response response = client.newCall(new Request.Builder().url("your url").build()).execute();
return Observable.just(response);
} catch (IOException e) {
return Observable.error(e);
}
}
});
That way unsubscription and backpressure are handled for you. Here's a great post by Dan Lew about create() and defer().
If you wished to go the Observable.create() route then it should look more like in this library with isUnsubscribed() calls sprinkled everywhere. And I believe this still doesn't handle backpressure.
I realise this post is a bit old, but there's a new and more convenient way of doing this now
Observable.fromCallable {
client.newCall(Request.Builder().url("your url").build()).execute()
}
More info: https://artemzin.com/blog/rxjava-defer-execution-of-function-via-fromcallable/
I came late to the discussion but, if for some reason the code need to stream the response body, then defer or fromCallable won't do it. Instead one can employ the using operator.
Single.using(() -> okHttpClient.newCall(okRequest).execute(), // 1
response -> { // 2
...
return Single.just((Consumer<OutputStream>) fileOutput -> {
try (InputStream upstreamResponseStream = response.body().byteStream();
OutputStream fileOutput = responseBodyOutput) {
ByteStreams.copy(upstreamResponseStream, output);
}
});
},
Response::close, // 3
false) // 4
.subscribeOn(Schedulers.io()) // 5
.subscribe(copier -> copier.accept(...), // 6
throwable -> ...); // 7
The first lambda executes the response after upon subscription.
The second lambda creates the observable type, here with Single.just(...)
The third lambda disposes the response. With defer one could have used the try-with-resources style.
Set the eager toggle to false to make the disposer called after the terminal event, i.e. after the subscription consumer has been executed.
Of course make the thing happen on another threadpool
Here's the lambda that will consume the response body. Without eager set to false, the code will raise an IOException with reason 'closed' because the response will be already closed before entering this lambda.
The onError lambda should handle exceptions, especially the IOException that cannot be anymore caught with the using operator as it was possible with a try/catch with defer.
Okhttp3 with RxSingle background API call.
Disposable disposables = Single.fromCallable(() -> {
Log.e(TAG, "clearData: Thread[" + Thread.currentThread().getName() + "]");
OkHttpClient client = Util.getHttpClient();
Request request = new Request.Builder()
.addHeader("Authorization", "Bearer " + Util.getUserToken())
.url(BuildConfig.BASE_URL + ApiConstants.DELETE_FEEDS)
.build();
Response response = client.newCall(request).execute();
if(response.isSuccessful()) {
...
return ; // Any type
} else {
return ; // Any type
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((result) -> {
Log.d(TAG, "api() completed");
});
compositeDisposable.add(disposables);

how to integrate AJAX and Java EE servlet 3 async response

I am thinking of how to make AJAX take use of servlet 3 async response. in the request-response synchronize processing model, when the response comes back, the callback of XmlHttpRequest can get the response text; but how about the response is processed in another thread, and returns some message, what will the XHR get when the request ends? can it still get the response body? I tried a simple codes to test it, it seems failed to get the response;
I can understand this, when the AJAX request return, there is nothing in the response, it will be delayed in another server thread, so the callback get nothing.
But I wonder is there any way to let AJAX get the correct response?
I am afraid I made a mistake before, that I forget to call asycContext.complete() after the async processing is done. after complete() is called, ajax get the response. However, if the processing lasts longer than the timeout setting, just like following, a exception saying illegal state of the asynccontext will arise, and client get nothing:
final AsyncContext ac = request.startAsync();
ac.setTimeout(1000);
Executors.newSingleThreadExecutor().execute(new Runnable(){
#Override
public void run() {
PrintWriter pw;
try {
Thread.sleep(2000);
pw = ac.getResponse().getWriter();
pw.write("Hello, World!");
ac.complete();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
So I guess the keys here are: 1. call complete after processing is done; 2. set a appropriate timeout;

Categories

Resources