-
Notifications
You must be signed in to change notification settings - Fork 1.4k
[CXF-7535] Adding client & server support for Project Reactor #331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Usually I have this issue if I miss some Maven profile selections. Adjusting the checkboxes normally fixes this. |
|
Hi @deki thanks for the tip. What profiles would you recommend? note that I only have the jaxrs systests module open in my IDE, but can go back to the broader one. One other thing, i'm noticing that the settings jar from intellij doesn't put imports in the right order. |
|
I usually check the everything profile. But in your case it looks more like the dependencies are not properly resolve. Hit the refresh button in the Maven projects tab (first one on the left). If this doesn't work, run File -> Invalidate Caches/ Restart. |
| } | ||
|
|
||
| @Override | ||
| public Flux get() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flux<Response> please :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be fixed.
| } | ||
|
|
||
| private <R> Flux<R> flux(Future<R> future) { | ||
| Flux<R> flux = Flux.from(Mono.fromFuture(toCompletableFuture(future))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part concerns. I would suggest to not use scheduler but pass executor directly to toCompletableFuture (so the supplyAsync call would be done outside of common ForkJoin pool). Otherwise we have too many pools and threads involved for no real reason.
Regarding the desired behavior, we would certainly want the subscription to be invoked on the separate thread (this is what subscribeOn does now). However, because we create Flux from CompletableFuture, intuitively the completion callback would be invoked from another thread, either the one from common ForkJoin pool or the provided one (unless then value is available immediately).
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, the executor part is fixed. If there's an executor passed in we use that.
However, there's an interesting conundrum. I'm not sure how rx() is meant to work this way. For a Mono object its pretty clear, I had to tweak the test to use a version of the json processor that didn't treat the result as an array. Which makes sense, its meant to be a single object.
However, I need to figure out a way to check if the opening character when reading the result is a single or multiple result. Effectively I need the MessageBodyReader equivalent of StreamingResponseProvider. I'm kind of wondering how this works for the RxJava versions (but then again, there's no tests for this method, so I wonder if you guys ran into that issue already).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the client side one works against a specific URI and thus the expectations are known in advance, i.e, it is the collection or not which is returned
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I'm thinking as well, to make it less complicated. So what that means is if you work with it, by default you're getting Mono objects back (since it's typically one element). Any method that supports a GenericType I'm going to add a Flux version for that just takes the singular attribute, so that you end up with something like <R> Flux<R> getFlux(Class<R> responseType) which should give a pretty clean programming model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you clarify a bit, may be with the code examples in the JIRA issue ? I'm not sure what is a distinction between a single and multiple objects on the client side, example, on the server one can be streaming a sequence of Book objects, but on the client it can be a single Books object.
|
Do we need to specifically support reactor or is it enough to just depend on reactive streams? |
|
@cschneider there's a new |
|
Got it now .. looks good this way. |
rt/rs/extensions/reactor/pom.xml
Outdated
| <parent> | ||
| <groupId>org.apache.cxf</groupId> | ||
| <artifactId>cxf-parent</artifactId> | ||
| <version>3.2.1-SNAPSHOT</version> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3.2.2-SNAPSHOT :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, just saw it.
|
|
||
| private <R> Flux<R> flux(Future<R> future) { | ||
| Flux<R> flux = Flux.from(Mono.fromFuture(toCompletableFuture(future, executorService))); | ||
| if (scheduler != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johnament Thanks for passing the executorService to toCompletableFuture. The scheduler and this snippet:
if (scheduler != null) {
flux = flux.subscribeOn(scheduler);
}
are not needed. The reason is that the completion callback for Mono.fromFuture will be called in one of the executorService threads or ForkJoin pool (you could easily trace it). Moreover, we could run into issues with this code because it uses two threads from the same pool for toCompletableFuture and subscription, exposing ourselves for likelihood of livelocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the underlying call is happening via async webclient, we can't take full advantage of the reactive nature (but not sure the JSON format works well, i want to play around with SSE next after this is merged). Scheduler should not be used.
| import reactor.core.publisher.Mono; | ||
|
|
||
| @SuppressWarnings("rawtypes") | ||
| public interface MonoRxInvoker extends RxInvoker<Mono> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you use extends RxInvoker<Mono<?>>, you could get rid of @SuppressWarnings("rawtypes")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but then the overrides don't line up properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind, whatever was happening isn't any longer.
| import reactor.core.publisher.Flux; | ||
|
|
||
| @SuppressWarnings("rawtypes") | ||
| public interface FluxRxInvoker extends RxInvoker<Flux> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as for MonoRxInvoker, if you use extends RxInvoker<Flux<?>> you could remove @SuppressWarnings("rawtypes")
| try { | ||
| return future.get(); | ||
| } catch (InterruptedException | ExecutionException e) { | ||
| throw new RuntimeException(e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using CompletionException would be more appropriate I think. Otherwise we would end up with longer ex-> RuntimeException(ex) -> CompletionException chain of causes.
|
@johnament It looks pretty good in general, thanks for addressing the comments. Are there any outstanding issues you are struggling with / need help? |
|
@reta there's open questions I have:
|
|
@johnament my few cents on the questions you have:
@sberyozkin What do you think? |
|
I would keep tests with the module unless they are slow or very involved. We spawn CXF endpoints in a lot of other modules tests. |
|
Hi All, my understanding is that it is a CXF convention to have unit tests with the modules and the end to end tests involving starting endpoints and clients and sending the data over some transports - in systests, so IMHO it makes sense to keep that way. John, Andriy, re CDI, yes, nice idea, but may be indeed at the next stage (and would that make a ReactorInvoker a default one when CDI is loaded ? something we can discuss later) |
|
Ok, I just pushed up the move to systests. Assuming that all passes in jenkins I'll merge and circle back on the remaining parts. |
This is still a WIP, so not ready to be merged.
I'm looking to add support for Project Reactor as an rx invoker for CXF. It effectively mirrors what the other two are doing, but using reactor's APIs.
I'm still working on tests (e.g. I don't know if this works). the biggest I'm having right now is getting the
systeststo import in intellij. I've done it previously, but for some reason after switching to 3.2 it's no good. I've even gone in, blown away all.imland the.ideafolder, no luck. Anyone else seen something like this?