I have a method:
public void getVmsAdminToken(HttpClient httpClient, handler<AsyncResult<String>> handler) {
httpClient.postAbs(url, h -> h.bodyHandler(bh -> {
try {
switch (h.statusCode()) {
case 200:
JsonObject vmsResponse = bh.toJsonObject();
handler.handle(Future.succeededFuture(Json.encode(vmsResponse)));
break;
default:
LOG.error("VMS call failed {}", h.statusCode());
handler.handle(Future.failedFuture(500 + ""));
break;
}
} catch (Throwable t) {
LOG.error("Exception in getVmsAdminToken", t);
handler.handle(Future.failedFuture(500 + ""));
}
}))
.setTimeout(timeOutMs)
.putHeader("content-type", "application/json")
.putHeader("stub", apiKey)
.end(vehicleReqBody.encode());
}
I use this inside the following method call :
private void getAdminToken(RoutingContext ctx, RedisFleetStorage storage, HttpClient httpClient) {
getVmsAdminToken(fleetId, user, vehicle, httpClient, replyVms -> {
if (reply.succeeded()) {
// why succeeded?!!
}
});
}
And even if the getVmsToken fails, the execution falls into the if (reply.succeeded())
Why might that be?
You should check the same AsyncResult object being the result of your HTTP call:
private void getAdminToken(RoutingContext ctx, RedisFleetStorage storage, HttpClient httpClient) {
getVmsAdminToken(fleetId, user, vehicle, httpClient, replyVms -> {
if (replyVms.succeeded()) {
// do you thing
}
});
}
Related
Is there any way to handle exceptions in a Flux parallel in case N of X rails fail? I have tried with the onErrorMap, onErrorReturn, and with this try catch, but it keeps throwing error even if all the others are ok, because it is going to the catch of the processRequest method.
protected Object processRequest(RequestHolder requestHolder) {
RequestHolderImpl requestHolderImpl = (RequestHolderImpl) requestHolder;
try {
if (requestHolderImpl.getPayload().getClass().equals(LinkedList.class)) {
payload.addAll((List<DataSourceRequest>) requestHolderImpl.getPayload());
} else {
payload.add((DataSourceRequest) requestHolderImpl.getPayload());
}
List<PurposeResponse> response = Flux.fromIterable(payload)
.parallel()
.flatMap(request -> {
try {
return dataSourceCall(request);
} catch (WebClientResponseException e) {
return Mono.just(new PurposeResponse(request.getPurpose(), buildResponseFromException(e, request.getPurpose())));
} catch (Exception e) {
LOGGER.error("No response could be obtained from DS. Exception thrown: {}", e.getMessage());
return Mono.just(new PurposeResponse(request.getPurpose(), new DataSourceException(e)));
}
})
.sequential()
.collectList()
.block();
return new ResponseHolderImpl(response, products);
} catch (Exception e) {
return new DataSourceException(e.getMessage());
}
}
private Mono<PurposeResponse> dataSourceCall(DataSourceRequest purpose) {
RequestHolder requestHolder = new RequestHolderImpl(purpose,
data,
products,
token);
String purposeName = getPurposeName(requestHolder);
RequestEntity<?> requestEntity = createRequestEntity(requestHolder);
LOGGER.info("Sending request to this url: {}", requestEntity.getUrl());
return webClient.get()
.uri(requestEntity.getUrl())
.header("Authorization", "Bearer " + token)
.retrieve()
.bodyToMono(JsonNode.class)
.elapsed()
.map(data -> {
LOGGER.info("Response took {} milliseconds", data.getT1());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Response obtained from Data Source: {}", data.getT2());
}
return new PurposeResponse(purposeName, data.getT2());
});
}
private Object buildResponseFromException(WebClientResponseException e, String purposeName) {
//do things
}
I am trying to send data over event bus when got the call from client and response back to client.
Everything is working fine until i add the interceptor on event bus..
Here is the Code:-
public class TestVerticle extends AbstractVerticle {
#Override
public void start(Promise<Void> promise) {
Router router = Router.router(vertx);
vertx.exceptionHandler(globalExceptionHandler -> {
System.out.println("Exception not handled in application : " + globalExceptionHandler.getCause());
});
vertx.eventBus().consumer("test", handler -> {
System.out.println("Message receive form event bus : " + handler.body().toString());
handler.reply(handler.body().toString());
});
router.get().handler(this::rootHandler);
vertx.createHttpServer().requestHandler(router).listen(8080, resultHandler -> {
if (resultHandler.succeeded()) {
promise.complete();
} else {
promise.fail(resultHandler.cause());
}
});
vertx.eventBus().addOutboundInterceptor(handler -> {
System.out.println("Outbound data : "+handler.body().toString());
});
// vertx.eventBus().addInboundInterceptor(handler -> {
// System.out.println("Inbound data : " + handler.body().toString());
// });
}
private void rootHandler(RoutingContext routingContext) {
JsonObject msg = new JsonObject().put("path", routingContext.request().path());
vertx.eventBus().request("test","Hello from Application",reply -> this.replyHandler(routingContext,reply));
}
private void replyHandler(RoutingContext ctx, AsyncResult<Message<Object>> reply) {
HttpServerResponse response = ctx.response()
.putHeader("Content-Type", "application/json");
System.out.println("Reply from event bus : " +reply.result().body().toString());
if (reply.succeeded()) {
response.setStatusCode(200)
.setStatusMessage("OK")
.end(reply.result().body().toString());
} else {
response.setStatusCode(500)
.setStatusMessage("Server Error")
.end(new JsonObject().put("error", reply.cause().getLocalizedMessage()).encodePrettily());
}
}
public static void main(String[] args) {
Vertx.vertx().deployVerticle(new TestVerticle(), deploymenHandler -> {
if (deploymenHandler.succeeded()) {
System.out.println("verticle deplyed");
} else {
System.out.println("failed");
}
});
}
#Override
public void stop(Promise<Void> promise) {
System.out.println("Exiting verticle");
promise.complete()
}
}
One more doubt is when I stop the application from IDE the Stop method is not called, but If I undeploy this verticle from another verticle that is working fine.
When you are using eventbus interceptors you will have to call the next() method in the handler otherwise you will get stuck there.
vertx.eventBus().addOutboundInterceptor(handler -> {
System.out.println("Outbound data : "+handler.body().toString());
handler.next();
});
vertx.eventBus().addInboundInterceptor(handler -> {
System.out.println("Inbound data : "+handler.body().toString());
handler.next();
});
LoginResponse and LoginRequest are my model class. and i display error
Response class is from retrofit library and really cant solve this. Rxjava flatmap Func1 to retrofit.Response class.
public Observable<LoginResponse> performLogin(String emailId, String password) {
LoginRequest requestBody = new LoginRequest(emailId,password);
Log.d("LoginRequestBody",requestBody.getLoginId());
return mSamparkService.performLogin(requestBody)
.flatMap(new Func1<Response<LoginResponse>, Observable<LoginResponse>>() {
#Override
public Observable<LoginResponse> call(Response<LoginResponse> response) {
//TODO: remove sensitive debug logs
Timber.d("status code: %s", response.code());
Timber.d("body: %s", response.body());
Timber.d("error body: %s", response.errorBody());
Timber.d("message: %s", response.message());
try {
Log.d(TAG,"in dtl attnd switch case");
switch (response.code()) {
case 200:
return Observable.just(response.body());
case 401:
case 403:
return Observable.error(new UnauthorizedException(response.errorBody().string()));
default:
return Observable.empty();
}
} catch (IOException e) {
Timber.e(e, "error while signIn");
return Observable.error(e);
}
}
});
}
Error:
On the change "SortBy", my program will do a NetworkIO to retrieve the top movies and display them.
However, it seems that though I have done subscribeOn(Schedulers.io()), the NetworkIO MovieDB.getPopular() and MovieDB.getTopRated() in the function call in map are excuted on the main thread and I get a android.os.NetworkOnMainThreadException.
I was wondering how to make the public Movie[] call(SortBy sortBy) asynchronous.
sortObservable.map(new Func1<SortBy, Movie[]>() {
#Override
public Movie[] call(SortBy sortBy) {
try {
switch (sortBy) {
case POPULAR:
return MovieDB.getPopular(); // NETWORK IO
case TOP_RATED:
return MovieDB.getTopRated(); // NETWORK IO
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return new Movie[0];
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Movie[]>() {
#Override
public void call(Movie[] movies) {
imageAdapter.loadData(movies);
}
});
Please check if the below works for you. It uses flatMap instead of map.
sortObservable.flatMap(new Func1<SortBy, Observable<Movie[]>>() {
#Override
public Observable<Movie[]> call(SortBy sortBy) {
try {
switch (sortBy) {
case POPULAR:
return Observable.just(MovieDB.getPopular()); // NETWORK IO
case TOP_RATED:
return Observable.just(MovieDB.getTopRated()); // NETWORK IO
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return Observable.just(new Movie[0]);
}
}).subscribe(new Action1<Movie[]>() {
#Override
public void call(Movie[] movies) {
imageAdapter.loadData(movies);
}
});
From your source code on Github, it seems like you are using synchronous mode of executing requests using OkHttp. OkHttp also supports asynchronous requests and that can be preferred. Below would be the changes required in few of the methods.
run method should consume enqueue instead of execute.
Observable<String> runAsync(String url){
return Observable.create(subscriber -> {
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onResponse(Call call, Response response) throws IOException {
subscriber.onNext(response.body().string());
}
#Override
public void onFailure(Call call, IOException e) {
subscriber.onError(e);
}
});
});
}
getApi can return an Observable<Movie[]> instead of Movie[]
public Observable<Movie[]> getApiAsync(String type){
return runAsync("http://api.themoviedb.org/3/movie/" + type
+ "?api_key=412e9780d02673b7599233b1636a0f0e").flatMap(response -> {
Gson gson = new Gson();
Map<String, Object> map = gson.fromJson(response,
new TypeToken<Map<String, Object>>() {
}.getType());
Movie[] movies = gson.fromJson(gson.toJson(map.get("results")),
Movie[].class);
return Observable.just(movies);
});
}
Finally I sort it out by myself:
sortObservable.flatMap(new Func1<SortBy, Observable<Movie[]>>() {
#Override
public Observable<Movie[]> call(SortBy sortBy) {
switch (sortBy) {
case POPULAR:
return Observable.fromCallable(() -> MovieDB.getPopular()).subscribeOn(Schedulers.io());
case TOP_RATED:
return Observable.fromCallable(() -> MovieDB.getTopRated()).subscribeOn(Schedulers.io());
default:
return Observable.fromCallable(() -> new Movie[0]).subscribeOn(Schedulers.io());
}
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Movie[]>() {
#Override
public void call(Movie[] movies) {
imageAdapter.loadData(movies);
}
});
.subscribe(
new Action1<Response>() {
#Override
public void call(Response response) {
if (response.isSuccess())
//handle success
else
//throw an Throwable(reponse.getMessage())
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
//handle Throwable throw from onNext();
}
}
);
I don't wanna handle (!response.isSuccess()) in onNext(). How can I throw it to onError() and handle with other throwable together?
If FailureException extends RuntimeException, then
.doOnNext(response -> {
if(!response.isSuccess())
throw new FailureException(response.getMessage());
})
.subscribe(
item -> { /* handle success */ },
error -> { /* handle failure */ }
);
This works best if you throw the exception as early as possible, as then you can do retries, alternative responses etc. easily.
you can flatMap your response to Response or Error
flatMap(new Func1<Response, Observable<Response>>() {
#Override
public Observable<Response> call(Response response) {
if(response.isSuccess()){
return Observable.just(response);
} else {
return Observable.error(new Throwable(response.getMessage()));
}
}
})
The solution is to add an operator in the middle. My suggestion is to use map as it does not generate new Observable object (in comparison to flatMap which does it):
.map(new Func1<Response, Response>() {
#Override
public Response call(Response response) {
if (response.isSuccess()) {
return response;
} else {
throw new Throwable(reponse.getMessage()));
}
}
})