Search code examples
javajava-websocket

How can I have a main method and websockets


I am looking for more information that I cannot find in my searches of the web. I am most likely searching for the wrong thing and I hope someone can correct me.

I have implemented a client(javascript)-server(java) websocket application. I have the connections working for several clients and now I want to start with some initial data created from a main method. However, the main method I have in my java application is not linked to the websocket endpoint. So if I create object instances when I run the application I want that information to be sent to the connected clients outside of the closed loop of session actions.

What I am describing sounds impossible to but I am certain I am just not explaining it properly.

Conclusion:

When dealing with websockets, how can I have an OnServerStartup so to speak so that I can either load saved data or create initial data?

Background:

I have implemented an annotated server endpoint using javax.websocket.server. The history which due to going out of order may have caused my quandry is that I started with the java application first and then I wanted to have a client UI to add, edit, delete data. So I went with a javascript frontend communicating with my java app via websockets. Now my app cannot communicate with the websocket connections or at least I do not know how yet.

Ideally what I am trying to do is have the useractions come in over the websocket and then I want to put the actions into a queue that my application then processes. When complete it sends the resulting changes to the dataset to all active sessions. There is a big issue here because I want to initialize saved data before allowing the websocket connections. Also if I have a timed event I want to be able to send all the updates to the connected sessions.


Solution

  • Here are three solutions. They might not help the original poster using Glassfish; I do not know. But they are answers to the posted question and might be of some use.

    I use embedded Jetty for my server. (Meaning the server is created in my Java code, so I have access to everything.)

    Answer 1: Have your websocket server endpoint listen to CDI events sent from your main thread. I have not done this, but I think it is a very clean concept.

    Answer 2: Have your endpoint "register" itself with the main thread.

    Create a static collection member in a core class:

    public class Core()
    ...
        public static List<MyEndpoint> webSockets = new ArrayList<MyEndpoint>();
    

    Endpoint adds self to collection:

    @ServerEndpoint(value="/path/")
    public class MyEndpoint
    {
        @OnOpen
        public void onWebSocketConnect(javax.websocket.Session session)
        {
            synchronized(Core.webSockets)
            {
                Core.webSockets.add(this);
            }
            ...
        }
    

    Back in Core you will iterate through webSockets to send messages. Synchronize the iteration, or better, copy webSockets and iterate throught the copy (standard synched collection practices). You will also need to handle removal from the List in @OnClose, and watch for closed sockets during the iteration.

    Note the synchronizations might be done in other ways as you need: Collections.synchronizedList() or CopyOnWriteArrayList are useful in different situations.

    Answer 3: Take control of constructing the endpoint instance. This is the most powerful of my choices, as you can construct it with data and external references.

    My implementation is JSR-356 (and Jetty) based, but there are hooks in other frameworks, too. Always slightly complicated.

    During server creation:

    context = new ServletContextHandler(ServletContextHandler.SESSIONS);
    // ... more server creation
    
    ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
    
    wscontainer.addEndpoint(ServerEndpointConfig.Builder.create(MyEndpoint.class, "/path/")
        .configurator(new MyConfigurator(aSocketManagerObject))
        .build());
    

    Where:

    public class MyConfigurator extends javax.websocket.server.ServerEndpointConfig.Configurator
    {
        private final ManagerObject aSocketManagerObject;
    
        public EventsConfigurator(ManagerObject aSocketManagerObject)
        {
            this.aSocketManagerObject = aSocketManagerObject;
        }
    
        @Override
        public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException
        {
            return (T) new MyEndpoint(aSocketManagerObject); // This constructor to be added to your MyEndpoint class
        }
    }
    

    The endpoint now has a reference to aSocketManagerObject and can communicate with it freely to acquire data, register as listener, whatever.

    Bhaskar S. provides a robust implementation of Answer 3 at Introduction to WebSockets

    Edit:

    More info on the CDI approach is in question, How to get an existing websocket instance.

    And Accessing HttpSession from HttpServletRequest in a Web Socket @ServerEndpoint includes a long discussion about instances of endpoints.