cdi
cdi copied to clipboard
Temporary Observers
When bridging between synchronous and event-driven APIs, there comes a situation when a synchronous method sends a request in the form of a CDI event and then needs to wait until a responding CDI event was received (i. e. it shall not just wait for the request event to be sent but really wait for an answering event was received). After that, the synchronous method can read that responding event and provide its own return value to the caller.
Unfortunately I have not found any easy way to write code for such a situation in CDI (maybe I missed something). I could imagine passing a Future around in the CDI event payload where the orignal thread waits for and where an observer method would fulfil - but that sounds pretty complex.
What I could imagine would be something like "temporary observers", i. e. before the synchronous method sends the original request CDI event, it registeres a lambda function as a CDI event observer for the expected answer. Once that lamba event fires, it unregisters itself before it returns the result to the blocked caller. I know that programmatic global observers are aready possible as extensions, but those cannot interact with the waiting thread inside of the synchronous caller method.
Is this already possible or is it planned for the future or is there another simple solution how to make a synchronous non-static method wait for an event in such a dynamic way?
Hi Markus, I'm not sure I fully understand but keep in mind that if you fire an event synchronously, e.g. via Event.fire()
then the method invocation completes once all sync observers (@Observe
) are notified and if any of the observer methods throws an exception the processing of the event is aborted and the exception is rethrown. If you fire an even asynchronously, i.e. via Event.fireAsync()
then the returned CompletionStage
is completed when all async observers (@ObservesAsync
) are notified.
Martin, sorry if my description is not clear enough. Let me rephrase:
- Step 1: Some thread T calls a Java method M which takes a parameter P and shall return a result S.
- Step 2: The method M in turn sends an event Q(P).
- Step 3: The thread T waits inside of M until some event R(S) in received (not: sent).
- Step 4: The thread T awakes and leaves the method M with the result R.
So the problem is not about asynchronous or synchronous sending / receiving of events, it is solely about how to wait in the middle of method M until event R with payload S is incoming, so that method M can return S.
So CDI events are not designed for point-to-point messaging. It's more a publish/subscribe style of messaging where observers represent the subscribe part and Event#fire()
represents the publish part. For sync events all observers are always notified unless an exception is thrown. For async event all observers are always notified. It does not make sense to register a "result callback". That said, you can leverage the message payload to process/collect the results. But keep in mind that the "result" could be set by multiple observers (and in case of async observers it should be also thread-safe).
Well, it's one thing what CDI was designed for, and another thing what reality forces us to do: If you MUST actively wait for a specific event to happen, you still have the same problem -- even if that event is not a result at all. So to make the question more simple: How to suspend a thread until a specific event arrived WITHOUT forcing specific payloads?
What Martin says is that CDI functionality won't cut it for your scenario. If you wanted CDI to handle it, you'd have to turn it around - instead of thread A waiting for thread B to complete, you'd want thread B to execute the logic and fire an event once done. Whatever thread A did would then become a body of an (async) observer method waiting for notification from B. But I understand this might not be an option for you; just tossing ideas around :-)
Outside of that, the most rudimentary thing I can think of is using some synchronization construct such as CountDownLatch
which the other thread releases once done. But I am sure there is some way more elegant solution.
You probably want to look at java.util.concurrent.Lock and Lock.Condition. That is a mechanism that's explicitly made for cases in which one thread has to wait for another thread to reach some specified state.
I could imagine passing a Future around in the CDI event payload where the orignal thread waits for and where an observer method would fulfil - but that sounds pretty complex.
I actually find that pretty straightforward. If the use case is "bridging between synchronous and event-driven APIs", I would expect the glue code to be somewhat weird.
CDI could add request/response messaging (in addition to current publish/subscribe), or it could add "temporary observers" as you describe them, but frankly -- in both cases, I believe you'd end up with worse code than if you just pass around a future.