3 回答

TA贡献1884条经验 获得超4个赞
Configurator可以同时由多个线程调用,很可能您无法在来自modifyHandshake()和的调用之间访问正确的HttpSession对象getEndpointInstance()。
或者说另一种方式......
请求A.
修改握手A.
请求B.
修改握手B.
获取端点实例A < - 这将具有请求B的HttpSession
获取端点实例B.
这是对Martin的代码的修改,它使用ServerEndpointConfig.getUserProperties()map HttpSession在@OnOpen方法调用期间使套接字实例可用
GetHttpSessionConfigurator.java
package examples;
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator
{
@Override
public void modifyHandshake(ServerEndpointConfig config,
HandshakeRequest request,
HandshakeResponse response)
{
HttpSession httpSession = (HttpSession)request.getHttpSession();
config.getUserProperties().put(HttpSession.class.getName(),httpSession);
}
}
GetHttpSessionSocket.java
package examples;
import java.io.IOException;
import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint(value = "/example",
configurator = GetHttpSessionConfigurator.class)
public class GetHttpSessionSocket
{
private Session wsSession;
private HttpSession httpSession;
@OnOpen
public void open(Session session, EndpointConfig config) {
this.wsSession = session;
this.httpSession = (HttpSession) config.getUserProperties()
.get(HttpSession.class.getName());
}
@OnMessage
public void echo(String msg) throws IOException {
wsSession.getBasicRemote().sendText(msg);
}
}
额外功能:不需要instanceof或需要铸造。
一些EndpointConfig知识
EndpointConfig 每个“端点实例”都存在对象。
但是,“端点实例”与规范有2个含义。
JSR的默认行为,其中每个传入的升级请求都会生成端点类的新对象实例
javax.websocket.Session将对象端点实例及其配置连接到特定逻辑连接的A。
可以将单个端点实例用于多个javax.websocket.Session实例(这是ServerEndpointConfig.Configurator支持的功能之一)
ServerContainer实现将跟踪一组ServerEndpointConfig,它们代表服务器可以响应websocket升级请求的所有已部署端点。
这些ServerEndpointConfig对象实例可以来自几个不同的来源。
手动提供的 javax.websocket.server.ServerContainer.addEndpoint(ServerEndpointConfig)
通常在javax.servlet.ServletContextInitializer.contextInitialized(ServletContextEvent sce)通话中完成
来自javax.websocket.server.ServerApplicationConfig.getEndpointConfigs(Set)电话。
通过扫描Web应用程序为@ServerEndpoint注释类自动创建。
这些ServerEndpointConfig对象实例作为javax.websocket.Session最终创建时的默认值存在。
ServerEndpointConfig.Configurator实例
在收到或处理任何升级请求之前,所有ServerEndpointConfig.Configurator对象现在都已存在并准备好执行其主要和唯一目的,以允许自定义websocket连接的升级过程到最终javax.websocket.Session
访问特定于会话的EndpointConfig
请注意,您无法ServerEndpointConfig从端点实例中访问对象实例。您只能访问EndpointConfig实例。
这意味着如果您ServerContainer.addEndpoint(new MyCustomServerEndpointConfig())在部署期间提供并稍后尝试通过注释访问它,则它将无法工作。
以下所有内容均无效。
@OnOpen
public void onOpen(Session session, EndpointConfig config)
{
MyCustomServerEndpointConfig myconfig = (MyCustomServerEndpointConfig) config;
/* this would fail as the config is cannot be cast around like that */
}
// --- or ---
@OnOpen
public void onOpen(Session session, ServerEndpointConfig config)
{
/* For @OnOpen, the websocket implementation would assume
that the ServerEndpointConfig to be a declared PathParam
*/
}
// --- or ---
@OnOpen
public void onOpen(Session session, MyCustomServerEndpointConfig config)
{
/* Again, for @OnOpen, the websocket implementation would assume
that the MyCustomServerEndpointConfig to be a declared PathParam
*/
}
您可以在Endpoint对象实例的生命周期内访问EndpointConfig,但是在有限的时间内。的javax.websocket.Endpoint.onOpen(Session,Endpoint),带有注释的@OnOpen方法,或通过使用CDI的。EndpointConfig不以任何其他方式或任何其他时间提供。
但是,您始终可以通过Session.getUserProperties()呼叫访问UserProperties,该呼叫始终可用。此用户属性地图总是可用的,通过注释的技术(如在会话期间的参数是它@OnOpen,@OnClose,@OnError,或@OnMessage呼叫),通过CDI注射会话的,或者即使使用,从延长非注释的WebSockets的javax.websocket.Endpoint。
升级如何运作
如前所述,每个定义的端点都将ServerEndpointConfig与之关联。
这些ServerEndpointConfigs是单个实例,表示EndpointConfig最终可用于最终创建的端点实例的默认状态。
当传入的升级请求到达时,它已在JSR上执行以下操作。
路径是否与任何ServerEndpointConfig.getPath()条目匹配
如果不匹配,则返回404进行升级
将升级请求传递到ServerEndpointConfig.Configurator.checkOrigin()
如果无效,则返回错误以升级响应
创建HandshakeResponse
将升级请求传递到ServerEndpointConfig.Configurator.getNegotiatedSubprotocol()
在HandshakeResponse中存储答案
将升级请求传递到ServerEndpointConfig.Configurator.getNegotiatedExtensions()
在HandshakeResponse中存储答案
创建新的端点特定的ServerEndpointConfig对象。复制编码器,解码器和用户属性。这个新的ServerEndpointConfig包含路径,扩展,端点类,子协议,配置器的默认值。
将升级请求,响应和新的ServerEndpointConfig传递到ServerEndpointConfig.Configurator.modifyHandshake()
调用ServerEndpointConfig.getEndpointClass()
在ServerEndpointConfig.Configurator.getEndpointInstance(Class)上使用class
创建Session,关联端点实例和EndpointConfig对象。
通知连接的端点实例
需要EndpointConfig的带注释的方法获取与此Session关联的方法。
调用Session.getUserProperties()返回EndpointConfig.getUserProperties()
需要注意的是,ServerEndpointConfig.Configurator是每个映射的ServerContainer端点的单例。
这是有意的,并且是期望的,以允许实现者具有若干特征。
如果他们愿意,为多个对等体返回相同的Endpoint实例。所谓无状态的websocket写作方法。
为所有Endpoint实例提供昂贵资源的单点管理
如果实现为每次握手创建了新的Configurator,则该技术将无法实现。

TA贡献1831条经验 获得超9个赞
可能吗?
让我们回顾一下Java API for WebSocket规范,看看是否可以获取HttpSession对象。该规范说,第29页:
因为websocket连接是使用http请求启动的,所以在客户端运行的HttpSession和在该HttpSession中建立的任何websockets之间存在关联。API允许在打开握手中访问对应于同一客户端的唯一HttpSession。
所以是的,这是可能的。
但是,我认为你不可能获得HttpServletRequest对象的引用。您可以使用a 监听所有新的servlet请求ServletRequestListener,但您仍然需要确定哪个请求属于哪个服务器端点。如果您找到解决方案,请告诉我们!
抽象的方法
如何在说明书的第13页和第14页中对其进行了松散的描述,并在下一个标题下以代码示例。
在英语中,我们需要拦截握手过程以获取HttpSession对象。然后,为了将HttpSession引用传递给我们的服务器端点,我们还需要在容器创建服务器端点实例并手动注入引用时进行拦截。我们通过提供我们自己ServerEndpointConfig.Configurator的方法modifyHandshake()并覆盖方法来完成所有这些getEndpointInstance()。
自定义配置程序将按逻辑实例化一次ServerEndpoint(请参阅JavaDoc)。
代码示例
这是服务器端点类(我在此代码片段之后提供了CustomConfigurator类的实现):
@ServerEndpoint(value = "/myserverendpoint", configurator = CustomConfigurator.class)
public class MyServerEndpoint
{
private HttpSession httpSession;
public void setHttpSession(HttpSession httpSession) {
if (this.httpSession != null) {
throw new IllegalStateException("HttpSession has already been set!");
}
this.httpSession = httpSession;
}
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
System.out.println("My Session Id: " + httpSession.getId());
}
}
这是自定义配置器:
public class CustomConfigurator extends ServerEndpointConfig.Configurator
{
private HttpSession httpSession;
// modifyHandshake() is called before getEndpointInstance()!
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
httpSession = (HttpSession) request.getHttpSession();
super.modifyHandshake(sec, request, response);
}
@Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
T endpoint = super.getEndpointInstance(endpointClass);
if (endpoint instanceof MyServerEndpoint) {
// The injection point:
((MyServerEndpoint) endpoint).setHttpSession(httpSession);
}
else {
throw new InstantiationException(
MessageFormat.format("Expected instanceof \"{0}\". Got instanceof \"{1}\".",
MyServerEndpoint.class, endpoint.getClass()));
}
return endpoint;
}
}
添加回答
举报