考虑尝试一个简单的Java EE 7原型应用程序,该应用程序涉及JAX-RS(REST),WebSockets和CDI。
注意 :不想让它成为破坏者-但这篇文章主要讨论了我在尝试使用Web套接字和使用CDI作为“胶水”的REST(在Java EE应用程序中)时遇到的问题。 整合并未实现,但是仍然吸取了一些教训:-)
这个想法是使用REST端点作为Web套接字端点的“提要”,从而将数据“推”到所有连接的客户端:
- JAX-RS端点,它从其他来源接收数据(可能是实时的)作为Web套接字端点的输入
- 使用CDI事件作为黑白JAX-RS和WebSocket端点的粘合剂,并“触发”有效负载
@Path("/feed") public class RESTFeed {@InjectEvent<String> event;@POST@Consumes(MediaType.TEXT_PLAIN)public void push(String msg) {event.fire(msg);} }
- 在WebSocket端点实现中使用CDI Observer方法将数据推送到连接的客户端:
public void onMsg(@Observes String msg) {//different WS enpoint instance - notice the hash code value in the server logSystem.out.println("WS End point class ID -- " + this.hashCode());try {client.getBasicRemote().sendText(msg);} catch (IOException ex) {Logger.getLogger(ServerEndpoint.class.getName()).log(Level.SEVERE, null, ex);} }
当然,目前还没有考虑到更详细的信息,例如性能,异步通信等。 更多实验
但这有可能吗?
这是我执行的步骤
- 部署代码
- 浏览到http:// localhost:8080 / Explore-WebSocket-CDI-Integration-Maven /并作为Web套接字客户端连接
- 使用Postman在REST端点上触发HTTP POST请求
繁荣! Observer方法中的NullPointerException –我等待了几秒钟,然后现实击中了我!
根本原因(据我了解)
- WebSocket端点的行为
WebSocket端点与JAX-RS资源类相似,因为每个连接的客户端都有一个Web套接字端点类的实例(至少默认情况下)。 WebSocket规范中明确提到了这一点。 客户端(对等方)连接后,便会创建一个唯一的实例,并且可以安全地将Web套接字会话对象(对等方的表示形式)作为实例变量进行缓存。 IMO,这是一个简单干净的编程模型
- 但是CDI容器还有其他计划!
REST端点一旦触发CDI事件(响应POST请求),CDI容器就会创建WebSocket终结点(在本例中为CDI Observer)的其他实例。 为什么? 因为CDI bean本质上是上下文相关的 。 该应用程序不控制CDI bean的实例。 它只是使用它们(通过@Inject)。 由容器来创建和销毁bean实例,并确保在相同上下文中执行的bean可以使用适当的实例。 容器如何确定上下文呢? 通过范围 –应用程序,会话,请求等…..
(再次,在CDI规范中明确提到)
因此,问题的要点是没有WebSocket终结点当前上下文的实例–因此,CDI将创建一个新实例以传递消息。 当然,这意味着实例变量将指向null,因此将指向NPE(Duh!)
。
WebSocket端点将使用哪个CDI范围? 我尝试了@ ApplicationScoped,@ SessionScoped和@RequestScoped却没有太多运气–仍然是一个新实例和一个NPE
还有其他选择吗?
- 将会话集定义为静态变量将达到目的:
private static Set<Session> peers = Collections.synchronizedSet(new HashSet());
但是,如果仅在观察者方法中需要处理客户端特定状态(只能作为实例变量处理)的情况下,IMO只是一种黑客手段,这是不可行的–它势必会保持未初始化的状态。
- 服务器发送事件 ? 但是最终,SSE!= WebSocket。 如果用例要求“仅”服务器端推送,则可以选择使用。 SSE尚未成为Java EE标准-Java EE 8可能使之成为可能
解决方法
我不是专家,但是我想这取决于WebSocket规范,以便更清楚地说明如何将其与CDI结合使用。 鉴于CDI是Java EE规范中必不可少的一部分,因此将其与其他规范(特别是以HTML5为中心的规范,例如JAX-RS,WebSocket等)进行无缝集成非常重要。
Bruno Borges的这篇文章链接到与JMS,CDI和WebSocket以及它们如何相互集成有关的类似问题。
我错过了明显的事情吗? 您有什么建议/解决方案吗? 请随时鸣叫! :-)
示例代码在GitHub上可用 (以供您查看)。 我在GlassFish 4.1和Wildfly 8.2.0上尝试过
我想现在就这些了。 :-)
干杯!
翻译自: https://www.javacodegeeks.com/2015/02/integrating-cdi-websockets.html