Java 如何获取现有的 websocket 实例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18481597/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get an existing websocket instance
提问by Aryan Venkat
I'm working on an application that uses Websockets (Java EE 7) to send messages to all the connected clients asynchronously. The server (Websocket endpoint) should send these messages whenever a new article (an engagement modal in my app) is created.
我正在开发一个使用 Websockets (Java EE 7) 向所有连接的客户端异步发送消息的应用程序。每当创建新文章(我的应用程序中的参与模式)时,服务器(Websocket 端点)都应发送这些消息。
Everytime a connection is established to the websocket endpoint, I'm adding the corresponding session to a list, which I could be able to access outside.
每次与 websocket 端点建立连接时,我都会将相应的会话添加到一个列表中,我可以在外部访问它。
But the problem I had is, when I'm accessing this created websocket endpoint to which all the clients connected from outside (any other business class), I've get the existing instance (like a singleton).
但我遇到的问题是,当我访问这个创建的 websocket 端点时,所有客户端都从外部(任何其他业务类)连接到该端点,我得到了现有实例(如单例)。
So, can you please suggest me a way I can get an existing instance of the websocket endpoint, as I can't create it as new MyWebsocketEndPoint() coz it'll be created by the websocket internal mechanism whenever the request from a client is received.
所以,请你给我建议一种方法,我可以获得 websocket 端点的现有实例,因为我不能将它创建为 new MyWebsocketEndPoint() 因为它会在来自客户端的请求时由 websocket 内部机制创建已收到。
For a ref:
对于参考:
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;
}
Thanks in advance.
提前致谢。
采纳答案by Ian Evans
The container creates a separate instance of the endpoint for every client connection, so you can't do what you're trying to do. But I think what you're trying to do is send a message to all the active client connections when an event occurs, which is fairly straightforward.
容器为每个客户端连接创建一个单独的端点实例,因此您无法执行您尝试执行的操作。但是我认为您要做的是在事件发生时向所有活动的客户端连接发送一条消息,这相当简单。
The javax.websocket.Session
class has the getBasicRemote
method to retrieve a RemoteEndpoint.Basic
instance that represents the endpoint associated with that session.
本javax.websocket.Session
类有getBasicRemote
检索方法RemoteEndpoint.Basic
,表示与该会话相关的端点实例。
You can retrieve all the open sessions by calling Session.getOpenSessions()
, then iterate through them. The loop will send each client connection a message. Here's a simple example:
您可以通过调用 检索所有打开的会话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) { ... }
}
}
But in your case, you probably want to use CDI events to trigger the update to all the clients. In that case, you'd create a CDI event that a method in your Websocket endpoint class observes:
但在您的情况下,您可能希望使用 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<Session> sessions =
Collections.synchronizedSet(new HashSet<Session>());
@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) { ... }
}
}
}
}
}
The EJB in the above example would do something like:
上面示例中的 EJB 将执行以下操作:
...
@Inject
Event<ArticleEvent> newArticleEvent;
public void publishArticle(Article article) {
...
newArticleEvent.fire(new ArticleEvent(article));
...
}
See the Java EE 7 Tutorial chapters on WebSocketsand CDI Events.
请参阅有关WebSocket和CDI 事件的 Java EE 7 教程章节。
Edit: Modified the @Observer
method to use an event as a parameter.
编辑:修改了@Observer
使用事件作为参数的方法。
Edit 2: wrapped the loop in broadcastArticle in synchronized, per @gcvt.
编辑 2:根据 @gcvt,将循环包装在同步的 broadcastArticle 中。
Edit 3: Updated links to Java EE 7 Tutorial. Nice job, Oracle. Sheesh.
编辑 3:更新了 Java EE 7 教程的链接。干得好,甲骨文。嘘。
回答by Pavel Bucek
Actually, WebSocket API provides a way how you can control endpoint instantiation. See https://tyrus.java.net/apidocs/1.2.1/javax/websocket/server/ServerEndpointConfig.Configurator.html
实际上,WebSocket API 提供了一种控制端点实例化的方法。见https://tyrus.java.net/apidocs/1.2.1/javax/websocket/server/ServerEndpointConfig.Configurator.html
simple sample (taken from Tyrus - WebSocket RItest):
简单示例(取自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> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
if (endpointClass.equals(MyEndpointAnnotated.class)) {
return (T) testEndpoint1;
} else if (endpointClass.equals(MyEndpointProgrammatic.class)) {
return (T) testEndpoint2;
}
throw new InstantiationException();
}
}
You need to register this to an endpoint:
您需要将其注册到端点:
@ServerEndpoint(value = "/echoAnnotated", configurator = MyServerConfigurator.class)
public static class MyEndpointAnnotated {
@OnMessage
public String onMessage(String message) {
assertEquals(MyServerConfigurator.testEndpoint1, this);
return message;
}
}
or you can use it with programmatic endpoints as well:
或者您也可以将它与编程端点一起使用:
public static class MyApplication implements ServerApplicationConfig {
@Override
public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses) {
return new HashSet<ServerEndpointConfig>
(Arrays.asList(ServerEndpointConfig.Builder
.create(MyEndpointProgrammatic.class, "/echoProgrammatic")
.configurator(new MyServerConfigurator())
.build()));
}
@Override
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
return new HashSet<Class<?>>(Arrays.asList(MyEndpointAnnotated.class));
}
Of course it is up to you if you will have one configurator used for all endpoints (ugly ifs as in presented snippet) or if you'll create separate configurator for each endpoint.
当然,如果您将有一个用于所有端点的配置器(如所呈现的片段中的丑陋 ifs),或者您是否将为每个端点创建单独的配置器,这取决于您。
Please do not copy presented code as it is - this is only part of Tyrus tests and it does violate some of the basic OOM paradigms.
请不要照原样复制呈现的代码——这只是 Tyrus 测试的一部分,它确实违反了一些基本的 OOM 范式。
See https://github.com/tyrus-project/tyrus/blob/1.2.1/tests/e2e/src/test/java/org/glassfish/tyrus/test/e2e/GetEndpointInstanceTest.javafor complete test.