1

I have encountered some issues with accessing the PushContext instance.

I would like to send the messages inside a thread that runs separately from the managed bean. By being injected via @Injected @Push, I can not. Does anyone have any idea how I can do this?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555

1 Answers1

3

Accessing OmniFaces PushContext instance in thread

The PushContext can only be injected in a container managed artifact residing in the WAR (e.g. @Named, @WebServlet, @WebFilter, @WebListener, etc), not elsewhere (i.e. absolutely not in @Stateless, @Stateful, @Singleton, etc).

I would like to send the messages inside a thread that runs separately from the managed bean

It will only work if the thread is managed by the container, such as the one initiated by EJB's @Asynchronous. It won't work if the thread is unmanaged (i.e. manually created using Thread). This is in detail explained in Is it safe to start a new thread in a JSF managed bean?

When EJB's @Asynchronous is properly used, then simply follow the examples in <o:socket> documentation. You can find them in EJB design hints section.

The below extract from the documentation shows how to push an application scoped socket by an @Asynchronous EJB method which is in turn invoked by some background job (e.g. @Schedule).

In case you'd like to trigger a push from EAR/EJB side to an application scoped push socket, then you could make use of CDI events. First create a custom bean class representing the push event something like PushEvent below taking whatever you'd like to pass as push message.

public final class PushEvent {

    private final String message;

    public PushEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

Then use BeanManager.fireEvent(Object, java.lang.annotation.Annotation...) to fire the CDI event.

@Inject
private BeanManager beanManager;

public void onSomeEntityChange(Entity entity) {
    beanManager.fireEvent(new PushEvent(entity.getSomeProperty()));
}

Finally just @Observes it in some request or application scoped CDI managed bean in WAR and delegate to PushContext as below.

@Inject @Push
private PushContext someChannel;

public void onPushEvent(@Observes PushEvent event) {
    someChannel.send(event.getMessage());
}

The below extract from the documentation shows how to push to a session or view scoped socket by an @Asynchronous EJB method which is in turn invoked by some JSF action.

In case the trigger in EAR/EJB side is an asynchronous service method which is in turn initiated in WAR side, then you could make use of callbacks from WAR side. Let the business service method take a callback instance as argument, e.g. the java.util.function.Consumer functional interface.

@Asynchronous
public void someAsyncServiceMethod(Entity entity, Consumer<Object> callback) {
    // ... (some long process)
    callback.accept(entity.getSomeProperty());
}

And invoke the asynchronous service method in WAR as below.

@Inject
private SomeService someService;

@Inject @Push
private PushContext someChannel;

public void someAction() {
    someService.someAsyncServiceMethod(entity, message -> someChannel.send(message));
}
Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555