Rx-Java replace Observable in case of error - java

I have 2 URLs to fetch the data, for example: \location_1\{userid} and \location_2\{userid}. for the first i get the list of the users and then need to fetch user details by above requests. the issue is that i need to call the \location_1\{userid} and in case there is an error(exception) fetch the data from \location_2\{userid}. is it possible to make it with single rx-chain? i've tried try/catch as described here but looks catch newer calls, only onErrorResumeNext calls.
Observable<List<TestModel2>> observable = apiTest
.performTest()
.flatMapIterable(items -> items)
.flatMap(testModel -> {
try
{
return apiTest.performTest2(testModel.userId);
} catch (Exception e)
{
return apiTest.performTest3(testModel.userId);
}
}).doOnNext(testModel2 -> {Log.d("TestItemData", "doOnNext --- " + testModel2.title);})
.onErrorResumeNext(throwable ->{
Log.d("TestItemData", "onErrorResumeNext -------- ");
return Observable.empty();
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());

Use onErrorResumeNext (as you already did a bit later in the flow):
Observable<List<TestModel2>> observable = apiTest
.performTest()
.flatMapIterable(items -> items)
.flatMap(testModel ->
apiTest.performTest2(testModel.userId)
.onErrorResumeNext(e -> apiTest.performTest3(testModel.userId)); // <----------------
)
.doOnNext(testModel2 -> {
Log.d("TestItemData", "doOnNext --- " + testModel2.title);
})
.onErrorResumeNext(throwable ->{
Log.d("TestItemData", "onErrorResumeNext -------- ");
return Observable.empty();
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());

Related

Reactor fireAndForget Mono pass context

I am trying to pass subscriber Context to the fireAndForget method which is called inside the doOnNext. The fireAndForget is run also async non-blocking. How this context might be passed so the value for "key" is present? When I run the following test it passes. However, in the logs I can see that for both doOnNext I get:
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.util.NoSuchElementException: Context is empty
#Test
void shouldPassContextToFireAndForget() {
final Mono<String> helloWorldMono = Mono.just("hello")
.doOnNext(this::fireAndForget)
.doOnNext(name -> Mono.deferContextual(contextView -> fireAndForget(contextView, name)).subscribe())
.flatMap(name -> Mono.deferContextual(contextView -> Mono.just(name + " " + contextView.get("key"))))
.contextWrite(Context.of("key", "world"));
StepVerifier.create(helloWorldMono)
.expectNext("hello world")
.verifyComplete();
}
private Mono<String> fireAndForget(ContextView context, String name) {
return Mono.just(name)
.flatMap(value -> Mono.deferContextual(contextView -> Mono.just(value + contextView.get("key"))))
.contextWrite(context);
}
private void fireAndForget(String name) {
Mono.just(name)
.flatMap(value -> Mono.deferContextual(contextView -> Mono.just(value + contextView.get("key"))))
.subscribe();
}
Context is a subscribe-time concept. There are two possible approaches.
You can expose the ContextView at the middle of the chain using transformDeferredContextual:
final Mono<String> helloWorldMono = Mono.just("hello")
.transformDeferredContextual((original, cntx) -> original.doOnNext(name-> fireAndForget(cntx, name).subscribe()))
.flatMap(name -> Mono.deferContextual(contextView -> Mono.just(name + " " + contextView.get("key"))))
.contextWrite(Context.of("key", "world"));
Alternatively, you could take advantage of Mono.deferContextual in order to expose the ContextView at the start of the chain like this:
final Mono<String> helloWorldMono = Mono.deferContextual(context ->
Mono.just("hello")
.doOnNext(name -> fireAndForget(context, name).subscribe())
.flatMap(name -> Mono.just(name + " " + context.get("key")))
).contextWrite(Context.of("key", "world"));

Process List of entities using completable futures

I have a list of entities of type T. I also have a functional interface which acts as Supplier which has the method to performTask on entity and send back the result R which looks like:
R performTask(T entity) throws Exception.
I want to filter both: the successful results and errors & exceptions coming out of it onto separate maps. The code I wrote here is taking time, Kindly suggest what can be done.
I am looping on the list of entities, then process their completable future one by one, which I think is not the right way to do. Can you all suggest what can be done here ?
private void updateResultAndExceptionMaps(List < T > entities, final TaskProcessor < T, R > taskProcessor) {
ExecutorService executor = createExecutorService();
Map < T, R > outputMap = Collections.synchronizedMap(new HashMap < T, R > ());
Map < T, Exception > errorMap = new ConcurrentHashMap < T, Exception > ();
try {
entities.stream()
.forEach(entity -> CompletableFuture.supplyAsync(() -> {
try {
return taskProcessor.performTask(entity);
} catch (Exception e) {
errorMap.put(entity, (Exception) e.getCause());
LOG.error("Error processing entity Exception: " + entity, e);
}
return null;
}, executor)
.exceptionally(throwable -> {
errorMap.put(entity, (Exception) throwable);
LOG.error("Error processing entity Throwable: " + entity, throwable);
return null;
})
.thenAcceptAsync(R -> outputMap.put(entity, R))
.join()
); // end of for-each
LOG.info("outputMap Map -> " + outputMap);
LOG.info("errorMap Map -> " + errorMap);
} catch (Exception ex) {
LOG.warn("Error: " + ex, ex);
} finally {
executor.shutdown();
}
}
outputmap should contain the entity and result, R.
errorMap should contain entity and Exception.
This is because you iterate over List of entities one by one, create CompletableFuture object and immediately block iteration because of join method which waits until given processor finishes it work or throw exception. You can do that with full multithreading support by converting each entity to CompletableFuture, collect all CompletableFuture instances and after that wait for all invoking join on each.
Below code should do the trick in your case:
entities.stream()
.map(entity -> CompletableFuture.supplyAsync(() -> {
try {
return taskProcessor.performTask(entity);
} catch (Exception e) {
errorMap.put(entity, (Exception) e.getCause());
}
return null;
}, executor)
.exceptionally(throwable -> {
errorMap.put(entity, (Exception) throwable);
return null;
})
.thenAcceptAsync(R -> outputMap.put(entity, R))
).collect(Collectors.toList())
.forEach(CompletableFuture::join);

Java 8 comparator not working

I have a basic SpringBoot app. using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file
I have this piece of code to compare POJOs, but the compartor seems not to work because lastDeviceEvent and firstDeviceEvent are the same object with the same ID
DeviceEvent lastDeviceEvent = null;
DeviceEvent firstDeviceEvent = null;
try {
lastDeviceEvent = deviceEvents
.stream()
.filter (o -> o.getId().equals(deviceId))
.sorted(comparing((DeviceEvent de) -> de.getId()).reversed())
.findFirst().get();
firstDeviceEvent = deviceEvents
.stream()
.filter (o -> o.getId().equals(deviceId))
.sorted(comparing((DeviceEvent de) -> de.getId()))
.findFirst().get();
LOG.info("lastDeviceEvent --> " + lastDeviceEvent.getId());
LOG.info("firstDeviceEvent -> " + firstDeviceEvent.getId());
} catch (NoSuchElementException nse) {
throw new AccessDeniedException("403 Forbidden");
}
The comparator seems correct. The problem seems to be in your filter clause, where you compare the event id to the device id
lastDeviceEvent = deviceEvents
.stream()
.filter (o -> o.getDeviceId().equals(deviceId)) // Original code used getId()
.sorted(comparing((DeviceEvent de) -> de.getId()).reversed())
.findFirst()
.get();

RxJava2: Need help to convert java code into rx

I am newbie in RxJava and need help to improve my code. Here is what I've done:
public Single<List<MenuItemsBlocks>> loadMenuItemsBlocks() {
Completable.fromAction(() -> DataStoreRepository.deleteMenuItemsBlock())
.subscribeOn(Schedulers.io()).blockingAwait();
List<MenuItemsBlocks> blocks = new ArrayList<>();
Set<String> aliasList = getAliasFromMenuItems();
for (String alias : aliasList) {
List<MenuItemsBlocks> itemsBlocks = ApiRepository.getMenuItemBlocks(alias)
.subscribeOn(Schedulers.io())
.flatMapIterable(list -> list)
.map(item -> new MenuItemsBlocks(
item.getId(),
item.getType(),
item.getImagePosition(),
item.getTextField(),
item.getSortOrder(),
item.getFileTimeStamp(),
alias
))
.doOnNext(block -> DataStoreRepository.saveMenuItemsBlock(block))
.subscribeOn(Schedulers.io())
.toList()
.blockingGet();
blocks.addAll(itemsBlocks);
}
return Single.just(blocks);
}
There is no problem at runtime with this code, but I want to improve it in rx style, I've tried to rewrite it something like this (but it's not working):
public Single<List<MenuItemsBlocks>> loadMenuItemsBlocks() {
Completable.fromAction(() -> DataStoreRepository.deleteMenuItemsBlock())
.subscribeOn(Schedulers.io()).blockingAwait();
Set<String> aliasList = getAliasFromMenuItems();
return Observable.fromIterable(aliasList)
.switchMap(alias -> ApiRepository.getMenuItemBlocks(alias)
.subscribeOn(Schedulers.io())
.flatMapIterable(list -> list)
.map(item -> new MenuItemsBlocks(
item.getId(),
item.getType(),
item.getImagePosition(),
item.getTextField(),
item.getSortOrder(),
item.getFileTimeStamp(),
alias
))
.doOnNext(block -> DataStoreRepository.saveMenuItemsBlock(block))
.subscribeOn(Schedulers.io())
.toList()
);
}
And I am stuck with it and need your help!
First of all, if you have blockingAwait in non-test code, you are doing it wrong. Second, you probably need concatMap instead of switchMap as it will just keep switching to later list elements, cancelling the outstanding API calls.
public Single<List<MenuItemsBlocks>> loadMenuItemsBlocks() {
return Completable.fromAction(() -> DataStoreRepository.deleteMenuItemsBlock())
.subscribeOn(Schedulers.io())
.andThen(Single.defer(() -> {
Set<String> aliasList = getAliasFromMenuItems();
return Observable.fromIterable(aliasList)
.concatMap(alias -> ApiRepository.getMenuItemBlocks(alias)
.subscribeOn(Schedulers.io())
.flatMapIterable(list -> list)
.map(item -> new MenuItemsBlocks(
item.getId(),
item.getType(),
item.getImagePosition(),
item.getTextField(),
item.getSortOrder(),
item.getFileTimeStamp(),
alias
))
.doOnNext(block -> DataStoreRepository.saveMenuItemsBlock(block))
.subscribeOn(Schedulers.io())
)
.toList();
}));
}

RxJava concurrency with multiple subscribers and events

I'm looking for a way to attach multiple subscribers to an RxJava Observable stream, with each subscriber processing emitted events asynchronously.
I first tried using .flatMap() but that didn't seem to work on any subsequent subscribers. All subscribers were processing events on the same thread.
.flatMap(s -> Observable.just(s).subscribeOn(Schedulers.newThread()))
What ended up working was consuming each event in a new thread by creating a new Observable each time:
Observable.from(Arrays.asList(new String[]{"1", "2", "3"}))
.subscribe(j -> {
Observable.just(j)
.subscribeOn(Schedulers.newThread())
.subscribe(i -> {
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(100, 500));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("s1=>" + Thread.currentThread().getName() + "=>" + i);
});
});
Output:
s1=>RxNewThreadScheduler-1=>1
s1=>RxNewThreadScheduler-2=>2
s1=>RxNewThreadScheduler-3=>3
And the end result with multiple subscribers:
ConnectableObservable<String> e = Observable.from(Arrays.asList(new String[]{"1", "2", "3"}))
.publish();
e.subscribe(j -> {
Observable.just(j)
.subscribeOn(Schedulers.newThread())
.subscribe(i -> {
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(100, 500));
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("s1=>" + Thread.currentThread().getName() + "=>" + i);
});
});
e.subscribe(j -> {
Observable.just(j)
.subscribeOn(Schedulers.newThread())
.subscribe(i -> {
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(100, 500));
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("s2=>" + Thread.currentThread().getName() + "=>" + i);
});
});
e.connect();
Output:
s2=>RxNewThreadScheduler-4=>2
s1=>RxNewThreadScheduler-1=>1
s1=>RxNewThreadScheduler-3=>2
s2=>RxNewThreadScheduler-6=>3
s2=>RxNewThreadScheduler-2=>1
s1=>RxNewThreadScheduler-5=>3
However, this seems a little clunky. Is there a more elegant solution or is RxJava just not a good use case for this?
Use .flatMap(s -> Observable.just(s).observeOn(Schedulers.newThread())....)
if I understood the rx-contract correctly, you are trying to do something, which is against it.
Lets have a look at the contract
The contract of an RxJava Observable is that events ( onNext() , onCompleted() , onEr
ror() ) can never be emitted concurrently. In other words, a single Observable
stream must always be serialized and thread-safe. Each event can be emitted from a
different thread, as long as the emissions are not concurrent. This means no inter‐
leaving or simultaneous execution of onNext() . If onNext() is still being executed on
one thread, another thread cannot begin invoking it again (interleaving). --Tomasz Nurkiewicz in Reactive Programming with RxJava
In my opinion you are trying to break the contract by using a nested subscription in the outer subscription. The onNext call to the subscriber is not serialized anymore.
Why not move the "async"-workload from the subscriber to a flatMap-operator and subscribe to the new observable:
ConnectableObservable<String> stringObservable = Observable.from(Arrays.asList(new String[]{"1", "2", "3"}))
.flatMap(s -> {
return Observable.just(s).subscribeOn(Schedulers.computation());
})
.publish();
stringObservable
.flatMap(s -> {
// do More asyncStuff depending on subscription
return Observable.just(s).subscribeOn(Schedulers.newThread());
})
.subscribe(s -> {
// use result here
});
stringObservable
.subscribe(s -> {
// use immediate result here.
});
stringObservable.connect();
flatMap along with doOnNext on the Observable inside the flatMap will result in the same output as yours.
onNext() is always called in a sequential manner hence using doOnNext after the flatMap will also not work for you. Due to the same reason writing the action inside the final subscribe didn't work in your case.
The below code is written using RxJava2. In version 1 of RxJava you will have to add the try-catch block around Thread.sleep.
ConnectableObservable<String> e = Observable.just("1", "2", "3").publish();
e.flatMap(
s -> Observable.just(s)
.subscribeOn(Schedulers.newThread())
.doOnNext(i -> { // <<<<<<
Thread.sleep(ThreadLocalRandom.current().nextInt(100, 500));
System.out.println("s1=>" + Thread.currentThread().getName() + "=>" + i);
}))
.subscribe();
e.flatMap(
s -> Observable.just(s)
.subscribeOn(Schedulers.newThread())
.doOnNext(i -> { // <<<<<<
Thread.sleep(ThreadLocalRandom.current().nextInt(100, 500));
System.out.println("s2=>" + Thread.currentThread().getName() + "=>" + i);
}))
.subscribe();
e.connect();
You can achieve it with Flowable and parallel:
Flowable.fromIterable(Arrays.asList("1", "2", "3"))
.parallel(3)
.runOn(Schedulers.newThread())
.map(item -> {
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(100, 500));
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("s1=>" + Thread.currentThread().getName() + "=>" + item);
return Completable.complete();
})
.sequential().subscribe();

Categories

Resources