javaswingjframestatic-methodsjava-websocket

Set data from WebSocket Server to JFrame components


I have WebSocket server:

@ServerEndpoint(value = "/demoApp")
public class MyWebSocketServer {
    @OnMessage
    public String onMessage (String message, Session session) throws IOException {
        for(Session session1 : Main.sessions){
            if(!session1.equals(session)){
                session1.getBasicRemote().sendText(message);
            }
        }
    }
}

It notifies everyone connected except the one who sent the message.

And I have a client side:

@ClientEndpoint
public class WebSocketClient {
    @OnMessage
     public void onMessage (String message, Session session) {
        System.out.println("[SERVER RESPONSE]: " + message);
        MyJFrame.setButtonGrid(message);
    }
}

My JFrame:

public class MyJFrame extends javax.swing.JFrame {

    //some generated stuff

    public static void setButtonGrid(String message){
        Component[] components = this.getContentPane().getComponents();
        int i = 0;
        for(Component component : components){
            if(component instanceof JButton button){
                if("Restart".equals(button.getText())){
                    continue;
                }
                if(message.charAt(i) == '*'){
                    button.setText("");
                }
                else {
                    button.setText(message.charAt(i) + "");
                }
            }
        }
    }
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new TTTForm().setVisible(true);
            }
        });
        ClientManager clientManager = ClientManager.createClient();
        URI uri = null;
        try {
        uri = new URI("ws://localhost:8080/java/demoApp");
        session = clientManager.connectToServer(WebSocketClient.class, uri);
        } catch (URISyntaxException e) {
        e.printStackTrace();
        } catch (DeploymentException e) {
        e.printStackTrace();
        } catch (InterruptedException e) {
        e.printStackTrace();
        }
    }
}

But in setButtonGrid method I can't get the components of my JFrame, obviously because it's static method. But if it is not static, then I will not be able to call it from the class of my WebSocket client. So how do I change the text in the JFrame buttons when information comes from the WebSocket server?


Solution

  • The essential problem here is how do you have the visible GUI instance communicate with the socket client? One way to do this would be to pass an instance of the GUI into the web socket client via its constructor. So the web socket client would need a gui field and a constructor that takes the GUI as a parameter, and use this to set the field.

    @ClientEndpoint
    public class WebSocketClient {
        private MyJFrame myJFrame; // add a GUI field
    
        // add a constructor that allows passing in the GUI as a parameter
        public WebSocketClient(MyJFrame myJFrame) {
            this.myJFrame = myJFrame;
        }
    

    Then, when creating the session, call the constructor above and then call the clientManager.connectToServer(...) method overload that accepts an object as first parameter, your WebSocketClient object, rather than takes the class as parameter:

    MyJFrame myJFrame = new MyJFrame();
    
    // ....
    
    WebSocketClient webSocketClient = new WebSocketClient(myJFrame);
    
    // and then initiate the session using the above client
    // This uses an overload of the connectToServer method that takes an object
    session = clientManager.connectToServer(webSocketClient, uri);
    
    

    Then, with this GUI instance, you could pass data from the client into the GUI, and visa-versa.

    Say you had a GUI that looked like so:

    public class MyJFrame extends javax.swing.JFrame {
    
        // ... code redacted ...
    
        public void fromServer(final String message) {
            // note that fromServer will of necessity be called *off* of the Swing event thread, the EDT
            // and so you will want to take steps to make sure that it is only used *on* this thread:
            EventQueue.invokeLater(new () -> {
                // use message from the GUI here
            });
        }
        
        public static void main(String args[]) {
            java.awt.EventQueue.invokeLater(() -> {
                public void run() {
                    MyJFrame myJFrame = new MyJFrame();
    
                    ClientManager clientManager = ClientManager.createClient();
                    URI uri = null;             
                    try {                   
                        uri = new URI("ws://localhost:8080/java/demoApp");
                        // create our WebSocketClient, passing in the GUI
                        WebSocketClient webSocketClient = new WebSocketClient(myJFrame);
                        
                        // and then initiate the session using the above client
                        // This uses an overload of the connectToServer method that takes an object
                        session = clientManager.connectToServer(webSocketClient, uri);
                        myJFrame.setVisible(true); // start the GUI
                    } catch (URISyntaxException e) {
                        e.printStackTrace();
                    } catch (DeploymentException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
    

    Your web socket client could look something like so:

    @ClientEndpoint
    public class WebSocketClient {
        private MyJFrame myJFrame;
        
        public WebSocketClient(MyJFrame myJFrame) {
            this.myJFrame = myJFrame;
        }
        
        @OnMessage
         public void onMessage (String message, Session session) {
            System.out.println("[SERVER RESPONSE]: " + message);
            // MyJFrame.setButtonGrid(message); // don't call a static method but rather an instance method
            myJFrame.fromServer(message);
        }
    }
    

    Note: