I am writing functions like these and I have a couple of them. if we see the response handler in both of them are same lines of code.
private void get(JsonObject request, Message<JsonObject> message) {
webClient.get(webClientPort,
webClientHost, "/document/" + request.getString("key"))
.putHeader(HttpHeaders.CONTENT_TYPE.toString(), "application/json")
.send(res -> {
if (res.succeeded()) {
message.reply(res.result().bodyAsString());
} else {
message.fail(0, Json.encode(new JsonObject().put("error", res.cause())));
}
});
}
private void add(JsonObject request, Message<JsonObject> message) {
webClient.post(webClientPort,
webClientHost, "/document/" + request.getString("key"))
.putHeader(HttpHeaders.CONTENT_TYPE.toString(), "application/json")
.sendJson(request.getJsonObject("document").encodePrettily(), res -> {
if (res.succeeded()) {
message.reply(res.result().bodyAsString());
} else {
message.fail(0, Json.encode(new JsonObject().put("error", res.cause())));
}
});
}
is there a way to avoid this duplicate code and move it to a common place?
res -> {
if (res.succeeded()) {
message.reply(res.result().bodyAsString());
} else {
message.fail(0, Json.encode(new JsonObject().put("error", res.cause())));
}
}
Can't you just encapsulate that logic another function and call it via method reference?
private void processResponse(HttpServerResponse res, Message<JsonObject> message) {
if (res.succeeded()) {
message.reply(res.result().bodyAsString());
} else {
message.fail(0, Json.encode(new JsonObject().put("error", res.cause())));
}
}
Then you can just call it with a method reference in both places your code:
.send(res -> this.processResponse(res, message));
and
.sendJson(request.getJsonObject("document").encodePrettily(), res -> this.processResponse(res, message));
I was able to do like this and it worked. Thanks #dovmo for your help. your inputs helped me reached to solution.
private void get(String key, Message<JsonObject> message) {
webClient.get(webClientPort,
webClientHost, "/document/" + key)
.putHeader(HttpHeaders.CONTENT_TYPE.toString(), "application/json")
.send(processResponse(message));
}
private void delete(String key, JsonObject body, Message<JsonObject> message) {
webClient.delete(webClientPort,
webClientHost, "/document/" + key)
.putHeader(HttpHeaders.CONTENT_TYPE.toString(), "application/json")
.send(processResponse(message));
}
private Handler<AsyncResult<HttpResponse<Buffer>>> processResponse( Message<JsonObject> message) {
Handler<AsyncResult<HttpResponse<Buffer>>> handler = reply -> {
if (reply.succeeded()) {
message.reply(reply.result().bodyAsString());
} else {
message.fail(0, Json.encode(new JsonObject().put("error", reply.cause())));
}
};
return handler;
}
Related
I want to do chaining of async calls and get a single callback response when finish.
For example something like this:
public invokeAsyncAPI1 () {
AsyncRabbitTemplate.RabbitConverterFuture<Response1> futureResponse1 = asyncAPI1();
futureResponse1.addCallback {
successCallBack (Response1 result1) {
if(result != OK) {
return immediately with false
} else {
invokeAsyncAPI2(result1);
}
}
failureCallBack () {
return immediately with false
}
}
}
public invokeAsyncAPI2 (result1) {
AsyncRabbitTemplate.RabbitConverterFuture<Response2> futureResponse2 = asyncAPI2();
futureResponse2.addCallback {
successCallBack (Response2 result2) {
if(result != OK) {
return immediately with false
} else {
return true response
}
}
failureCallBack () {
return immediately with false
}
}
}
In the end get result2.get(), if valid retrun ok response.
Yes; it's possible - here's an example:
#SpringBootApplication
public class So69213655Application {
public static void main(String[] args) {
SpringApplication.run(So69213655Application.class, args);
}
#RabbitListener(queues = "service1")
#SendTo
public String service1(String in) {
System.out.println("service1: " + in);
return in.toUpperCase();
}
#RabbitListener(queues = "service2")
#SendTo
public String service2(String in) {
System.out.println("service2: " + in);
return in.toLowerCase();
}
#Bean
AsyncRabbitTemplate template(RabbitTemplate template) {
AsyncRabbitTemplate async = new AsyncRabbitTemplate(template);
return async;
}
#Bean
ApplicationRunner runner(ChainedSender sender) {
return args -> {
System.out.println("And the answer is: " + sender.send("TesT").get(10, TimeUnit.SECONDS));
};
}
}
#Component
class ChainedSender {
private final AsyncRabbitTemplate template;
ChainedSender(AsyncRabbitTemplate template) {
this.template = template;
}
Future<String> send(String out) {
SettableListenableFuture<String> future = new SettableListenableFuture<>();
RabbitConverterFuture<Object> future1 = this.template.convertSendAndReceive("service1", out);
future1.addCallback(result1 -> {
RabbitConverterFuture<Object> future2 = this.template.convertSendAndReceive("service2", result1);
future2.addCallback(result2 -> {
future.set((String) result2);
}, ex -> {
future.setException(ex);
});
}, (ex) -> {
future.setException(ex);
});
return future;
}
}
service1: TesT
service2: TEST
And the answer is: test
I have this bit of code:
public static void main(String[] args) throws UnirestException {
ArrayList<Stock> listStock
= getAllAvailableStocks("https://api.iextrading.com/1.0/ref-data/symbols");
//doing more actions after the one before, using the data from the listStock etc.
}
private static ArrayList<Stock> getAllAvailableStocks(String url) {
ArrayList<Stock> stocks = new ArrayList<Stock>();
Future<HttpResponse<JsonNode>> future = Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
ObjectMapper objectMapper = new ObjectMapper();
try {
listStock = objectMapper.readValue(response.getRawBody(), new TypeReference<List<Stock>>(){});
} catch (Exception e) {
System.out.println("all is fucked");
}
return listStock;
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
}
I am a newbie in java, i want to do the following:
1) I want to do async call to get and extract a list of stocks, only after the request completed i want to do the next things in the main method.
2) How do i extract data from the method i built so i can use the data outside of the method?
3) If i need to do the following:
getAllAvailableStocks("https://api.iextrading.com/1.0/ref-data/symbols",new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
ArrayList<Stock> listStock = new ArrayList<Stock>();
ObjectMapper objectMapper = new ObjectMapper();
int code = response.getStatus();
System.out.println(code);
try {
listStock = objectMapper.readValue(response.getRawBody(), new TypeReference<List<Stock>>(){});
} catch (Exception e) {
}
System.out.println(listStock);
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
}
private static Future<HttpResponse<JsonNode>> getAllAvailableStocks(String url,Callback<JsonNode> cb) {
return Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(cb);
}
Or something of that sort, it makes the code horrible, and when i want to do much more async requests after, i have a callback hell here, is there any way to avoid it? what are my options here?
I think your are mixing up asnychronous and synchronous.
If you
want to do async call to get and extract a list of stocks, only after the request completed I want to do the next things in the main method
then you actually want to perform a synchronous call.
An asynchronous call would be to perform the request, then doing other things (not related to the request) and at some point in the future you get the result of the request and handle it.
To perform a synchronous call, which is probably what you want, try to adapt your code like this:
private static ArrayList<Stock> getAllAvailableStocks(String url) {
ArrayList<Stock> stocks = new ArrayList<Stock>();
Future<HttpResponse<JsonNode>> future = Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
System.out.println("The request succeeded");
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
HttpResponse<JsonNode> response = future.get(); // NOTE: This call is blocking until the request is finished
if (response != null && response.getStatus() == 200) {
JsonNode body = response.getBody();
// TODO Parse body and add items to `stocks`
}
return stocks;
}
This method can be used like this:
ArrayList<Stock> stocks = getAllAvailableStocks(...);
stocks.forEach(x -> System.out.println(x));
Edit
If you want to handle the result asynchronously without providing callbacks, you could use a CompletableFuture. Consider the following snippet as a starting point which does not handle unsuccessful calls.
private static CompletableFuture<ArrayList<Stock>> getAllAvailableStocks(String url) {
CompletableFuture<ArrayList<Stock>> result = new CompletableFuture<>();
Future<HttpResponse<JsonNode>> future = Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
System.out.println("The request succeeded");
ArrayList<Stock> stocks = new ArrayList<Stock>();
if (response != null && response.getStatus() == 200) {
JsonNode body = response.getBody();
// TODO Parse body and add items to `stocks`
}
result.complete(stocks);
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
return result;
}
The method can be used as follows:
CompletableFuture<ArrayList<Stock>> stocksFuture = getAllAvailableStocks(...);
stocksFuture.thenAccept((stocks) -> {
// NOTE: This will be called after and only if the request succeeded
stocks.forEach(x -> System.out.println(x));
});
System.out.println("This is probably executed before the above request finished.");
Thread.sleep(10000); // If you are calling from your `main` method: Prevent JVM exit
I want to replace the following with generics:
for (Post post : postResponse.getResults()) {, where Post can be any POJO.
List<Post> posts = postResponse.getResults(); where List<Post> can be a list of anything I pass into it.
What would my method call and method body look like?
Different examples of method calls:
retrieveData(mCardAdapter, new Post(), Post.class);
retrieveData(mCardAdapter, new Contact(), Contact.class);
retrieveData(mCardAdapter, new Product(), Product.class);
retrieveData(mCardAdapter, new Booking(), Booking.class);
Method:
private void retrieveData(final CardAdapter mCardAdapter, final Object<T> postObject, Class<T> postClass) {
RetrofitService service = ServiceFactory.createRetrofitService(RetrofitService.class, RetrofitService.SERVICE_ENDPOINT);
service.getPosts()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<PostResponse>() {
#Override
public final void onCompleted() {
setRefreshingFalse();
Log.e("RetrofitService", "Retrofit Request Completed!");
}
#Override
public final void onError(Throwable e) {
setRefreshingFalse();
Log.e("RetrofitService", e.getMessage());
}
#Override
public final void onNext(PostResponse postResponse) {
if (postResponse != null) {
Log.e("RetrofitService", "Returned objects: " + postResponse.getResults());
for (postObject post : postResponse.getResults()) {
Log.e("RetrofitService", post.getObjectId() + ": " + post.getText());
}
setRefreshingFalse();
mCardAdapter.clear();
List<postClass> posts = postResponse.getResults();
mCardAdapter.addData(posts);
} else {
Log.e("RetrofitService", "Object returned is null.");
}
}
});
}
I'm getting Unknown class: 'tClass' and Unknown class: 'postClass'. Obviously this is not the way to do it so perhaps treat what I've shown above as pseduo-code. Does it makes sense what I'm trying to? I really want to generify this retrieveData method so that I can be used to query differences classes.
To help with understanding. What I want to avoid:
retrievePosts(mCardAdapter);
retrieveUsers(mCardAdapter);
private void retrievePosts(final CardAdapter mCardAdapter) {
RetrofitService service = ServiceFactory.createRetrofitService(RetrofitService.class, RetrofitService.SERVICE_ENDPOINT);
service.getPosts()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<PostResponse>() {
#Override
public final void onCompleted() {
setRefreshingFalse();
Log.e("RetrofitService", "Retrofit Request Completed!");
}
#Override
public final void onError(Throwable e) {
setRefreshingFalse();
Log.e("RetrofitService", e.getMessage());
}
#Override
public final void onNext(PostResponse postResponse) {
if (postResponse != null) {
Log.e("RetrofitService", "Returned objects: " + postResponse.getResults());
for (Post post : postResponse.getResults()) {
Log.e("RetrofitService", post.getObjectId() + ": " + post.getText());
}
/*for (Post post : postResponse.getResults()) {
mCardAdapter.addData(post);
}*/
setRefreshingFalse();
mCardAdapter.clear();
List<Post> posts = postResponse.getResults();
mCardAdapter.addData(posts);
} else {
Log.e("RetrofitService", "Object returned is null.");
}
}
});
}
private void retrieveUsers(final CardAdapter mCardAdapter) {
RetrofitService service = ServiceFactory.createRetrofitService(RetrofitService.class, RetrofitService.SERVICE_ENDPOINT);
service.getUsers()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<PostResponse>() {
#Override
public final void onCompleted() {
setRefreshingFalse();
Log.e("RetrofitService", "Retrofit Request Completed!");
}
#Override
public final void onError(Throwable e) {
setRefreshingFalse();
Log.e("RetrofitService", e.getMessage());
}
#Override
public final void onNext(PostResponse postResponse) {
if (postResponse != null) {
Log.e("RetrofitService", "Returned objects: " + postResponse.getResults());
for (User user : userResponse.getResults()) {
Log.e("RetrofitService", user.getObjectId() + ": " + user.getText());
}
/*for (Post post : postResponse.getResults()) {
mCardAdapter.addData(post);
}*/
setRefreshingFalse();
mCardAdapter.clear();
List<User> users = userResponse.getResults();
mCardAdapter.addData(users);
} else {
Log.e("RetrofitService", "Object returned is null.");
}
}
});
}
If I understood your question correctly you want to have a generic method for different classes. I did not see that you are adding something to the List so this might work for you.
private <clazz> void retrieveData(final Class<?> clazz) {
for (clazz post : postResponse.getResults()) {
// you can't do anything here since clazz can be anything
}
List<clazz> posts = postResponse.getResults();
}
I listened to this talk
https://www.youtube.com/watch?v=QdmkXL7XikQ&feature=youtu.be&t=274
And eared that I should avoid creating an Observable using the create method, because it doesn't handle unsubscription and backpressure automatically, but I can't find an alternative to use in the code bellow.
compositeSubscription.add(
Observable.create(new Observable.OnSubscribe<DTOCompaniesCallback>() {
#Override
public void call(final Subscriber<? super DTOCompaniesCallback> subscriber) {
modelTrainStrike.getCompaniesFromServer(new CompaniesCallback() {
#Override
public void onResult(DTOCompaniesCallback dtoCompaniesCallback) {
try {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(dtoCompaniesCallback);
subscriber.onCompleted();
}
} catch (Exception e) {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(e);
}
}
}
});
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<DTOCompaniesCallback>() {
#Override
public void call(DTOCompaniesCallback dtoCompaniesCallback) {
Log.i("TAG", "onResult: " + dtoCompaniesCallback.getCompaniesList().size());
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
throw new OnErrorNotImplementedException("Source!", throwable);
}
})
);
And I call clear the CompositeSubscription in the OnDestroy method
#Override
public void onDestroy() {
if (compositeSubscription != null) {
compositeSubscription.clear();
}
}
Do you see any alternative to the create method that I could use here?
Do you see any potential danger or is this approach safe?
Thanks
You can use defer + AsyncSubject:
Observable.defer(() -> {
AsyncSubject<DTOCompaniesCallback> async = AsyncSubject.create();
modelTrainStrike.getCompaniesFromServer(v -> {
async.onNext(v);
async.onComplete();
});
return async;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
...
In case the getCompaniesFromServer supports cancellation, you can:
Observable.defer(() -> {
AsyncSubject<DTOCompaniesCallback> async = AsyncSubject.create();
Closeable c = modelTrainStrike.getCompaniesFromServer(v -> {
async.onNext(v);
async.onComplete();
});
return async.doOnUnsubscribe(() -> {
try { c.close(); } catch (IOException ex) { }
});
})
I was trying to use netflix observable however I managed to do so only synchronously:
This is how I define the remote call:
#Named
public class BroConsumerService {
..
#HystrixCommand(fallbackMethod = "stubbedMethod")
public Observable<String> executeObservableBro(String name) {
return new ObservableResult<String>() {
#Override
public String invoke() {
return executeRemoteService(name);
}
};
}
private String stubbedMethod(String name) {
return "return stubbed";
}
//here I am actually invoking (and observing this method)
#RequestMapping("/executeObservableBro")
public String executeObservableBro(#RequestParam(value = "name", required = false) String name) throws ExecutionException, InterruptedException {
Observable<String> result= broConsumerService.executeObservableBro(name);
result.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.out.println("completed");
}
#Override
public void onError(Throwable e) {
System.out.printf(e.getMessage());
}
#Override
public void onNext(String s) {
System.out.println("on next..");
}
});
}
But that works synchronously. I want to be able to "listen" to the executeObservableBro before I execute it. and each time it's being executed ill get notified.
Example would be highly appreciated.
Thanks,
ray.
you have to provide schedulers in subscribeOn method like:
public static void main(String[] args) throws InterruptedException {
Observable<Integer> observable2 = Observable.create(subscriber->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Arrays.asList(1, 2, 3).forEach((value)-> subscriber.onNext(value));
subscriber.onCompleted();
subscriber.onError(new RuntimeException("error"));
});
System.out.println("Before");
observable2
.subscribeOn(Schedulers.io()).subscribe(
(next) -> log.info("Next element {}", next),
(error) -> log.error("Got exception", error),
() -> log.info("Finished")//on complete
);
System.out.println("After");
//Thread.sleep(5000); //uncomment this to wait for subscriptions, otherwise main will quit
}
Its not async by default :)