如何获取现有的websocket实例

我正在开发一个使用Websockets(Java EE 7)的应用程序,以异步方式向所有连接的客户端发送消息。 每当创建新文章(我的应用程序中的参与模式)时,服务器(Websocket端点)都应发送这些消息。

每次建立与websocket端点的连接时,我都会将相应的会话添加到列表中,我可以在外部访问该列表。

但我遇到的问题是,当我访问这个创建的websocket端点时,所有客户端都从外部连接(任何其他业务类),我得到了现有的实例(如单例)。

所以,你能否告诉我一个方法,我可以得到一个websocket端点的现有实例,因为我无法创建它作为新的MyWebsocketEndPoint()因为它将由websocket内部机制创建,只要客户端的请求是接收。

对于参考:

private static WebSocketEndPoint INSTANCE = null; public static WebSocketEndPoint getInstance() { if(INSTANCE == null) { // Instead of creating a new instance, I need an existing one INSTANCE = new WebSocketEndPoint (); } return INSTANCE; } 

提前致谢。

容器为每个客户端连接创建一个单独的端点实例,因此您无法执行您尝试执行的操作。 但我认为你要做的是在事件发生时向所有活动的客户端连接发送消息,这是相当简单的。

javax.websocket.Session类具有getBasicRemote方法,用于检索表示与该会话关联的端点的RemoteEndpoint.Basic实例。

您可以通过调用Session.getOpenSessions()来检索所有打开的会话,然后遍历它们。 循环将为每个客户端连接发送一条消息。 这是一个简单的例子:

 @ServerEndpoint("/myendpoint") public class MyEndpoint { @OnMessage public void onMessage(Session session, String message) { try { for (Session s : session.getOpenSessions()) { if (s.isOpen()) { s.getBasicRemote().sendText(message); } } catch (IOException ex) { ... } } } 

但在您的情况下,您可能希望使用CDI事件来触发对所有客户端的更新。 在这种情况下,您将创建一个CDI事件,Websocket端点类中的方法会观察到该事件:

 @ServerEndpoint("/myendpoint") public class MyEndpoint { // EJB that fires an event when a new article appears @EJB ArticleBean articleBean; // a collection containing all the sessions private static final Set sessions = Collections.synchronizedSet(new HashSet()); @OnOpen public void onOpen(final Session session) { // add the new session to the set sessions.add(session); ... } @OnClose public void onClose(final Session session) { // remove the session from the set sessions.remove(session); } public void broadcastArticle(@Observes @NewArticleEvent ArticleEvent articleEvent) { synchronized(sessions) { for (Session s : sessions) { if (s.isOpen()) { try { // send the article summary to all the connected clients s.getBasicRemote().sendText("New article up:" + articleEvent.getArticle().getSummary()); } catch (IOException ex) { ... } } } } } } 

上例中的EJB会执行以下操作:

 ... @Inject Event newArticleEvent; public void publishArticle(Article article) { ... newArticleEvent.fire(new ArticleEvent(article)); ... } 

请参阅有关WebSockets和CDI Events的Java EE 7 Tutorial章节。

编辑:修改@Observer方法以将事件用作参数。

编辑2:在@gcvt中,在synchronizedArticle中包装循环。

编辑3:更新了Java EE 7教程的链接。 干得好,甲骨文。 啧。

实际上,WebSocket API提供了一种控制端点实例化的方法。 请参阅https://tyrus.java.net/apidocs/1.2.1/javax/websocket/server/ServerEndpointConfig.Configurator.html

简单样本(取自Tyrus – WebSocket RI测试):

  public static class MyServerConfigurator extends ServerEndpointConfig.Configurator { public static final MyEndpointAnnotated testEndpoint1 = new MyEndpointAnnotated(); public static final MyEndpointProgrammatic testEndpoint2 = new MyEndpointProgrammatic(); @Override public  T getEndpointInstance(Class endpointClass) throws InstantiationException { if (endpointClass.equals(MyEndpointAnnotated.class)) { return (T) testEndpoint1; } else if (endpointClass.equals(MyEndpointProgrammatic.class)) { return (T) testEndpoint2; } throw new InstantiationException(); } } 

您需要将其注册到端点:

 @ServerEndpoint(value = "/echoAnnotated", configurator = MyServerConfigurator.class) public static class MyEndpointAnnotated { @OnMessage public String onMessage(String message) { assertEquals(MyServerConfigurator.testEndpoint1, this); return message; } } 

或者您也可以将它与程序化端点一起使用:

 public static class MyApplication implements ServerApplicationConfig { @Override public Set getEndpointConfigs(Set> endpointClasses) { return new HashSet (Arrays.asList(ServerEndpointConfig.Builder .create(MyEndpointProgrammatic.class, "/echoProgrammatic") .configurator(new MyServerConfigurator()) .build())); } @Override public Set> getAnnotatedEndpointClasses(Set> scanned) { return new HashSet>(Arrays.asList(MyEndpointAnnotated.class)); } 

当然,如果您有一个用于所有端点的配置程序(如所示的片段中的丑陋ifs)或者您将为每个端点创建单独的配置程序,则由您决定。

请不要复制现有的代码 – 这只是Tyrus测试的一部分,它确实违反了一些基本的OOM范例。

请参阅https://github.com/tyrus-project/tyrus/blob/1.2.1/tests/e2e/src/test/java/org/glassfish/tyrus/test/e2e/GetEndpointInstanceTest.java以进行完整测试。