web-dev-qa-db-fra.com

Accès à ServletContext et à HttpSession dans @OnMessage d'un JSR-356 @ServerEndpoint

Je dois obtenir la ServletContext à l'intérieur d'un @ServerEndpoint afin de trouver la variable Printemps ApplicationContext et la recherche d'un haricot.

Pour le moment, ma meilleure approche consiste à lier ce bean dans le contexte de dénomination JNDI et à le rechercher dans Endpoint. Toute meilleure solution est la bienvenue.

Je cherche également un moyen raisonnable de synchroniser la variable HttpSession du servlet avec la variable Session de websocket.

25
Sombriks

La servlet HttpSession est disponible dans JSR-356 sous la forme HandshakeRequest#getHttpSession() qui est également disponible lorsqu'une demande de prise de contact est faite juste avant @OnOpen d'un @ServerEndpoint . La ServletContext est quant à elle uniquement disponible via HttpSession#getServletContext() . C'est deux oiseaux avec une pierre.

Pour capturer la demande d'établissement de liaison, implémentez un ServerEndpointConfig.Configurator et substituez la méthode modifyHandshake() . La HandshakeRequest est ici disponible en tant qu'argument de méthode. Vous pouvez mettre la HttpSession dans EndpointConfig#getUserProperties() . La EndpointConfig est à son tour disponible comme argument de méthode @OnOpen .

Voici un exemple de démarrage de l'implémentation ServerEndpointConfig.Configurator:

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);
    }

}

Voici comment vous pouvez l'utiliser, notez l'attribut configurator du @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();
        // ...
    }

}

Comme conseil de conception, il est préférable de garder votre @ServerEndpoint entièrement dépourvu de dépendances de l'API de servlet. Dans l'implémentation modifyHandshake(), vous auriez mieux besoin d'extraire immédiatement exactement que informations (généralement un Javabean mutable) dont vous avez besoin de la session ou du contexte de servlet et de les placer dans la mappe des propriétés de l'utilisateur. Si vous ne le faites pas, gardez à l'esprit qu'une session websocket peut vivre plus longtemps qu'une session HTTP. Ainsi, lorsque vous transportez toujours HttpSession dans le système d'extrémité, vous pouvez alors rencontrer IllegalStateException lorsque vous essayez d'y accéder pendant son expiration.

Si vous avez entre les mains CDI (et peut-être JSF), vous pouvez vous inspirer du code source de OmniFaces <o:socket> (les liens se trouvent tout en bas de la vitrine).

Voir également:

37
BalusC

Code mis à jour pour la réponse de BalusC, la méthode onOpen doit être décorée avec @OnOpen. Ensuite, il n'est plus nécessaire d'étendre la classe Endpoint:

@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

Parfois nous ne pouvons pas obtenir session avec ci-dessus ServletAwareConfig de BalusC, c'est parce que la session n'est toujours pas créée. comme nous ne cherchons pas session mais servletContext, nous pouvons procéder comme suit dans Tomcat:

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) {
        }
    }

}

si nous voulons une session init immédiatement, nous pouvons appeler request.getSession ().

Ref: Websocket - httpSession renvoie null

0
Inshua

J'ai essayé la réponse de BalusC sur Tomcat (versions 7.0.56 et 8.0.14). Sur les deux conteneurs, le paramètre request de modifyHandshake ne contient pas de HttpSession (et donc aucun servletContext). Comme j'avais besoin du contexte de servlet uniquement pour accéder aux variables "globales" (c'est-à-dire l'application web globale), stocké ces variables dans un champ statique ordinaire d'une classe de détenteurs. C'est inélégant, mais cela a fonctionné.

Cela ressemble à un bogue dans cette version spécifique de Tomcat. Quelqu'un at-il déjà vu cela?

0
Wolfgang Liebich