I feel like this is a dumb question, but I couldn't find any answer for a while, so I'm gonna ask it, sorry :)
So, I need a function that does the following:
1) Calls another function to create an Observable User
2) Gets the User object from the Observable User
3) Gets some info about the user and runs through some logic
4) Returns Observable User
I am having troubles with step #2. How do I do that? Or, is this approach somehow fundamentally wrong?
Here's the "model" of the function:
#Override protected Observable buildUseCaseObservable(){
Observable<User> userObservable = userRepository.findUserByUsername(username);
//User user = ??????
//if (...) {...}
return userObservable;
}
Thank you :)
You can use operators(map, flatMap, doOnNext, etc) to get the object wrapped by your observable through the pipeline
Observable.just("hello world")
.map(sentence-> sentence.toUpperCase) --> do whatever you need.
.subscribe(sentence -> println(sentence)
By design Observable follow the Observer patter, which subscribe to the observable and receive the item once has been emitted through the pipeline.
Also what you can do is instead use observer patter, just extract the object from the pipeline using toBlocking. But that´s is consider an anti pattern and means you´re not applying a good design.
#Test
public void observableEvolveAndReturnToStringValue() {
assertTrue(Observable.just(10)
.map(String::valueOf)
.toBlocking()
.single()
.equals("10"));
}
You can see more examples about to Blocking here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/utils/ObservableToBlocking.java
You cannot 'extract' something from an observable. You get items from observable when you subscribe to them (if they emit any). Since the object you are returning is of type Observable, you can apply operators to transform your data to your linking. The most common and easy to use operator in RxJava is 'map' which changes one form of data to other by applying a function.
In your case, you can use 'map' operator directly on Observable<user>:
return userRepository.findUserByUsername(username)
.map(new Func1<User, Object>() {
#Override
public Object call(User u) {
// ..
// apply your logic here
// ..
return myDataObject; // return you data here to subcribers
}
});
I hope you know the basics of RxJava and doesn't need any introduction about how to use operators. For map documentation, follow this link
Related
I have a Flux.concat() operation which takes 3 checks like this:
public Mono<Boolean> checkSleep(Student std)
{
Flux.concat(isHealthy(std), isWealthy(std, sleep), isWise(std, sleep))
.filter(result -> !result)
.next()
.flatMap(result -> Mono.just(false)) //returns false if any one is false
.switchIfEmpty(Mono.just(true)); // returns true if all are true
}
each of this methods has a common external api call extService.getSleep(Student std) to get Sleep Object Mono<Sleep> for its flow.
I want to call extService.getSleep(Student std) only once in the entire flow,
ideally in the first check isHealthy(std) and pass the object Mono<Sleep> to the next 2 checks as well.
I am not understanding how to make this call as Flux.concat does not allow a Mono to be added in the prefix.
Each of the checks have similar body like this:
Mono<Boolean> isHealthy(Student std)
{
return Mono.just(std)
.flatMap(std->extService.getSleep(std)).map(sleep-> sleep.isValid());
}
in the next check I want to pass sleep object from previous method,
isWealthy(Student std, Sleep sleep)
I do not want to call extService.getSleep(std)) once again,
I thought of creating a variable outside these 3 methods and update it when the api returns a something,
it throws error saying "Variable used in lambda expression should be final or effectively final"
Let me know if there is a better way to handle this scenario.
I am new to reactive spring programming, any help is appreciated.
thanks in advance.
Your line of thinking was not far off!
Whenever you need to "reach outside" a publisher, consider using AtomicBoolean, AtomicInteger, etc. or the parameterized AtomicReference to get around the final or effectively final compiler warning. However, it should be noted that asynchronous operations like flatMap may not have the correct value when they get the wrapped values from these, so it's best to get around the problem in a different way.
Fortunately, Reactor has a myriad of useful methods on its publishers!
If I understand correctly, the checkSleep function should resolve to true if all three of isHealthy, isWealthy and isWise also resolve to true - false if even one of them resolve to false.
I have created a simple simulation of this scenario:
private Mono<Boolean> checkSleep(Student std) {
return getSleep(std)
.flatMapMany(sleep -> Flux.merge(isHealthy(std, sleep), isWealthy(std, sleep), isWise(std, sleep)))
.all(result -> result);
}
private Mono<Sleep> getSleep(Student std) {
return Mono.just(new Sleep(8));
}
private Mono<Boolean> isHealthy(Student std, Sleep sleep) {
return Mono.just(true);
}
private Mono<Boolean> isWealthy(Student std, Sleep sleep) {
return Mono.just(true);
}
private Mono<Boolean> isWise(Student std, Sleep sleep) {
return Mono.just(true);
}
This way, getSleep is only called once, and is used to flat map the emitted value into the three booleans you're looking for. The Flux::all method then ensures that the returned Mono will wrap true only if all three inners have emitted true.
Another thing to note is that I've replaced Flux::concat with Flux::merge. The former goes sequentially, subscribing, waiting for result, then repeat. These three publishers seem to be independent of one another, so replacing concat with merge allows all three to be subscribed to at the same time, thereby reducing time wasted with waiting.
I have reacently started to use reactive thinking together with java and Spring.
Would it be possible create a scenario where you have a dynamic number of producers? Im developing a product that listen to a particular hardware for events and It would be awsome if I could add (and possibly remove) listening to devices dynamicaly A,B...Z without ending the stream.
I imagine a something like:
#GetMapping("/")
public Flux<HardwareEvent> void test() {
return Flux.merge(
listenHardware(A),
listenHardware(B)
);
}
Flux<HardwareEvent> listenHardware(HardwareId id); // produced indefinite stream of events
Where you somehow could inject new producers to the merge dynamically without stopping the stream.
Is this possible?
If someone could point me to (or post) an example it would be awesome.
Regards.
You might need some glue code in the middle, but this looks like a good use case for Subjects
Instead of having a "regular" Observable, you'd have a Subject that you're seeing as an Observable. Good thing of Subjects is that you can emit new values on demand so you could do something like
Subject<Value> s = ...;
Observable<Value> getObservable() {
return s;
}
void addSource(Observable<Value> obs) {
obs.subscribe(v -> s.emit(s))
}
Notice that it's pseudocode and will probably not compile as is
Take a look to the Subject docs to see which one fits your needs better
I'm wondering if it is possible to put the value of a Mono as a value into a Flux just like you could append any object to a list. I know there are some methods you could use but none of them fulfills my exact purpose. What I tried so far:
Flux<T> flux;
Mono<T> mono;
Flux.merge(flux, mono); // <- returns Flux<Object>
This doesn't sound too bad but notice that it does not return Flux<T> as I would need it but Flux<Object>. Same with Flux.concat(flux, mono);. The method Flux.zip(flux, mono); would stop merging as soon as the Mono completes as far as I understand.
Has somebody a solution for this?
This is what I ended up doing:
I have the method return a Flux of the desired type which in my case was an 'ExtendedResourceModel'. I create a Mono of this type which gets initialized by another method that I commented out to keep this answer as concise as possible. If a certain condition is met I want to also use a Flux from a different source so I use 'Flux.concat()' to put all elements into a single Flux. The 'concat'-method works by subscribing to the first argument, letting it finish, subscribing to the second one and so on.
In case of my condition not being met I just use 'concat' again but this time with only the Mono which will put the Mono's element into a new Flux. This way both cases have the same return type.
It is not possible to do, if you need to do that you can convert your Flux to mono
Mono monoToFlux = flux.collectList();
Mono mono;
Mono.zip (mono, monoToFlux).flatmap(tuple -> {
... more code ...
})
I have two Observables which are API calls. I want these two to run in parallel and after they both finish I want to call a third observable with the combined result.
For example:
I have an Observable<List<Place>> getPlaces() and an Observable<AdditionalPlaceData> getAdditonalPlaceData()
My idea was a method that zips them then enriches the places with the additonal data and calls a third observable
I try it like this but the BiFunction of Observable.zip cant return an observable:
private Observable<List<Place>> getPlaces(){
return Observable.zip(getPlaces(), getAdditonalPlaceData(),(places, additonalPlaceData) -> {
//enrich places with additional data
return thirdApiCall(places);//This is not allowed
});
}
Is there an other way instead of Observable.zip I dont know to achieve this use case? And I dont want to use flatMap beacuse that would run the two observables in a sequence. Thanks for your help.
Observables only emit one object, so doOnNext() is always called with an Action1. How can I use Action2 in a similar fashion?
Can I combine 2 observables call an Action2?
EDIT: Why would I want to do this? I am working on a checkout app. I have a view that in order to display correctly, it needs two pieces of data (1: tip% and 2: total cost). So if this view could react to an observable sequence as an Action2, I would be happy.
EDIT2: Here's a method on the view mentioned previously. If this were an Action1, I could easily call it like I do with .doOnNext(). Are there operators that can operate similarly to .doOnNext() but take in an Action2 as a parameter? Maybe something like withLatestFrom() that takes in an Action2 instead of a Func2?
public Action2<Money, List<Integer>> displayGratuityOptions() {
return (subtotal, gratuityPercents) -> {
removeAllTabs();
for (final Integer percent : gratuityPercents) {
addTab(createTab(subtotal, percent));
}
addTab(createCustomGratuityTab());
};
}
Operators like withLatestFrom are there to combine multiple sequences into a single sequence. I think you will be better placed to work with Rx if you can adopt this way of thinking i.e. single data type sequences.
To this end I would suggest creating a type that has both the data types you want. You can then merge your data and then pass the single value to your doOnNext handler*.
This is simple stuff in most languages that Rx is used in (C#, F#, JS, Scala) but in Java, you may have to actually declare/define a type to do this. See - A Java collection of value pairs? (tuples?)
*Please try to avoid using the Do side-effect operators. There is almost always a better way. In you example it looks like you are doing some significant work in that handler (Creating, Adding and Removing tabs which I assume are Visual Controls)