I have a java android app with I am using retrofit/rxjava2 to make multiple requests to my api. I want to wait until all the requests have completed then return the responses. So far I am able to create the requests but I can not get any responses. The code only does the first request.
I used this tutorial, https://github.com/fakefacebook/Retrofit-2-with-Rxjava-multiple-request. But it still doesn't work....
public static void checkDB(List<String> list) {
if (list.isEmpty()) {
//no tags to check
} else {
System.out.println("list is not empty");
MyAPIendpoint apiService = RetrofitAPI.getClient().create(MyAPIendpoint.class);
List<Observable<Bmwvehiclemain2>> requests = new ArrayList<>();
for (String t : list) {
requests.add(apiService.getVehByRfidtag(t));
}
Observable.zip(requests, new Function<Object[], List<Bmwvehiclemain2>>() {
#Override
public List<Bmwvehiclemain2> apply(Object[] objects) throws Exception {
List<Bmwvehiclemain2> newList = new ArrayList<>();
for (Object response : objects) {
newList.add((Bmwvehiclemain2) response);
}
return newList;
}
}).subscribe(
new Consumer<List<Bmwvehiclemain2>>() {
#Override
public void accept(List<Bmwvehiclemain2> newList) throws Exception {
}
},
new Consumer<Throwable>() {
#Override
public void accept(Throwable e) throws Exception {
}
});
}
}
It will only do the first request.
Related
I have a list of objects and for each object, I need to hit external API using the RestTemplate. To do that I am using the for loop with try-catch block. In case external API does respond with 4xx, 5xx status code I need to create the list of errors and throw the custom exception which is handled using the exception handler to send client-friendly messages which also send the email notifications. The requirement is to remove the try-catch block and hit the external API in a loop and create the list of errors and check if the error list is not empty throw the exception and send all the error messages at once in the email notification using the exception handler method handleApplicationErrors. But I believe when any exception will occur for loop will be a break and I will not be able to create a list of error messages without a try-catch block, is there any possible way to do it?
public void method() {
List<Objects> objects = fetchObjects();
List<String> errorList = new ArrayList();
for(int i=0;i<objects.size();i++) {
try{
hitExternalApi(object)
}
catch(Exception e){
errorList.add("Error Message")
}
}
if(!errorList.isEmpty()) {
throw new ErrorDTO(Status.BAD_REQUEST, errorList);
}
}
#Override
public void hitExternalApi(Object object) {
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> request = new HttpEntity<>(object, httpHeaders);
restTemplate.exchange(url, HttpMethod.POST, request, Void.class);
}
#ExceptionHandler(ErrorDTO.class)
public ResponseEntity<Problem> handleApplicationErrors(NativeWebRequest request, ErrorDTO error) {
notificationService.sendNotification(error.getErrors());
Problem problem =
Problem.builder()
.withStatus(error.getStatus())
.withTitle(error.getMessage())
.withDetail(error.getErrors().toString())
.build();
return create(error, problem, request);
}
you can put the for loop inside a function and and call the exception after calling that function.
refer the following example for a better understanding:
BEFORE
public class ExceptionInLoop{
public static void sampleMethod(){
String str[] = {"Mango", "Apple", "Banana", "Grapes", "Oranges"};
try {
for(int i=0; i<=10; i++) {
System.out.println(str[i]);
System.out.println(i);
}
}catch (ArrayIndexOutOfBoundsException ex){
System.out.println("Exception occurred");
}
System.out.println("hello");
}
public static void main(String args[]) {
sampleMethod();
}}
AFTER
public class ExceptionInLoop{
public static void print(String str) {
System.out.println(str);
}
public static void sampleMethod()throws ArrayIndexOutOfBoundsException {
String str[] = {"Mango", "Apple", "Banana", "Grapes", "Oranges"};
for(int i=0; i<=10; i++) {
try {
print(str[i]);
System.out.println(i);
} catch(Exception e){
System.out.println(i);
}
}
}
public static void main(String args[]) {
try{
sampleMethod();
}catch(ArrayIndexOutOfBoundsException e) {
System.out.println("");
}
}}
You should be able to solve what you are asking for by moving the try-catch block down to the hitExternalApi method and let this return a boolean or a DTO if you need to pass any information. The for-loop can then just check the return value of hitExternalApi and populate the errorList if needed.
Modified code example:
public void method() {
List<Objects> objects = fetchObjects();
List<String> errorList = new ArrayList();
for (int i = 0; i < objects.size(); i++) {
if (!hitExternalApi(object)) {
errorList.add("Error Message");
}
}
if (!errorList.isEmpty()) {
throw new ErrorDTO(Status.BAD_REQUEST, errorList);
}
}
public boolean hitExternalApi(Object object) {
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> request = new HttpEntity<>(object, httpHeaders);
try {
restTemplate.setErrorHandler();
restTemplate.exchange(url, HttpMethod.POST, request, Void.class);
return true;
} catch (Exception exception) {
return false;
}
}
Per default, (so when using the DefaultResponseErrorHandler), the invocation of restTemplate.exchange(...) will throw some kind of RestClientException for 4xx and 5xx responses, so you will either need to handle this exception somewhere or provide your own ResponseErrorHandler and build logic around that.
You can just create an extra object for controlling the flow
public class ApiResponsePojo {
private HttpStatus status;
private String message;
private String data;
}
You can use a class like this and modify it to the way you want to store the messages. you can check if there's any error through status and populate a message in the status
I have multiple requests (upload files) into Observable and I want to execute them in parallel. The code is:
private void myMethod(List<String> filePathsList) {
List<Observable<String>> observables = new ArrayList<>();
for (String filePath : filePathsList) {
MultipartBody.Part multipartFile = getMultipartFile("some_file_name", filePath);
//here I'm just creating request from Retrofit restclient - the problem can't be here ;)
Observable<String> fileUploadObservable = UploadsRestClient.get().sendFile(multipartFile, "another_post_param");
observables.add(fileUploadObservable);
}
Observable<String> combinedObservable = Observable.zip(observables, new FuncN<String>() {
#Override
public String call(Object... args) {
return null;
}
});
combinedObservable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
//called at the end
}
#Override
public void onError(Throwable throwable) {
//called if error occurs
}
#Override
public void onNext(String string) {
//should be called foreach request, but it's called only after the last one
}
});
}
The problem is that onNext() is called only after the last call is done, before onCompleted(),how can I get triggered after each request?
I'm new on Android development and I'm learning how to use MVP pattern correctly in recently.
Now I'm facing a tricky problem, hope can get some helpful suggestion or solution from here.
First, here is my presenter
public class MVPPresenter {
private MVPView mvpView;
public MVPPresenter(MVPView mvpView) {
this.mvpView = mvpView;
}
public void loadData() {
mvpView.startLoading();
final List<MVPModel> list = new ArrayList<>();
//the part that I trying to extract starts here.
Call call = DataRetriever.getDataByGet(URLCombiner.GET_FRONT_PAGE_ITEMS);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
mvpView.errorLoading();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
try {
JSONObject result = new JSONObject(response.body().string());
int errorCode = result.getInt("ErrorCode");
if (errorCode == 0) {
JSONArray value = result.getJSONObject("Value").getJSONArray("hot");
for (int i = 0; i < value.length(); i++) {
MVPModel mvpModel = new MVPModel();
String name = null;
String image = null;
try {
name = value.getJSONObject(i).getString("title");
image = URLCombiner.IP + value.getJSONObject(i).getString("pic");
} catch (JSONException e) {
e.printStackTrace();
}
mvpModel.setName(name);
mvpModel.setImage(image);
list.add(mvpModel);
}
if (list.size() > 0) {
mvpView.successLoading(list);
mvpView.finishLoading();
} else {
mvpView.errorLoading();
}
} else {
mvpView.errorLoading();
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
mvpView.errorLoading();
}
}
});
//the part that I trying to extract ends here.
}
}
As you can see, I'm trying to extract the part which is using OKHttp libs into a class (I call it data manager) which I hope it can handle every task between server and client. But here's the thing, when I trying to pass the result from the data manager to presenter, I got NullPointException because of the async mechanism.
I would like to know how to passing the data, which is come from server in async, to the presenter when the data has finish downloading.
And here's my ideal data manager, I know this might looks stupid but I think this can make my problem more clearly.
public class LoadServerData {
private static JSONArray arrayData = new JSONArray();
public static JSONArray getServerData() {
Call call = DataRetriever.getDataByGet(URLCombiner.GET_FRONT_PAGE_ITEMS);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
try {
JSONObject result = new JSONObject(response.body().string());
int errorCode = result.getInt("ErrorCode");
if (errorCode == 0) {
arrayData = result.getJSONObject("Value").getJSONArray("hot"); //the data I would like to return.
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});
return arrayData; //this is gonna be an empty data.
}
}
I've reading some article that might can solve my problem, but still not getting any fine answer. Perhaps I've using wrong keyword I think. Hopes you guys can give me some ideas or solutions to help me or inspire me.
P.S. version of OKhttp libs is 3.7.0
Create a simple Listener so it can be called whenever the server call finishes:
public class LoadServerData {
public static interface Listener {
public void onSuccess(JSONArray data);
public void onError(Exception error);
}
public static void getServerData(Listener listener) {
Call call = DataRetriever.getDataByGet(URLCombiner.GET_FRONT_PAGE_ITEMS);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
listener.onError(e);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
try {
JSONObject result = new JSONObject(response.body().string());
int errorCode = result.getInt("ErrorCode");
if (errorCode == 0) {
JSONArray arrayData = result.getJSONObject("Value").getJSONArray("hot"); //the data I would like to return.
listener.onSuccess(arrayData);
} else {
listener.onError(...);
}
} catch (JSONException e) {
e.printStackTrace();
listener.onError(e);
}
} else {
listener.onError(...);
}
}
});
}
}
I am trying to make two methods generator and consumer to run synchronously.I have tried queue but unable to achieve.
I am using the below code
#Override
protected List<ConfigIssue> init() {
client = new RtmClientBuilder(endpoint, appkey)
.setListener(new RtmClientAdapter() {
})
.build();
SubscriptionAdapter listener = new SubscriptionAdapter() {
#Override
public void onSubscriptionData(SubscriptionData data) {
for (AnyJson json : data.getMessages()) {
queue.add(json);
}
}
};
client.createSubscription(channel, SubscriptionMode.SIMPLE, listener);
client.start();
return super.init();
}
#Override
public String produce(String lastSourceOffset, int maxBatchSize, BatchMaker batchMaker) throws StageException {
long nextSourceOffset = 0;
if (lastSourceOffset != null) {
nextSourceOffset = Long.parseLong(lastSourceOffset);
}
if (messageQueue.size() != 0) {
for (AnyJson json : queue) {
Record record = getContext().createRecord("some-id::" + nextSourceOffset);
Map<String, Field> map = new HashMap<>();
map.put("fieldName", Field.create(json.toString()));
record.set(Field.create(map));
batchMaker.addRecord(record);
++nextSourceOffset;
messageQueue.remove();
}
}
return String.valueOf(nextSourceOffset);
}
I am trying to Sync both init method and produce method.Since init method start first and produce method start later they are not in sync.Can it be achieved by multi threading?How?
I am facing a problem,I can't store the handler result into Json Array.Every time the array is empty . I tried to use Future but it still the same problem, here is my code :
static void getCaraReferTypeDocBin(RoutingContext routingContext){
String ids = routingContext.request().getParam("ids");
logger.debug(ids);
String[] idsArray = ids.split(",");
JsonArray caraRefTypeDocBin = new JsonArray();
for (int i = 0; i <idsArray.length ; i++) {
GET.getCaraTypeDocBin(Integer.parseInt(idsArray[i]), res->{
if (res.succeeded()){
logger.debug(res.result());
caraRefTypeDocBin.add(res.result());
}else{
logger.debug(res.cause().getMessage());
}
});
}
logger.debug(caraRefTypeDocBin);
}
this is getCaraReferTypeDocBin implementation :
public static void getCaraTypeDocBin(int id ,Handler<AsyncResult<JsonArray>> resultHandler) {
JsonArray pIn = new JsonArray();
pIn.add(new JsonObject().put("pos", 2).put("type", OracleTypes.NUMBER).put("val", id));
JsonArray pOut = new JsonArray().add(new JsonObject().put("pos", 1).put("type", OracleTypes.CURSOR));
DB.cursor(SQL.LIST_CARA_TYPE_DOC_BIN,pIn,pOut, res -> {
if (res.succeeded()) {
try {
resultHandler.handle(Future.succeededFuture(res.result().getJsonArray("1")));
}catch (Exception e){
logger.error(e);
resultHandler.handle(Future.failedFuture(Error.ERROR_OCCURED.toString()));
}
} else {
resultHandler.handle(Future.failedFuture(res.cause().getMessage()));
}
});
}
In async systems api with futures should be written something like this:
private Future<String> loadFromDb() {
Future<String> f = Future.future();
//some internal loading from db
String result = "fromDb";
//when loading completes, pass it to future result
f.complete(result);
return f;
}
And how it uses:
private void handleSo(RoutingContext routingContext) {
loadFromDb()
.map(new Function<String, JsonArray>() {
#Override
public JsonArray apply(String fromDb) {
//map to json
return new JsonArray(...);
}
})
.setHandler(
new Handler<AsyncResult<JsonArray>>() {
#Override
public void handle(AsyncResult<JsonArray> result) {
routingContext.response().end(result.result().toString());
}
}
);
}
You are using futures wrong. You example simple and you haven't async chains (where result calculates based on previous result etc), so instead futures, you can simple use callback:
private void loadFromDb(Handler<String> handler) {
String result = "fromDb";
handler.handle(result);
}
private void handleSo(RoutingContext routingContext) {
loadFromDb(new Handler<String>() {
#Override
public void handle(String fromDb) {
routingContext.response().end(new JsonArray(...).toString());
}
});
}
Upd You need collect results from multiple async calls doc. Don't know how to implement it with you callback style api. But with futures it's not problem:
private void handleSo(RoutingContext routingContext) {
List<Future> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
//make 10 async calls
futures.add(loadFromDb()
.map(new Function<String, JsonObject>() {
#Override
public JsonObject apply(String fromDb) {
return new JsonObject().put("data", fromDb);
}
}));
}
CompositeFuture.all(futures)
.map(new Function<CompositeFuture, JsonArray>() {
#Override
public JsonArray apply(CompositeFuture compositeFuture) {
JsonArray array = new JsonArray();
List<JsonObject> jsons = compositeFuture.result().list();
jsons.forEach(jsonObject -> array.add(jsonObject));
return array;
}
})
.setHandler(new Handler<AsyncResult<JsonArray>>() {
#Override
public void handle(AsyncResult<JsonArray> res) {
routingContext.response().end(res.result().toString());
}
});
}
GET.getCaraTypeDocBin() runs asynchronously, right? And I assume there's something time-consuming in there like hitting a remote API? So the loop will run in milliseconds, kicking off all the requests, and then the "logger.debugs" will happen, and only then will the callbacks start to do caraRefTypeDocBin.add(res.result());
If I'm right, you should see the results logged before the empty array.