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.
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).
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();
// ...
}
}
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 ().
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?