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) { }
});
})
Related
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;
}
I have two methods that return an Observable< Response< ResponseBody > >
firstAPI.getFirstInfo("1", "2");
secondApi.getSecondInfo(resultFirstObservable, "3");
I'm trying to get an information from the first one and use it as a parameter in the second one.
I started by trying to use a flatMap:
Observable<Integer> mergedObservers = firstAPI.getFirstInfo("1","2")
.flatMap((Response<ResponseBody> resultFirstObservable) -> {
try {
return secondApi.getSecondInfo(transformToTheFormatNeeded(resultFirstObservable.body().string()), "3");
} catch (IOException e) {
e.printStackTrace();
return secondApi.getSecondInfo("defaultValue", "3");
}
}, ((Response<ResponseBody> resultFirstObservable), (Response<ResponseBody> resultSecondObservable)) -> {
try {
return transformToWhatINeed(resultSecondObservable.body().string());
} catch (IOException e) {
e.printStackTrace();
}
});
I would try then to subscribeOn this new Observable and onNext I would send the returned value to my Activity.
Subscription sub = mergedObservers.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.repeatWhen(completed -> completed.delay(30, TimeUnit.SECONDS))
.subscribe(new Observer<Integer>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Integer integer) {
view.updateInfo(integer);
}
});
My problem is that the flatMap throws 2 errors at compilation:
error: lambda body is neither value nor void compatible
error: no suitable method found for
flatMap((__)->{ tr[...]; } },(.. ,..)->{ [...]; } })
What am I doing wrong ?
I don't think the API call actually throws an IOException while also returning an Observable. In addition, you have to return something from the second lambda but the try-catch there doesn't do that, causing the error. Try this:
Observable<Integer> mergedObservers = firstAPI.getFirstInfo("1","2")
.flatMap(resultFirstObservable -> {
return secondApi.getSecondInfo(resultFirstObservable, "3")
.onErrorResumeNext(e -> {
e.printStackTrace();
return secondApi.getSecondInfo("defaultValue", "3");
});
}, (resultFirstObservable, resultSecondObservable) -> {
try {
return transformToWhatINeed(resultSecondObservable.body().string());
} catch (IOException ex) {
ex.printStackTrace();
return "";
}
});
In the below code the subscriber stops recieving data whenever there is a timeout exception. How can I make sure that the subscriber does not stop when there is exception.
public class ReactiveDataService
{
private static String[] quotes = {"ITEM1", "ITEM2", "ITEM3"};
public Observable<Notification<String>> getStreamData()
{
return Observable.create(subscriber -> {
if(!subscriber.isUnsubscribed())
{
Stream<String> streams = Arrays.stream(quotes);
streams.map(quote -> quote.toString()).filter(quote -> quote!=null)
.forEach(q -> {
subscriber.onNext(Notification.createOnNext(q));
try
{
Random rand = new Random();
Integer i = (rand.nextInt(5)+1)*1000;
Thread.sleep(i);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
});
}
subscriber.onCompleted();
});
}
}
public class ReactiveResource
{
public static void main(String args[])
{
Observable<Notification<String>> watcher = new ReactiveResource().getData()
.timeout(4, TimeUnit.SECONDS)
.doOnError(failure -> System.out.println("Error:" + failure.getCause()))
.onErrorResumeNext(th -> {
return Observable.just(Notification.createOnError(new TimeoutException("Timed Out!")));
});
watcher.subscribe(
ReactiveResource::callBack,
ReactiveResource::errorCallBack,
ReactiveResource::completeCallBack
);
}
public static Action1 callBack(Notification<String> data)
{
System.out.println(data.getValue());
return null;
}
public static void errorCallBack(Throwable throwable)
{
System.out.println(throwable instanceof TimeoutException);
System.out.println(throwable);
}
public static void completeCallBack()
{
System.out.println("On completed successfully");
}
private Observable<Notification<String>> getData()
{
return new ReactiveDataService().getStreamData();
}
You can combine publish, mergeWith and timer to achieve this effect:
static <T> ObservableTransformer<T, T> onTimeoutKeepAlive(
long timeout, TimeUnit unit, Scheduler scheduler, T keepAliveItem) {
return upstream ->
upstream.publish(o ->
o.mergeWith(
Observable.timer(timeout, unit, scheduler)
.map(t -> keepAliveItem)
.takeUntil(o)
.repeat()
.takeUntil(o.ignoreElements().toObservable())
)
);
}
usage:
source
.compose(onTimeoutKeepAlive(
10, TimeUnit.SECONDS, Schedulers.computation(),
Notification.createOnError(new TimeoutException())
))
.subscribe(/* ... */);
I recently went through a code review, and it was firmly suggested that I consolidate two methods into one. Both methods are identical, save for a single method call in each, and one does not require an argument.
Method #1
private void updateCache(List<CategoryObject> objectList) {
ServiceApi serviceApi = getService();
if (serviceApi != null) {
try {
serviceApi.updateResources(objectList);
} catch (BusinessException e) {
log.error(e);
}
}
}
Method #2
private void registerCache() {
ServiceApi serviceApi = getService();
if (serviceApi != null) {
try {
serviceApi.registerCategory(CATEGORY_NAME);
} catch (BusinessException e) {
log.error(e);
}
}
}
Can these even be efficiently combined?
You can pull the inner functionality out into an interface:
private interface Op {
void perform(ServiceApi serviceApi);
}
static void cache(Op op) {
ServiceApi serviceApi = getService();
if (serviceApi != null) {
try {
op.perform(serviceApi);
} catch (BusinessException e) {
log.error(e);
}
}
}
private void updateCache(List<CategoryObject> objectList) {
cache(new Op() {
#Override
public void perform(ServiceApi serviceApi) {
serviceApi.updateResources(objectList);
}
});
}
private void registerCache() {
cache(new Op() {
#Override
public void perform(ServiceApi serviceApi) {
serviceApi.registerCategory(CATEGORY_NAME);
}
});
}
In Java 8 the two methods become truly simple and elegant.
private void updateCache(List<CategoryObject> objectList) {
cache(serviceApi -> serviceApi.updateResources(objectList));
}
private void registerCache() {
cache(serviceApi -> serviceApi.registerCategory(CATEGORY_NAME));
}
You could just use one method and differentiate by the input parameter:
private void updateCache(List<CategoryObject> objectList) {
ServiceApi serviceApi = getService();
if (serviceApi != null) {
try {
if(objectList != null){
serviceApi.updateResources(objectList);
}
else{
serviceApi.registerCategory(CATEGORY_NAME);
}
} catch (BusinessException e) {
log.error(e);
}
}
}
Maybe the method name should be refactored as well then: handleCache()?
You then can call the method in 2 ways:
handleCache(objectList) --> works like method #1
handleCache(null) --> works like method #2
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 :)