Re: FW: [jersey/jersey] SseEventSink invokes custom WriterInterceptor with wrong type, then throws NPE (#3592)


 

Pavel,

 

from the view of an application programmer the arguments look quite different: SSE within JAX-RS is not recognized as a different technology, but simply as a push-add-on that will keep the client's state representation continuously updated. So looking from the "top" (the business use case vs. the purely technical bottom) it is quite not understandable why the initial state representation can be produced using WriterInterceptor + MBW, but the updates cannot. In terms of code style, it looks even worse. In contrast to Sergey I need to say that even when I was not fond of Interceptors when Bill came up with the concept, I am a big fan of them meanwhile. The reason is pretty simple: Code style and separation of concers. Let me explain this with an example:

 

The following JAX-RS code produces the initial status on demand and it looks really, really smart as it is not cluttered with any technological code but is pure business domain code:

 

@GET public Optional<HardwareStatus> stuff(@PathParam("id") id) {

      return hardwareService.machine(id).map(Machine::getStatus);

}

 

Since JAX-RS 1.0 times there is a MBW in place for JAXB, since JAX-RS 2.1 for JSON, and possibly the application developer write one for producing PDFs from HardwareStatus. So the MBW's role here is "turn *business object* into network representation (XML, JSON, PDF). With JAX-RS the programmer wants to support Optional as that is produced by the hardware services since Java 8 (and he gets rid of exceptions as he does not care for them anyways). So he just adds an *interceptor* to unwrap. This is totally easy and straightfoward (replace Java class by Java class) to write and much simpler than writing an MBW (compare the actual code to do that and you will see). Interceptors mix in *technical* aspects (like replace Java class by Java class, use Caches, and so on). That single interceptor works with ANY MBW -- even if the developer adds more of them to produce new output formats (like HTML)! Now try to do that with an MBW as suggested: You would end up with either writing many MBWs (each technological-aspect-interceptor multiplied by each output-format-MBW) or by one synthetical MBW that does the M-by-N matrix all-in-one, hence is hard to maintain, is error-one due to ist complexity, and is clearly application specific. In contrast, each single format-MBW, and each single technical-aspect-interceptor, is so clean that it even could get commonly published for other users / projects!

 

Regarding the binding when the interceptor is needed / events being leighweight, I have to say that both is not an issue. Looking at the timing behavior of an event one will notice that processing in-memory (with or without interceptor) is not the top cost, but serializing via the network is. Also, an interceptor is pretty lightweight compared to an MBW as it is just a thin bunch of Java-by-Java replacer while the MBW is serializing data! And yes, certainly on the client one expects that each received event is first sent through all interceptors and he costs again are like zero compared to the network transfer. The binding of an interceptor is trivial: An business application programmer (which is someone different than a network layer programmer) simple expects that all interceptors will be running that are bundled with the application. He just does not want to specifically bind them. Why should he? The interceptor is added to the application to bring it a global solution (here: Support Optional<T>), so why should he not want to bind it globally? If he does not want it be executed, he can dynamically switch it off using a filter that modifies the config on the fly (that's what its good for)!

 

To sum up: From the view of an business application programmer, it would be really really really a big loss and very hard to understand to not support interceptors with SSE! :-)

 

-Markus

 

From: jaxrs-spec@javaee.groups.io [mailto:jaxrs-spec@javaee.groups.io] On Behalf Of Sergey Beryozkin
Sent: Dienstag, 27. Juni 2017 11:34
To: jaxrs-spec@javaee.groups.io
Subject: Re: [jaxrs] FW: [jersey/jersey] SseEventSink invokes custom WriterInterceptor with wrong type, then throws NPE (#3592)

 

Hi Pavel

So, when the SSE server reports a new event which will need to be delivered to SSE Client in for ex JSON, MessageBodyWriter is supported, right ?
I'm not understanding why WriterInterceptor is not supported in this case to keep it consistent with the regular response case.

I agree one can write a custom MessageBodyWriter with injected @Providers - believe it or not but I'm nearly 100% sure this is what I used as an argument against introducing Reader/Writer interceptors back in 2.0 :-). Everything you can do with WriterInterceptor can most likely be done with the custom MBW.

But we do have WriterInterceptor now so it is not clear why not just let users use them with SSE too

Cheers, Sergey



On 27/06/17 06:42, Pavel Bucek wrote:

Markus,

yes, the issue is that mostly, you wouldn't want to mix "standard" response processing and SSE.

MessageBodyWriter is slightly different, since there is a type, the ultimate definition of that provider, which can be used for lookup and the registered provider won't have any consequence on running app, unless used as a return type. WriterInterceptor doesn't have anything like that - how would you mark the provider as "please use it only with some particular EventSink"? Also, if we'd think about symmetry - that would mean that we need to invoke ReaderInterceptor per each received client event.

EventSink is not a resource method, we have almost no control of the context from which it is invoked from. Allowing MessageBodyWriters to be invoked is a simplest enhancement compared to "writing only strings". Server sent event is supposed to be lightweight and cheap to send - notice that it doesn't need to be used, writing a string is still supported.

And your usecase - WriterInterceptor is not what you want to use in described scenario. You can implement a MessageBodyWriter, unwrap, lookup the MBW for the type you've unwrapped using injected Providers instance.

Regards,
Pavel


On 26/06/2017 21:45, Markus KARG wrote:

Experts,

 

I know this is really late to discuss, but in fact, I doubt that it was a good decision to take WriterInterceptor out of the processing chain for SSE events.

 

In fact, today I set up a demo app and needed this, so I actually thought that this is a Jersey bug…!

 

Is there any good technical reason that made us strip WriterInterceptor from the processing chain (see below commit)?

 

Scenario where this is a problem: I wanted to add support for Optional<T> to demonstrate Java 8 support of JAX-RS. So I set up a WriterInterceptor which simply unwraps Optional to produce T, so all existing pre-2.1 EntityWriters for T will still work. Great. But fails with SSE! Hence, one must rewrite all WriterIntercepors if support for Optional<T> is needed with SSE. Very bad thing!

 

Why don't we use WriterInterceptors with SSE events?

 

Thanks

-Markus

 

From: Pavel Bucek [mailto:notifications@...]
Sent: Montag, 26. Juni 2017 09:38
To: jersey/jersey
Cc: Markus KARG; Author
Subject: Re: [jersey/jersey] SseEventSink invokes custom WriterInterceptor with wrong type, then throws NPE (#3592)

 

Please see this: jax-rs/spec@b4b0d91


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or mute the thread.

 

 

Join jaxrs-spec@javaee.groups.io to automatically receive all group messages.