Vaadin CDI: @UIScoped / @Observes & javax.inject.Provider

Hello guys,

I’m using a mvp-pattern implemented with the Vaadin CDI-Addon as it’s described
here (via github)
.
With the @UIScoped annotaion I’m able to use @Observes and javax.enterprise.event.Event for commincation between the views and presenter.
Now I would like to use multipe instances of a mvp-component, like chat tabs or something else. But due to the restriction of
@UiScoped the javax.inject.Provider interface isn’t able to provide unique instance.
Ist there any way to create multiple mvp-components of an class and also use the @Observes annotation and events?

I can provide an minimal example on demand, if the github code isn’t clear enough.

Thank you.

Hey buddy, I’m not exactly sure if I understand what you tried to achieve but I’ll try to give few pointers and hope they help.

javax.enterprise.event.Event is related to the currently active scopes / context in such a way that if you fire an event from ViewScoped, UIScoped or SessionScoped beans the event can only be observed in those beans which share the same or higher scope within the same context. This is, if from ViewScoped you fire an event it can only be observed in other ViewScoped, UIScoped, SessionScoped or ApplicationScoped beans belonging to the same context. With context I mean “user session”. This effectively means that you cannot fire an event from UI to UI.

The javax.inject.Provider that you’re referring to is a way to programatically inject a bean. It’s especially handy if you want to inject the bean inside your code instead of injecting it with a static injection point by using @Inject. Provider behaves same way as other injections in general which means that if you attempt to inject bean which is UIScoped the actual injected bean will always be the same as long as you’re “inside” the same UI.

Your problem for making the chat application is in general that CDI events cannot target beans in “sibling scopes” which don’t share a context. This simply means that you cannot fire an event from one UI to another. What you can do on the other hand is that you make a “global” component which is for example in @ApplicationScope. This bean is shared between all the other beans belonging to same CDI deployment. What you could do there is that you have a collection or map of available UI’s participating to the chat session and when you detect that an event targets one of these UI’s you would call ui.access(Runnable) for that particular UI. Calling UI.access will activate the context of the particular UI allowing it to work as part of the process.

In short, I would advice the following:

  1. ViewScope → UIScope → SessionScope → ApplicationScope is the only way how events propagate but even then they cannot cross context boundary from one user session to another user session or from one ui scoped bean to another ui scoped bean (if those ui scoped beans belong to different UI’s, which are in different contexts)

  2. Events cannot propagate between sibling beans meaning that you cannot fire an event from UI to UI.

  3. To make a chat application you need to have a component in your system that acts as singleton @ApplicationScoped. This component can receive events with @Observes from all other beans as its effectively part of everybody elses context as well.

  4. The @ApplicationScoped component needs to maintain information about other UI’s participating the chat and it needs to access the particular recipient UI with UI.access(Runnable) method. Inside the runnable you provide the actual logic that you want to execute when event is propagated. This takes care of activating the proper context as well as thread safety.

  5. Provider is just a programmatic way to inject the bean instead of using static @Inject based injection point. It will follow the same scoping rules as other injections as well, hence if inside a UI- or ViewScoped bean you attempt to inject the UI you will always get the same instance.

Hope the above thoughts help!

cheers,
Peter

I drew a little illustration on how scoping works.


https://drive.google.com/file/d/0B7BydXnTdb0vTHpIbFVra19PTVE

Idea would be that in higher level bean you have a reference to the head of “other user’s stack”.

In world of Spring there is a global event bus called ApplicationEvents, I don’t think CDI has equivalent for that unfortunately.

Thx for you help i made some wrong assumptions. Now i understand the concept.