Search code examples
javaservletsmvvmzk

ZK Framework: notify ViewModel from a servlet


I have a ZK MVVM application with a standard UI bound to a ViewModel and one additional servlet for communication with an external app. I need to do the following: when the additional servlet receives a http request, I need to notify the ViewModel and display some data to the user via server-push.

I tried to notify the ViewModel from the servlet via a GlobalCommand by calling BindUtils.postGlobalCommand and also via an event queue using EventQueues.lookup but in both cases, I get an IllegalStateException with message: Not in an execution.

I also tried to keep a reference to the ViewModel in the servlet and directly calling its method, in which the ViewModel tried to call BindUtils.postNotifyChange but I got the same result.

Is there some way I can achieve the desired behavior?


Solution

  • As commented you should review your architecture. But for the sake of the answer you could use the Observable java built-in pattern.

    You can create a service (assuming you are using Spring) which extends Observable. Then you declare a Window to implement the Observer pattern and to enable the server push on his desktop. Last but not least you register the Window (the observer) to the service.

    When the http request arrives to your servlet, you call a method in the service which will update ALL of its observers (every instance of your Window).

    Here becomes a bit tricky. If you don't want to alert every instance of your Window, inside the http request must be present a reference to the instance to be updated. If I'm supposing right and the http message is an asynchronous response to a previous http request, you can embed in some way the Window Uuid in it to receive it back. In this way, when the observers are triggered, you can "understand" which was the initial caller.

    After this you can use the Executions.schedule to fire your specific ZK event.

    In summary:

    Declare a Observable spring service

    @Service
    public class MyServiceImpl extends Observable implements MyService {
    
        @Override
        public void manageResponse(String response) {
    
          this.setChanged();
          this.notifyObservers(response);               
        }
    }
    

    Declare an Observer ZK Window (or Component, or SelectorComposer)

    public class WndMain extends Window implements Observer {
    
        private static final long serialVersionUID = -216014257162713363L;
    
        private String _uuid;
        private Desktop _desktop;
        private int _notificationX;
        private MyService myService = "*Locate the service in Spring context*";
    
    
        public void onCreate(){
            super.onCreate();
            this._uuid = this.getUuid();
            this._desktop = getDesktop();
            _desktop.enableServerPush(true);
            myService.addObserver(this);
        }
    
        @Override
        public void update(Observable o, Object arg) {  
            //Check which Observable notified this Observer
            if(o instanceof MyService){
                String response = (String) arg;
                if(response.equals(_uuid)){ 
                    //Enter the Desktop and fire the Notification
                    Executions.schedule(_desktop, new NotificationEventListener(), new Event("onHttpResponse", this, null));
                }   
            }
        }
        private class NotificationEventListener implements EventListener<Event>{
    
            @Override
            public void onEvent(Event event) throws Exception {
              if(event.getName().equals("onHttpResponse")){
                    Clients.showNotification("Response arrived!", Clients.NOTIFICATION_TYPE_INFO, event.getTarget(), notificationX- 200, 0, 2000);
            }
        }
    
        public void onClientInfo(ClientInfoEvent event){
            this._notificationX=event.getDesktopWidth()-80;
        }
    }
    

    EDIT

    The manageResponse(String response) method is called within a JAXB REST servlet in my case:

    @Path("myPath/servlet/response")
    public class MyServiceREST {
    
        private MyService myService = "*Locate the service in Spring context*";
    
    
        @POST
        @Consumes(MediaType.WATHEVER)
        public Wathever receiveResponse(Whatever response){
            if(response!=null){     
                myService.manageResponse(response.getWindowUuid());
            return Response.status(201).build();
            }
            return Response.status(400).build();
        }
    }