it-swarm.com.de

Zugreifen auf ServletContext und HttpSession in @OnMessage eines @ Server-Endpunkts JSR-356

Ich muss die ServletContext aus einem @ServerEndpoint holen, um Spring ApplicationContext zu finden und nach einem Bean zu suchen.

Im Moment ist mein bester Ansatz, diese Bean im JNDI-Namenskontext zu binden und in der Variablen Endpoint nachzuschlagen. Jede bessere Lösung ist willkommen.

Ich suche auch nach einer sinnvollen Möglichkeit, Servlets HttpSession mit Websocket Session zu synchronisieren.

25
Sombriks

Das Servlet HttpSessionist in JSR-356 verfügbar durch HandshakeRequest#getHttpSession() , das wiederum verfügbar ist, wenn eine Handshake-Anforderung direkt vor @OnOpen eines @ServerEndpoint erfolgt. Der ServletContextist wiederum nur über HttpSession#getServletContext() verfügbar. Das sind zwei Fliegen mit einer Klappe.

Um die Handshake-Anforderung zu erfassen, implementieren Sie eine ServerEndpointConfig.Configurator und überschreiben Sie die modifyHandshake() -Methode. Der HandshakeRequeststeht hier als Methodenargument zur Verfügung. Sie können den HttpSessionin EndpointConfig#getUserProperties() eingeben. Der EndpointConfigist wiederum als Methodenargument @OnOpen verfügbar.

Hier ist ein Kickoff-Beispiel für die ServerEndpointConfig.Configurator-Implementierung:

public class ServletAwareConfig extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        config.getUserProperties().put("httpSession", httpSession);
    }

}

So können Sie es verwenden, beachten Sie das configuratorNAME_ Attribut des @ServerEndpoint:

@ServerEndpoint(value="/your_socket", configurator=ServletAwareConfig.class)
public class YourSocket {

    private EndpointConfig config;

    @OnOpen
    public void onOpen(Session websocketSession, EndpointConfig config) {
        this.config = config;
    }

    @OnMessage
    public void onMessage(String message) {
        HttpSession httpSession = (HttpSession) config.getUserProperties().get("httpSession");
        ServletContext servletContext = httpSession.getServletContext();
        // ...
    }

}

Als Konstruktionshinweis ist es am besten, Ihren @ServerEndpoint vollständig frei von Servlet-API-Abhängigkeiten zu halten. Sie würden in der modifyHandshake()-Implementierung besser genau die dass -Informationen (normalerweise ein veränderlicher Javabean) aus der Servlet-Sitzung oder dem Kontext extrahieren und diese stattdessen in die Benutzereigenschaftszuordnung einfügen. Wenn Sie dies nicht tun, sollten Sie bedenken, dass eine Websocket-Sitzung länger als die HTTP-Sitzung leben kann. Wenn Sie also HttpSessionnoch in den Endpunkt tragen, können Sie IllegalStateExceptionausführen, wenn Sie versuchen, auf den Endpunkt zuzugreifen, während er abgelaufen ist.

Falls Sie CDI (und möglicherweise JSF) zur Hand haben, können Sie sich vom Quellcode von OmniFaces <o:socket> inspirieren lassen (Links befinden sich ganz unten im Schaufenster).

Siehe auch:

37
BalusC

Aktualisierter Code für die Antwort von BalusC, die onOpen-Methode muss mit @OnOpen versehen werden. Dann muss die Endpoint-Klasse nicht mehr erweitert werden:

@ServerEndpoint(value="/your_socket", configurator=ServletAwareConfig.class)
public class YourSocket {

    private EndpointConfig config;

    @OnOpen
    public void onOpen(Session websocketSession, EndpointConfig config) {
        this.config = config;
    }

    @OnMessage
    public void onMessage(String message) {
        HttpSession httpSession = (HttpSession) config.getUserProperties().get("httpSession");
        ServletContext servletContext = httpSession.getServletContext();
        // ...
    }

}
5
hfmanson

Manchmal können wir session nicht mit der obigen ServletAwareConfig von BalusC erhalten, da die Sitzung immer noch nicht erstellt wird. Da wir nicht nach Session suchen, sondern nach ServletContext, können wir in Tomcat Folgendes tun:

public static class ServletAwareConfig extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
        try {
            Field reqfld = request.getClass().getDeclaredField("request");
            reqfld.setAccessible(true);
            HttpServletRequest req = (HttpServletRequest) reqfld.get(request);
            ServletContext ctxt = req.getServletContext();
            Map<String, Object> up = config.getUserProperties();
            up.put("servletContext", ctxt);
        } catch (NoSuchFieldException e) {
        } catch (SecurityException e) {
        } catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        }
    }

}

wenn wir die init-Sitzung sofort wünschen, können wir request.getSession () aufrufen.

Ref: Websocket - httpSession gibt null zurück

0
Inshua

Ich habe die Antwort von BalusC auf Tomcat ausprobiert (Versionen 7.0.56 und 8.0.14). In beiden Containern enthält der request-Parameter von modifyHandshake keine HttpSession (und somit auch keinen ServletContext). Da ich den Servlet-Kontext nur für den Zugriff auf "globale" Variablen (das heißt die globale Webanwendung) benötige, brauche ich nur diese Variablen in einem gewöhnlichen statischen Feld einer Inhaberklasse gespeichert. Das ist nicht wichtig, aber es hat funktioniert.

Das sieht aus wie ein Fehler in dieser spezifischen Tomcat-Version - hat irgendjemand auch das gesehen?

0