Cerbenus
Cerbenus

Reputation: 213

How to design communication from a backend server to the frontend using JavaEE (EJB, JSF, PrimeFaces)

Normally, a frontend such as a web application (e.g. JSF) is used to retrieve data from a system and display it. User interaction is required: a user pulls up a website and e.g. clicks a button which in turn triggers a whole round trip process (view calls controller, controller informs model, model updates and notifies view via controller again).

In my setup I don't have any user interaction because my webapp is a view only. No user can do any CRUD actions (except Read). The "user interaction" comes so to speak from my backend: This system runs the entirety of business logic and receives user interactions through other channels than the webapp. However, all changes within the backend need to be somehow pushed into the webapp frontend.

I am using JavaEE, more precisely EJB 3.2, JSF 2.2 and PrimeFaces 5.1 (as well as JMS 2.x, WildFly 8.2, OmniFaces 2.0)

My backend communicates changes via JMS. I defined a MessageListener within my webapp that listens to all incoming changes. These changes are pushed into the model (EJBs).

As far as my knowledge goes should a model never know anything about it's view: the model is completely agnostic to the view. In JavaEE terms this means that my EJBs (model) should not inject any ManagedBeans (controller).

Now here is my question: How should the model inform the controller/view about changes, so that they get reflected within the view?

Upvotes: 2

Views: 1366

Answers (1)

Cerbenus
Cerbenus

Reputation: 213

We found a solution doing it in a "hybrid" mode. Here is what we came up with:

  1. Observer Pattern

    We used the classic Observer Pattern: A managed bean adds itself as listener to an EJB, which in turn is updated by the JMS message listener. Therefore, we made use of the @PostConstruct to add a listener as well as @PreDestroy to remove listener respectively whenever the managed bean gets created/destroyed. In other words, whenever the model gets updated, it informs its listeners, which are the managed beans (controller). But this doesn't yet update the view. Here comes the hybrid mode into play:

  2. PrimeFaces Push

    Once the model (EJB) is updated it has to push a notification to it's web clients via PrimeFaces Push. This way all clients know they have to go grab the changes:

    @ManagedBean(name = "notifier")
    @ApplicationScoped
    
    public class MarketDataPushNotifier implements Serializable {
    
    private final Logger log = LoggerFactory.getLogger(MarketDataPushNotifier.class);
    public final static String CHANNEL = "/notify";
    private final EventBus eventBus = EventBusFactory.getDefault().eventBus();
    
        public MarketDataPushNotifier() {
            // bean instantiation only
        }
    
        public void notifyListeners() {
            log.info("Sending UI update notification...");
            eventBus.publish(CHANNEL, true);
        }
    }
    

    Of course one can design the notify channel more fine grained. In the view (facelet) do:

    <p:remoteCommand name="updateTable" actionListener="#{bean.onUpdate}" update="table" process="@this" />
    
    <!--table snippet ommitted-->
    
    <p:socket onMessage="handleMessage" channel="/notify" autoConnect="true"/>      
    
    <script type="text/javascript">
        function handleMessage(update) {
            if (update) {
               updateTable();
            }
        }
    </script>
    

Hope that helps...

Upvotes: 1

Related Questions