-
Notifications
You must be signed in to change notification settings - Fork 894
[RESTEASY-1798] #1503
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
[RESTEASY-1798] #1503
Conversation
ronsigal
commented
Apr 27, 2018
- Implemented RxInvoker for rxjava and rxjava2 classes.
- Extended Resteasy proxies to support reactive classes.
|
Replaces pull request #1493 . |
|
Hi, Did you see all my questions about the |
79c6f18 to
4a5d3ed
Compare
|
|
||
| import java.util.concurrent.CompletionStage; | ||
|
|
||
| public interface AsyncClientResponseProvider<T> { |
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.
Javadoc
asoldano
left a comment
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 is massive! :-)
Overall I like this quite a lot. Do you believe anything is still missing to properly address RESTEASY-1798, besides additional documentation?
| * This class is not currently used, having been replaced by AsyncStreamSseResponseConsumer even for | ||
| * non-SSE connections. | ||
| */ | ||
| private static class AsyncStreamingResponseConsumer extends AsyncStreamResponseConsumer |
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.
Is this still really the case? It looks needed above
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.
@asoldano, ah, yes, it's back in use.
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, thanks
| } | ||
|
|
||
| @Test | ||
| @Ignore // @TODO Fix |
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.
?
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.
@asoldano, there's a problem, maybe in undertow, such that after making a HEAD call, the connection hangs. I set it aside in RESTEASY-1885 for later attention.
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, no problem, then just add a reference to RESTEASY-1885 in the TODO comment 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.
@asoldano done. Haven't committed it yet.
|
Hi @asoldano, Yes, it's grown over time. ;-) I would say that it's just about in a state that it makes a coherent package. The recent exchanges with @FroMage have led me to think more about the compatibility of different client and server modes. For example, if a resource method is annotated @stream(mode=Stream.MODE.SSE) and it returns a Flowable, can the result be read by a pure SSE client. It turns out the answer is "yes". Can it be read by a FlowableRxInvoker? Right at the moment, it's not working, which I'm trying to figure out. Can a pure SSE resource method be read by a FlowableRxInvoker? I don't know yet. Eventually, it would be nice if all of these scenarios worked. For now, I'm trying to get a coherent picture so I can say something sensible in the documentation. I'll see what I can figure out tonight. -Ron Following up: The answers are "yes", "yes", and "yes", if "@stream(mode=Stream.MODE.SSE)" is replaced by "@produces("text/event-stream"), as demonstrated in
|
| * @tpSince RESTEasy 4.0 | ||
| */ | ||
| @Test | ||
| @Ignore // Doesn't currently work. |
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.
@ronsigal Is this a regression? Can you please provide same details about this ignore?
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.
@marekkopecky, this is a new version of an existing test, which is now PublisherResponseNoStreamTest.testTextErrorDeferred(). The original still works, so it's not a regression. I've added a comment.
|
@ronsigal OK, excellent, thanks for the status update. I'm starting looking at the doc you just pushed in the mean time. |
| </programlisting> | ||
|
|
||
| <para> | ||
| The way to think about <methodname>logRunningOpAsync()</methodname> is that the method is asynchronously |
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.
Typo, s/logRunningOpAsync/longRunningOpAsync/g
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.
@marekkopecky, got it. Thanks.
| </programlisting> | ||
|
|
||
| <para> | ||
| Here, the byte arrays are written to the ... |
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 is to be completed, right? :-)
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.
@asoldano, yes. :))
| </para> | ||
|
|
||
| <programlisting> | ||
| <List<Thing>> Flowable<List<Thing>> Flowable.create(FlowableOnSubscribe<List<Thing>> source, BackpressureStrategy mode); |
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.
The beginning of this line should not be here, right?
Basically, use this:
Flowable<List<Thing>> Flowable.create(FlowableOnSubscribe<List<Thing>> source, BackpressureStrategy mode);
instead of this:
<List<Thing>> Flowable<List<Thing>> Flowable.create(FlowableOnSubscribe<List<Thing>> source, BackpressureStrategy mode);
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.
@marekkopecky, well, Flowable.create() is defined
public static <T> Flowable<T> create(FlowableOnSubscribe<T> source, BackpressureStrategy mode)
so <T> in this case <List<Thing>>. But I'll delete <List<Thing>>.
| @Override | ||
| public void subscribe(FlowableEmitter<List<Thing>> emitter) throws Exception { | ||
| for (int i = 0; i < listSize; i++) { | ||
| List<Thing> list = new ArrayList<Thing>()< |
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.
;char should be at the end of this line instead of < ...
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.
Aaarrgh. Yes, thanks.
| System.out.println("result: " + stage.toCompletableFuture().get()); | ||
| } | ||
|
|
||
| private CompletionStage<String*gt; getCompletionStage() { |
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.
s/*gt;/>/g
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.
@marekkopecky, OK, got it. Thanks.
|
@ronsigal I started Rx2FlowableTest#testGet test. Test passes, but server prints many warnings, like this: I use this command: Can you please check it? I can provide you more logs, if you want ... |
| it holds at most one potential value. <classname>Flowable</classname> implements | ||
| <classname>io.reactivex.Publisher</classname>, and <classname>Observable</classname> is very | ||
| similar to <classname>Flowable</classname> except that it doesn't support backpressure. | ||
| So, if you import <code>resteasy-rxjava2</code>, you can just start returning these reactive types from your |
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.
@ronsigal, @asoldano: "import resteasy-rxjava2" ... Are there some future plans to add resteasy-rxjava2 module to wf? (I know, that this can't be in scope of this PR and in scope of RESTEASY-1798)
Currently, if I want to import resteasy-rxjava2 to WF, I need to copy ${RESTEASY_GIT}/jboss-modules/target/modules/org/jboss/resteasy/resteasy-rxjava2 directory to WF and add resteasy-rxjava2 dependency to jboss-deployment-structure.xml file in deployment ... and it is quite complicated in my PoV ...
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.
My opinion here is that we'll have to add the resteasy-rxjava and resteasy-rxjava2 modules into wildfly, possibly as tech-preview
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 we add resteasy-rxjava2 only? (without resteasy-rxjava(1))
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.
Hi @marekkopecky,
- Re the Rx2FlowableTest error messages: That sounds familiar. I added
<module name="javax.ws.rs.api"/>
to the resteasy-rxjava2 module.xml file in jboss-modules a while ago (at home). I think that's what fixed it. I should have a new commit soon.
-
Re jboss-deployment-structure.xml: Is that necessary? I just unzip resteasy-jboss-modules-4.0.0-SNAPSHOT.zip
-
Re wildfly: I defer to Alessio on that question.
-
Re Just resteasy-rxjava2: I guess the question is: How far along is the rx community in upgrading from rxjava to rxjava2. @marekkopecky, @FroMage, what do you think?
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.
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.
Well, yes, Marek is probably right on rxjava becoming obsolete. Still, at least the rxjava2 module should be added, to simplify wildfly user acess to the new features. To be re-evaluated in the next future.
| </para> | ||
| </sect1> | ||
|
|
||
| <sect1> |
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.
Last two sections need to be finished too ...
I add comment here just for tracking purpose ...
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.
@marekkopecky, coming soon!!
b2d661f to
43a7d7e
Compare
1) Implemented RxInvoker for rxjava and rxjava2 classes. 2) Extended Resteasy proxies to support reactive classes. [RESTEASY-1798] Internationalized resteasy-rxjava, resteasy-rxjava2. [RESTEASY-1798] Added tests for transforming to and from CompletionStage and reactive classes. [RESTEASY-1798] Add mode to @stream. [RESTEASY-1798] First draft of reactive documentation. RESTEASY-1798 1) Various corrections. 2) New and updated tests. [RESTEASY-1798] Fixed some texts, added some javadoc. [RESTEASY-1798] Fixed syntactic errors. [RESTEASY-1798] Fixed missing declarations in tests. [RESTEASY-1798] 1. Improved stream element media type handling; 2) minor test updates; and 3) additions to reactive chapter in User Guide. [RESTEASY-1798] Modified handling of media type in OutboundSseEventImpl to fix failing unit test.
| </para> | ||
|
|
||
| <programlisting> | ||
| applicaton/x-stream-raw;"element-type=<element-type>" |
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.
I'm sorry I only noticed this now @ronsigal but I think it's wrong: in all use-cases I can think of, I need the content type to be set by @Produces and not set to the useless x-stream-raw that no existing client can handle. Can we change this behaviour?
| } | ||
| } | ||
| } | ||
| else if (o instanceof String) |
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.
@ronsigal This condition doesn't make sense. if (o instanceof String) statement is reachable only if o == null (see line 227). And if o == null, if (o instanceof String) is evaluated to false. So if (o instanceof String) is always evaluated as false, so lines 239-247 are useless now. Can you please check&&fix it?
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.
@marekkopecky, d'oh! It looks like I copy and pasted to the wrong place. Thanks.
| public Byte[] readFrom(Class<Byte[]> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { | ||
| ArrayList<Byte> list = new ArrayList<Byte>(); | ||
| int b = entityStream.read(); | ||
| while (b != -1) { |
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.
Wouldn't be better to add b = entityStream.read(); to while loop? This still (forever) adds to list first int from stream ...
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.
@marekkopecky, d'oh^2.
| jaxrsResponse = (BuiltResponse) Response.ok().type(MediaType.SERVER_SENT_EVENTS).build(); | ||
| ResourceMethodInvoker method =(ResourceMethodInvoker) request.getAttribute(ResourceMethodInvoker.class.getName()); | ||
| Produces produces = method.getMethod().getAnnotation(Produces.class); | ||
| if (produces != null & contains(produces.value(), MediaType.SERVER_SENT_EVENTS)) |
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.
Typo? '&' -> '&&'?
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.
@marekkopecky, you're right again. Oddly, I think the result is the same even though the semantics are different.
|
|
||
| if (name == null) | ||
| { | ||
| name = String.format("sse-event-source(%s)", target.getUri()); |
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.
Name is never used, can you please check it? I know, that this line is not changed in this PR ...
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.
@marekkopecky, I'll ask Jim about this one. It looks like he might have intended to use it.
|
|
||
| private MediaType[] getAccept() | ||
| { | ||
| if (syncInvoker instanceof ClientInvocationBuilder) |
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.
Is this correct? syncInvoker is always ClientInvocationBuilder ...
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.
@marekkopecky, you're right, but javax.ws.rs.client.SyncInvoker is an interface, so, in principle, someone could use a different implementation, right?
|
|
||
| private MediaType[] getAccept() | ||
| { | ||
| if (syncInvoker instanceof ClientInvocationBuilder) |
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 here ...
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.
@marekkopecky, same here ...
Minor corrections pointed out on resteasy#1503.
|
@marekkopecky, thank you for taking the time to look at this stuff. I've created #1531 for follow up changes. |
Minor corrections pointed out on resteasy#1503. [RESTEASY-1798] Changed treatment of RAW streaming.
Minor corrections pointed out on #1503. [RESTEASY-1798] Changed treatment of RAW streaming.
Minor corrections pointed out on resteasy#1503. [RESTEASY-1798] Changed treatment of RAW streaming.
| @SuppressWarnings("deprecation") | ||
| public class ObservableRxInvokerImpl implements ObservableRxInvoker | ||
| { | ||
| private static Object monitor = new Object(); |
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.
Hey @ronsigal,
I was having a look at this class, in relation to what I've done in #1599, and just for understanding I was wondering what is this monitor for?
According to the code I suppose that it prevents both multiple and concurrent invocations of sseEventSource.open(...) for a given eventSource. I'm OK with it.
But does this monitor needs to be global and shared beetwen all Observable instance ?
I mean why didn't you use a local sync mech as follow. It will improve concurrency between multiple invocations on the same ObservableRxInvokerImpl instance:
private <T> Observable<T> eventSourceToObservable(SseEventSourceImpl sseEventSource, Class<T> clazz, String verb, Entity<?> entity, MediaType[] mediaTypes)
{
Observable<T> observable = Observable.create(
new Observable.OnSubscribe<T>() {
private final AtomicBoolean open= new AtomicBoolean();
@Override
public void call(Subscriber<? super T> sub) {
sseEventSource.register(
(InboundSseEvent e) -> {T t = e.readData(clazz, ((InboundSseEventImpl) e).getMediaType()); sub.onNext(t);},
(Throwable t) -> sub.onError(t),
() -> sub.onCompleted());
if (open.compareAndSet(false, true))
{
sseEventSource.open(null, verb, entity, mediaTypes);
}
}
});
return observable;
}
I suppose there must be an good reason bu I do not get it yet.
Thanks