Search code examples
javaakkaactorvaadin7

Vaadin notification in UI with Akka actor


I'm working on integrating an akka actors based backend within a vaadin application. the problem i'm facing is i dare say simple (bear with me i'm a vaadin first timer :P).

Project structure (simplified)
2 modules front and backend.

  • my backEnd has an actor system that manages all the backend actors.
    -> and a service class that has all relevant methods for necessary computations.

  • my frontEnd contains a single UI with a bunch of views.
    -> and a second actor system that manages one UIActor (since backend actors can't access the UI)
    needless to say the back-end is part of the front-end's dependencies.

The problem
In one of my views i have a click event handler that sends a message through the UIActor to one of the backEnd actors which in turn answers back once computations are done.
this is the "onReceive" method in the UIActor

public void onReceive(Object message) throws Exception {

    if (message  == "test") {
        System.out.println("showing notification by "+currentUI);
        currentUI.testMethod();

    } else if (message instanceof MyUI) {
        System.out.println("this is the ui actor test request forwarded");
        currentUI = (MyUI) message;
        backEndAgent.tell("check",getSelf());
    } else {
        unhandled(message);
    }
}

the currentUI contains the return value of (MyUI) getCurrent(), sent as an message by the handler, in case you're wondering.

as for the testMethod it's pretty basic.

public void testMethod() {
        Notification.show("Success", Notification.Type.HUMANIZED_MESSAGE);
    }

unfortunately this call generates a null pointer in the Show method resulting from the Page.getCurrent() returning null.

So my question is why do i get a null pointer here?
also in my actor i send the currentUI reference as message because otherwise calling the getCurrent inside the UIActor returns null. any idea why?
EDIT
This is the new test method the page parameter is currentUI.getPage()

    public void testMethod(final Page page) {
            this.access(new Runnable() {
                @Override
                public void run() {
                    new Notification("Success",Notification.Type.HUMANIZED_MESSAGE).show(page);

                }
            });
        }

this solves the problem somehow but only shows the notification after i click the button a second time but not the first time.
This is driving me crazy.


Solution

  • Notification.show() uses Page.getCurrent() to fetch the current page. Page.getCurrent() is a ThreadLocal which is set during a traditional HTTP request-response cycle meaning that Notification.show() works (doesn't return null) during the HTTP request-response thread.

    In you case Notification.show() is called outside the HTTP request-response thread and that's why it returns null. If I understand correctly from your code, currentUI is reference to your current UI and it's not null. In that case you could change your code from

    public void testMethod() {
        Notification.show("Success", Notification.Type.HUMANIZED_MESSAGE);
    }
    

    to

    public void testMethod() {
        new Notification("Success", Notification.Type.HUMANIZED_MESSAGE).show(getPage());
    }
    

    The modified version doesn't use Page.getCurrent() anymore but gets the current page from the getPage() method of your UI instance.

    EDIT. Forgot to add important information related to actors / external threads accessing Vaadin code:

    The actor calling the method to show a notification is an external thread from the Vaadin point of view. Vaadin assumes that access to Vaadin components and Vaadin related other objects is locked/syncronized properly. A proper locking/syncronization is in place when the traditional HTTP request-response thread is used but when an external thread (e.g. actor) is accessing those, the developer must ensure locking. One way to ensure locking is to use UI.access:

    getUI().access(new Runnable() {
        @Override
        public void run() {
            // insert your code accessing Vaadin related objects here
        }
    });
    

    The reason why you see the notification after clicking a button is that browser doesn't know that there is something to show when an actor calls Notification.show. The request caused by a button click pulls changes from the server to the browser and then the notification is show. To see changes from actors / external threads without user interaction (e.g. a button click) the application must use polling or push. To enable polling, setPollInterval for your ui.