Return OkHttp Asynchronous result has some problem - java

I have a java SDK,which use OkHttp client(4.0.0) to get token from IAM server and return token to application.The relation may like this:Applicaiton Sync call SDK,SDK Async call IAM.Refer to this answerJava - Retrieving Result from OkHttp Asynchronous GET,the code like:
The Async Class:
class BaseAsyncResult<T> {
private final CompletableFuture<T> future = new CompletableFuture<>();
T getResult() {
try {
return future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return null;
}
void onFailure(IOException e) {
future.completeExceptionally(e);
}
void onResponse(Response response) throws IOException {
String bodyString = Objects.requireNonNull(response.body()).string();
future.complete(IasClientJsonUtil.json2Pojo(bodyString, new TypeReference<T>() {}));
}
}
Okhttp call like this:
public void invoke(Request request, BaseAsyncResult result) {
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
result.onFailure(e);
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
result.onResponse(response);
}
});
}
The application use sdk code like,iasClient is a wrapper of okhttp client :
BaseAsyncResult<AuthenticationResponse> iasAsyncResult = new BaseAsyncResult();
iasClient.invoke(request, iasAsyncResult);
AuthenticationResponse result = iasAsyncResult.getResult();
The erroe message:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to x.x.x.AuthenticationResponse
What have I missed?

You need to make sure jackson knows which class to deserialize the value to . In this case, you are asking Jackson to deserialize the response to a TypeReference , which will resolve to a Map by default unless you specify the class (in this case, AuthenticationResponse ) . The Future resolves to a linkedHashMap due to this and causes the class cast.
try replacing the below line .
future.complete(IasClientJsonUtil.json2Pojo(bodyString, new TypeReference<T>() {}));
with
future.complete(IasClientJsonUtil.json2Pojo(bodyString, new TypeReference<AuthenticationResponse>() {}));

One method from #Arpan Kanthal is add a private Class type variable to BaseAsyncResult and then use that class in your json2Pojo function,then the BaseAsyncResult may like this:
public class BaseAsyncResult<T> {
private final CompletableFuture<T> future = new CompletableFuture<>();
private Class<T> classType;
public BaseAsyncResult(Class<T> classType) {
this.classType = classType;
}
public T getResult() {
try {
return future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return null;
}
void onFailure(IOException e) {
future.completeExceptionally(e);
}
void onResponse(Response response) throws IOException {
future.complete(JacksonUtil.json2Pojo(response.body().string(), classType));
}
}

Related

Using Callable In ROOM

I am building TODO app with room database and MVVM.
So since I can't use ROOM in main thread,I searched for solutions and came across "Callable" which is just what I need!
Since I have more than 5 functions that make database calls, I wonder how I can use the same Callable code instead of writing it 5 times in different functions.
This is how I currently doing it:
public List<Task> getAllUnCompletedTasksAsList() {
Callable<List<Task>> callable = new Callable<List<Task>>() {
#Override
public List<Task> call() throws Exception {
return appDataBase.taskDao().getAllUnCompletedTasksAsList();
}
};
Future<List<Task>> future = Executors.newSingleThreadExecutor().submit(callable);
try {
return future.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
return null;
}
public List<Task> getCompletedTasksAsList() {
Callable<List<Task>> callable = new Callable<List<Task>>() {
#Override
public List<Task> call() throws Exception {
return appDataBase.taskDao().getCompletedTasksAsList();
}
};
Future<List<Task>> future = Executors.newSingleThreadExecutor().submit(callable);
try {
return future.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
return null;
}
public List<Task> getWeeklyTasksAsList() {
Callable<List<Task>> callable = new Callable<List<Task>>() {
#Override
public List<Task> call() throws Exception {
return appDataBase.taskDao().getWeeklyTasksAsList();
}
};
Future<List<Task>> future = Executors.newSingleThreadExecutor().submit(callable);
try {
return future.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
return null;
}
I would love to hear your suggestions,Thank you !
You are instantiating an anonymous inner class. Do it once outside the methods and use the field instance of your Callable.
private Callable<List<Task>> callable = new Callable<List<Task>>() {
#Override
public List<Task> call() throws Exception {
return appDataBase.taskDao().getAllUnCompletedTasksAsList();
}
};
And (for example)
public List<Task> getAllUnCompletedTasksAsList() {
/*
Callable<List<Task>> callable = new Callable<List<Task>>() {
#Override
public List<Task> call() throws Exception {
return appDataBase.taskDao().getAllUnCompletedTasksAsList();
}
};
*/
Future<List<Task>> future = Executors.newSingleThreadExecutor().submit(this.callable);
try {
return future.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
return null;
}

Java Spring, waiting for AsyncHandlers

apologies for the basic question; I'm new to the Java world and the spring framework. I've built a little example application that makes a bunch of async requests to an external service and returns a list of the responses ('metrics'), but I need to make my application wait until all the responses have come back. Right now I have a (don't hate me) Thread.sleep while I let the results come back, but obviously this is very nasty. Can anyone suggest a better way of architecting this?
Calling class:
#Service
public class MetricService {
#Autowired
private MetricProcessor processor;
private LinkedBlockingQueue<Metric> queue;
#Scheduled(fixedDelay = 60000)
public void queryExternalService() {
List<Metrics> metrics = new ArrayList<>();
metrics = processor.getMetrics();
//this is horrible and I'm a horrible human being
try {
Thread.sleep(10000); //wait for the requests to come back
}
catch (Exception e) {
e.printStackTrace();
}
queue.addAll(metrics);
}
}
Class:
#Component
public class MetricProcessor {
#Autowired
private AsyncClient externalClient;
public List<Metrics> getMetrics() {
List<Metrics> returnObj = new Arraylist<>();
for(Blah blah : bleh) {
Request request = new Request("abc");
externalClient.getMetricAsync(request, new AsyncHandler<request, result>() {
#Override
public void onError(Exception e) {
System.out.println("Error");
}
#Override
public void onSuccess(Request request, Result result) {
returnObj.add(new Metric(result.getKey(), result.getValue()));
}
});
}
return returnObj;
}
}
Any help would be greatly appreciated!
Try a Future.
In MetricService:
public void queryExternalService() {
Future<List<Metrics>> metricsFuture = processor.getMetrics();
try {
queue.addAll(metricsFuture.get(60, TimeUnit.SECONDS));
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
}
So notice instead of the desired List, your processor provides a reference to a Future which may fulfil that request later:
public Future<List<Metrics>> getMetrics() {
MetricsFuture metricsFuture = new MetricsFuture();
// Need to ask for the metrics to be built
metricsFuture.buildMetrics();
return metricsFuture;
}
private static class MetricsFuture extends AbstractFuture<List<Metrics>> {
// Assuming the requests are asynchronous, this should be a thread-safe list
List<Metrics> returnObj = new CopyOnWriteArrayList<>();
void buildMetrics() {
for(Blah blah : bleh) {
final Request request = new Request("abc");
externalClient.getMetricAsync(request, new AsyncHandler<request, result>() {
#Override
public void onError(Exception e) {
onError(request, e);
}
#Override
public void onSuccess(Request request, Result result) {
addMetrics(new Metrics(result.getKey(), result.getValue()));
}
});
}
}
void onError(Request request, Exception e) {
// Is any error a total failure? This allows us to terminate waiting
setException(e); // alternative we could remove request or keep a list of errors
System.out.println("Error");
}
void addMetrics(Metrics metric) {
returnObj.add(metric);
// Once we have received the expected number of results we can pass that prepare that
// as a result of this future.
if(returnObj.size() == bleh.size()) {
set(returnObj);
}
}
}

Android RxJava asynchronous call in map function

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);
}
});

How to include a message in a BadRequestException?

Is it possible to include a message in a BadRequestException so when the user sees a response code a 400, he/she can also figure out why?
The scenario would be something like this, simplified:
public Entity getEntityWithOptions(#PathParam("id") String id, #QueryParam("option") String optValue) {
if (optValue != null) {
// Option is an enum
try {
Option option = Option.valueOf(optValue);
} catch (IllegalArgumentException e) {
throw new BadRequestException(e.getMessage());
}
return new Entity(option);
}
return new Entity();
}
I know this can be done returning a Response object instead, but I wouldn't want that.
Is this possible? Maybe with an ExceptionMapper<BadRequestException>? Or this cannot be done since BadRequestException is already a Jersey-specific exception?
There is a really simple approach to this as shown below.
Response.ResponseBuilder resp_builder=Response.status(Response.Status.BAD_REQUEST);
resp_builder.entity(e.getMessage());//message you need as the body
throw new WebApplicationException(resp_builder.build());
if you need to add headers, response media type or some other functionality, ResponseBuilder provides them all.
You can throw a CustomException and map it to a CustomExceptionMapper to provide customized response.
public class CustomException extends RuntimeException {
public CustomException(Throwable throwable) {
super(throwable);
}
public CustomException(String string, Throwable throwable) {
super(string, throwable);
}
public CustomException(String string) {
super(string);
}
public CustomException() {
super();
}
}
#Provider
public class CustomExceptionMapper implements ExceptionMapper<CustomException> {
private static Logger logger = Logger.getLogger(CustomExceptionMapper.class.getName());
/**
* This constructor is invoked when exception is thrown, after
* resource-method has been invoked. Using #provider.
*/
public CustomExceptionMapper() {
super();
}
/**
* When exception is thrown by the jersey container.This method is invoked
*/
public Response toResponse(CustomException ex) {
logger.log(Level.SEVERE, ex.getMessage(), ex);
Response.ResponseBuilder resp = Response.status(Response.Status.BAD_REQUEST)
.entity(ex.getMessage());
return resp.build();
}
}
Use the CustomException in your code like this.
public Entity getEntityWithOptions(#PathParam("id") String id,
#QueryParam("option") String optValue)
throws CustomException {
if (optValue != null) {
// Option is an enum
try {
Option option = Option.valueOf(optValue);
} catch (IllegalArgumentException e) {
throw new CustomException(e.getMessage(),e);
}
return new Entity(option);
}
return new Entity();
}
Instead of message, you can also construct an object and pass it to mapper through CustomException.
You should create a custom exception like this
public class CustomBadReq extends WebApplicationException {
public CustomBadReq(String message) {
super(Response.status(Response.Status.BAD_REQUEST)
.entity(message).type(MediaType.TEXT_PLAIN).build());
}
}
See also this
You can do it using the BadRequestException(Response response)constructor.
For example:
String msg = e.getMessage();
throw new BadRequestException(Response.status(BAD_REQUEST)
.entity(msg).build());

Functionality for automatic retry after exception

I have made this abstract class to automatically retry network calls if some exception is thrown.
I take care to not retry after InterruptedException &
UnknownHostException.
I retry 5 times. After each failure
I perform an exponential back off, starting from 300ms going upto
1500ms.
public abstract class AutoRetry {
private Object dataToReturn = null;
public Object getDataToReturn() {
return this.dataToReturn;
}
public AutoRetry() {
short retry = -1;
while (retry++ < StaticData.NETWORK_RETRY) {
try {
Thread.sleep(retry * StaticData.NETWORK_CALL_WAIT);
this.dataToReturn = doWork();
break;
} catch (InterruptedException | UnknownHostException e) {
e.printStackTrace();
this.dataToReturn = null;
return;
} catch (IOException e) {
e.printStackTrace();
}
}
}
protected abstract Object doWork() throws IOException;
}
I use it as follows :
final Object dataAfterWork = new AutoRetry() {
#Override
protected Object doWork() throws IOException {
return; //a network call which returns something
}
}.getDataToReturn();
So is this implementation good/correct ?
EDIT
moved to https://codereview.stackexchange.com/questions/87686
This looks pretty good, but I would split the running task from the retry. Also use generics, don't just throw Object about.
Use a Java 8 lambda and the return of the method:
public static <T> Optional<T> doWithRetry(final Supplier<T> t) {
for (int retry = 0; retry <= StaticData.NETWORK_RETRY; ++retry) {
try {
Thread.sleep(retry * StaticData.NETWORK_CALL_WAIT);
return Optional.of(t.get());
} catch (InterruptedException | UnknownHostException e) {
LOGGER.log(Level.SEVERE, "Call failed.", e);
return Optional.empty();
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Call failed. Retry.", e);
}
}
LOGGER.log(Level.SEVERE, "Call failed. Retries exceeded.");
return Optional.empty();
}
Also, use a real logger, not printStackTrace...
Usage:
final String data = doWithRetry(() -> {
//do stuff
});
If your lambda needs to throw an exception, you'll need to define your own #FunctionalInterface:
#FunctionalInterface
interface StuffDoer<T> {
T doStuff() throws Exception;
}
And use that in the method signature, you'll need to handle generic Exception.
Pre-Java 8 usage:
final String data = doWithRetry(new StuffDoer<T>() {
#Override
public T get() throws Exception {
return null;
}
});

Categories

Resources