9

I have a jsf view which shows some data from a managed-bean (viewscope) in a table, that is retrieved remotely.
Currently the data is updated via polling from the client-view using primefaces poll component.

This is not sufficient enough, since to much traffic is sent to client and now that primefaces supports server-push I only want to reload the data and push it to the client-view if data has been changed.

This should be realized via polling from the web-tier to application tier calling a method like hasChanged(...). If data is changed the web-tier pushes a notification to client to reload data.

Current client poll

client >> web-tier >> app-tier

client asks web-tier via ajax for data which again asks app-tier for data and updates view

Wished web-tier poll and push

client << web-tier >> app-tier

web-tier polls app-tier if data has changed and reloads on behalf and informs (pushes) client to update view

Approaches:

What is the best approach to realize the polling at the managed-bean in the web-tier?

  1. TimerTask in the managed-bean
    Spawning threads in a JSF managed bean for scheduled tasks using a timer
  2. Additional EJB with Schedule annotation
  3. Additional EJB with TimerService
  4. other?

Edit:

Architecture: (3-tier)

  • Server1: database
  • Server2: app-tier (EAR with Remote EJB + Hibernate)
  • Server3: web-tier (WAR with JSF 2.0 + Primefaces 3.4)
  • Client: Browser

Architecture

Community
  • 1
  • 1
djmj
  • 5,266
  • 4
  • 47
  • 89
  • i think on many ocations you dont want to push the actual data to be updated, just a notification that the client should request a partial update on some part of the page. this way you may avoid implementing security on the channels. also you can do the push event drriven from the ejb-tier directly to comet/atmosphere. – Aksel Willgert Oct 25 '12 at 05:47
  • No i dont want to push the actual data, just the notification as you suggested. You mean from the ejb that resides on the web-tier? – djmj Oct 25 '12 at 14:12
  • I did the push ejb in ejb-tier. (added atmosphere dependencies to ejb.jar). See: http://stackoverflow.com/questions/12294909/access-pushcontext-from-ejb-tier/12998971#12998971 – Aksel Willgert Oct 25 '12 at 14:22
  • I don't know what you mean by ejb-tier. I have a 3-tier architecture, with an EAR and a WAR. Do you mean app-tier (EAR) when you use ejb-tier? – djmj Oct 25 '12 at 14:27
  • maybe im using the wrong terminology. ear i consider the archive where you package the different artifacts of your tiered app. webtier goes into .war ejbs and entities goes into ejb.jar then both go into .ear – Aksel Willgert Oct 25 '12 at 15:00
  • I added some information about my architecture. One Server the app-tier has a EAR with remote EJB's and the web-tier Server only has a war. – djmj Oct 25 '12 at 17:42
  • Ah k, now i understand. I havnt tried this setup, but if you are only going to push from EJBs, i think you could more or less follow the solution i linked to above. Difference that atmosphere has to run on Server2-app-tier and in web.xml set push-server to point to server2 instead of web-server primefaces.PUSH_SERVER_URL http://localhost:8080 – Aksel Willgert Oct 25 '12 at 17:57
  • Isn't atmosphere a push framework from web-tier to client including ajax? – djmj Oct 25 '12 at 18:02
  • I think (havnt tried)it can run on separate server, iow doesnt need to be bundled with the .war – Aksel Willgert Oct 25 '12 at 18:28
  • Atmosphere as part of ear on server2 should work, But maybe you dont want users to access that server So the alternative would be to put it on server3, but im not sure in what way you may talk with atmosphere then. Think you can use JMS, but havnt tried http://i.stack.imgur.com/Ux515.png – Aksel Willgert Oct 25 '12 at 19:22
  • No i dont want to access this server directly, since this would destroy the 3-tier paradigm. Communication must pass the web-tier (server3 (your A? in the image)That's why i asked my question on how to realize the polling between web-tier and app-tier. – djmj Oct 25 '12 at 19:25

1 Answers1

3

Based on my experience, I can recommend two routes, Spring Integration and CDI Events. I'd recommend the Spring route, but based on your current stack, I think CDI events have you covered. They cleanly help you achieve the Observer/Observable pattern and you can pull of a clean separation of tiers too. I must warn you though, this approach is effective only for a small-medium use case. Consider the following:

  1. Design and implement an Event class that encapsulates all the information that is required to be delivered to consumers. Let's call it a FundsTransfer event Your event implementation should contain enough information to enable listeners filter only for events of interest. A simple POJO

    class FundsTransfer {
    
        BigDecimal transferValue;
        Date transferDate;
        int transferCurrencyCode;
    
        public FundsTransfer(BigDecimal transferValue, Date date, int currencyCode) {
            //set accordingly
        }
        //setters and getters
    }
    
  2. Implement a business object at the business layer, call it Notifier. The function of polling should be delegated to this object. It will be in charge of creating and publishing objects of type event in response to changes on the server side. Depending on your requirements, this object could be a singleton to handle all Event types or you could have a group of Notifier types polling for different events.

    //A sample implementation of your Observer object :
    @Singleton //Defines a singleton EJB
    public class PollerService {
    
        @Inject
        Event fundsTranferNotifier;
    
        //this annotation specifies that the polling method should run every second.
        @Schedule(second = "*/1", minute = "*", hour = "*", persistent = false)
        public void pollIt() {
            boolean found = this.pollingMethod(); // pollingMethod() will do the actual polling
            if (found) {     //based on the outcome of the polling method, fire the notifier  method
                blowWhistleOnTransfer();
            }
        }
    
        public void blowWhistleOnTransfer() {
            //this is the broadcast event.
            fundsTransferNotifier.fire(new FundsTransfer(new BigDecimal("100000", new Date(), 855));
        }
    }
    

    In the code above, I've used a Timer EJB as my Observer. See this for an introduction to EJB timers. Again, the Observer object will live in the app tier

  3. The client tier will each have access to an listener object that will be notified when an event of interest occurs (that is has been published by a Notifier type). Then the listener can issue a push based on this event. Your listener object could be a POJO with @Named CDI annotation. In your listener object, simply implement a method with an @Observes annotation, with a parameter of the type of event the listener is interested in:

    public void onNewTransfer(@Observes FundsTransfer transfer) {
        if (transfer.compareTo(new BigDecimal("150000")) > 0) {
            //push to view.
        }
    }
    

    The above filtering is still quite crude compared to the message filtering options that CDI provides. As you can see from the tutorial I referenced earlier, you could create CDI qualifiers that can give you finer grained filtering on messages. Like I stated earlier, this is a bit heavy for large scale deployment, in which case I'd advise the spring integration route, if you're up to take the dependency on.

In summary, the model of the system will be:

            One Poller(Notifier) Object (In the app layer)
                    |
                    |
                    |
            Multiple Listener Objects (In the web tier)
---------------------------------------------------
|   |    |    |    |   |   |     |   |    |    |  |  
Tiny
  • 24,933
  • 92
  • 299
  • 571
kolossus
  • 19,953
  • 3
  • 45
  • 94
  • Is this Observer pattern working between the two different servers app-tier and web-tier (Remote Observer pattern)? `Implement a business object at the business layer, call it Notifier. The function of polling should be delegated to this object.` My initial question was how to solve the polling? I am little confused or have not enough experience for your answer. – djmj Nov 01 '12 at 18:05
  • @djmj, the `Notifier` object will be in the app tier. It is supposed to do the polling/checking for changes. based on it's findings, it will broadcast a message throughout the JEE context. The `Observer` object will receive an `Event` instance/notification based on the `Notifier`'s broadcast. The `Observer` object will be in the web tier and based on the `Event` instance, you can then use the primefaces push to effect the change in the client side. – kolossus Nov 01 '12 at 20:45
  • @djmj, I've added more implementation details to my answer. I may have caused your confusion, the object in the application tier is the observer and will do the actual polling. The objects in the web tier are simply listeners, to listen for `Events` generated in the application/business tier. The listeners are to be injected into your JSF managed bean(s) – kolossus Nov 01 '12 at 21:19
  • I created an singleton ejb called `Notifier` in my `app-tier` with scheduled method and event `Event` (Periodically fires the event and prints to console). In my `web-tier` I created an Stateful ejb `Listener` with the `@Observes String str` method which prints out the String to console. That `ListenerEJB` is injected using `@EJB` into a managed-bean which is invoked in my view. Of cause nothing happens. How should that EJB get notified by a remote singleton? For all previously remote EJB accesses I had to use a jndi lookup to establish connection, and what should I do in this case? – djmj Nov 02 '12 at 23:28
  • How should I establish the connection between the remote ejb and the web-tier in combination with the `@Observes` annotation? – djmj Nov 05 '12 at 13:10
  • @djmj, sorry, I just fully understood this. You cannot reference an EJB that is physically deployed outside of an application server via injection. The only platform independent way to do this is via boilerplate JNDI lookups.Depending on your application server, you may be able to pull it off. I found [this for glassfish](http://stackoverflow.com/q/3779748/1530938). Other than that, you will have to implement it in other vendor specific ways – kolossus Nov 05 '12 at 16:35
  • Thanks for the link. I will look into this the coming week. – djmj Nov 06 '12 at 23:48