4

How can I access a HttpSession object inside an annotated @WebSocket class in Jetty 9?

I found how to do it using @ServerEndpoint annotation, like here: HttpSession from @ServerEndpoint

Using the @WebSocket annotation, like in the class bellow, how can I do it?

@WebSocket
public class AuctionWebSocket {

    // NEED TO ACCESS HttpSession OBJECT INSIDE THESE METHODS:

    @OnWebSocketConnect
    public void onConnect(Session session) {
        System.out.println("onConnect...");        
    }

    @OnWebSocketMessage
    public void onMessage(String message) {        
        System.out.println("Message: " + message);
    }    

    @OnWebSocketClose
    public void onClose(int statusCode, String reason) {
        System.out.println("onClose...");        
    }

    @OnWebSocketError
    public void onError(Throwable t) {
        System.out.println("onError...");        
    }    
}

Inside the method onConnect(Session session), I tried to call session.getUpgradeRequest().getSession() which always returns null.

For sake of information, here is how I start embedded Jetty 9:

public class Main {

    public static void main(String[] args) throws Exception {

        String webPort = System.getenv("PORT");
        if (webPort == null || webPort.isEmpty()) {
            webPort = "8080";
        }

        Server server = new Server(Integer.parseInt(webPort));

        ClassList classlist = org.eclipse.jetty.webapp.Configuration.ClassList.setServerDefault(server);
        classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
                "org.eclipse.jetty.annotations.AnnotationConfiguration");

        WebAppContext wac = new WebAppContext();
        String webappDirLocation = "./src/main/webapp/";

        wac.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*/classes/.*");
        wac.setDescriptor(webappDirLocation + "/WEB-INF/web.xml");
        wac.setBaseResource(new ResourceCollection(new String[]{webappDirLocation, "./target"}));
        wac.setResourceAlias("/WEB-INF/classes/", "/classes/");
        wac.setContextPath("/");
        wac.setParentLoaderPriority(true);

        /*
         * WebSocket handler.
         */
        WebSocketHandler wsh = new WebSocketHandler() {

            @Override
            public void configure(WebSocketServletFactory wssf) {
                wssf.register(AuctionWebSocket.class);
            }
        };

        ContextHandler wsc = new ContextHandler();
        wsc.setContextPath("/auction-notifications");
        wsc.setHandler(wsh);

        ContextHandlerCollection chc = new ContextHandlerCollection();
        chc.setHandlers(new Handler[]{wac, wsc});

        server.setHandler(chc);

        server.start();
        server.join();
    }

}

Let me know if you need more information.

Any help will be appreciated.

Thanks in advance.

Community
  • 1
  • 1
reinaldoluckman
  • 6,317
  • 8
  • 42
  • 50

1 Answers1

5

You'll want to use the WebSocketCreator concepts.

First you set the WebSocketCreator of your choice in the WebSocketServletFactory that you configure in your WebSocketServlet

public class MySessionSocketServlet extends WebSocketServlet
{
    @Override
    public void configure(WebSocketServletFactory factory)
    {
        factory.getPolicy().setIdleTimeout(30000);
        factory.setCreator(new MySessionSocketCreator());
    }
}

Next, you'll want to grab the HttpSession during the upgrade and pass it into the WebSocket object that you are creating.

public class MySessionSocketCreator implements WebSocketCreator
{
    @Override
    public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
    {
        HttpSession httpSession = req.getSession();
        return new MySessionSocket(httpSession);
    }
}

Finally, just keep track of that HttpSession in your own WebSocket.

@WebSocket
public class MySessionSocket
{
    private HttpSession httpSession;
    private Session wsSession;

    public MySessionSocket(HttpSession httpSession)
    {
        this.httpSession = httpSession;
    }

    @OnWebSocketConnect
    public void onOpen(Session wsSession)
    {
        this.wsSession = wsSession;
    }
}

Of note: the HttpSession can expire and be scavenged and cleaned up while a WebSocket is active. Also, the HttpSession contents at this point are not guaranteed to be kept in sync with changes from other web actions (this mostly depends on what Session storage / caching technology you use on the server side)

And one more note: resist the urge to store / track the ServletUpgradeRequest object in your Socket instance, as this object is recycled and cleaned up aggressively by Jetty proper.

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • I just want to access the logged user that is in the `HttpSession` to maintain a map of users and their respective websocket session for further communication. As a workaround, in the `onopen` javascript callback, I send a message to the websocket with the user id, like this `ws.onopen = function () { ws.send("onopen:" + sessionId_); };` and, then, I make the association between the user and websocket session on the server. Do you think this approach is better than access the `HttpSession` via `WebSocketCreator` concept? – reinaldoluckman Dec 29 '14 at 23:40
  • I have somewhat similar requirement, (my requirement is, if same user open app in multiple tabs, then multiple websocket session is created, so if user logs out in one tab, I want to logout user from all tabs), can you please help how you solved the problem? – Jayesh Feb 26 '17 at 04:53
  • @Jayesh please open a new question for new topics – Joakim Erdfelt Feb 27 '17 at 14:08
  • `HttpSession httpSession = req.getSession();` returns null to me, I think because the request has already been upgraded. I solved with `HttpSession httpSession = req.getHttpServletRequest().getSession();`. – Syco Nov 18 '20 at 11:49
  • Thank you for contributing this answer! But I'm left wondering about the lifecycle of the HttpSession object. Your warnings at the end are more than relevant. The best answer should be not only you *can* do this... but *should* you do this? – Robert Jan 16 '22 at 13:52