Jetty 中的 JSR-356 javax websockets(嵌入式和非嵌入式)

Posted

技术标签:

【中文标题】Jetty 中的 JSR-356 javax websockets(嵌入式和非嵌入式)【英文标题】:JSR-356 javax websockets in Jetty (embedded and not) 【发布时间】:2018-11-28 06:09:05 【问题描述】:

鉴于:

我想将 webapp(打包为 WAR,其中包含 web.xml)部署到 Jetty 服务器。

在该应用程序中,我希望能够配置 JSR-356 指定的 javax websocket 端点。我更喜欢那些通过ServerEndpointConfig 提供的端点,不是注释扫描。

有许多资源可以通过嵌入式 Jetty 来举例说明,这些资源利用了众所周知的 WebSocketServerContainerInitializer.configureContext(context); API。很明显,我做不到。

还有其他的,直接跳转到ServletContextListener,通过著名的context.getAttribute("javax.websocket.server.ServerContainer"获得ServerContainer)。到目前为止,我通过这个 API 得到了很多 NULL,所以显然没有添加容器。

问题:

缺少的配置是什么?可以通过web.xml 完成吗?如果它是关于像 jetty.xmljetty.ini 这样的配置文件 - 再一次的例子会很好,最好是 xml 语法。

更新:

根据下面的答案(已接受的答案)以及我实际上试图在这里描述的那样 - 已知的配置方式绝对工作得很好。说已知我的意思是通过将--module=websocket 添加到某个非嵌入式码头的*.ini 文件,或者通过调用WebSocketServerContainerInitializer.configureContext 来获取嵌入式码头。

所以换个说法:有人有任何经验/知识可以通过纯粹基于XML 的配置来启用 websocket 模块吗?

【问题讨论】:

【参考方案1】:

如果使用$jetty.base$jetty.home recommended installation process for Standalone Jetty,您应该转到您的$jetty.base 实例目录并启用websocket 模块。

$ cd /path/to/mybase
$ java -jar /opt/jetty/jetty-home-9.4.14.v20181114/start.jar --add-to-start=websocket
$ grep "websocket" start.ini
--module=websocket

现在您已经为 $jetty.base 实例启用了 websocket。

如果您希望 Jetty 通过字节码扫描您部署的 web 应用以查找注释来发现您的服务器 WebSocket 端点,那么您还需要 annotations 模块。

$ cd /path/to/mybase
$ java -jar /opt/jetty/jetty-home-9.4.14.v20181114/start.jar --add-to-start=annotations
$ grep "annotations" start.ini
--module=annotations

完成后,您可以执行以下一项(或多项)操作以将 websocket 服务器端点与您的 web 应用程序一起部署。

只需使用@ServerEndpoint(来自javax.websocket.server.ServerEndpoint)注释您的类 或者,如果您想以编程方式添加 WebSocket 服务器端点,您有 2 个选项。
    在您的项目中提供javax.websocket.server.ServerApplicationConfig 的实现,并返回您希望 Jetty 部署的服务器端点。 在 WebApp 启动/初始化期间,从 ServletContext.getAttribute("javax.websocket.server.ServerContainer") 获取 javax.websocket.server.ServerContainer 并使用它的 addEndpoint() 方法。请注意,这仅适用于 ServletContextListener.contextInitialized(ServletContextEvent sce)ServletContainerInitializer.onStartup(Set<Class<?>> c, ServletContext ctx)

为什么这在独立的 Jetty 中有效?独立的 Jetty 做了什么来使这成为可能?

会发生以下情况:

websocket module 将lib/websocket/*.jar 添加到服务器类路径 websocket 模块依赖于 clientannotations 模块 client module 将lib/jetty-client-<jetty.version>.jar 添加到服务器类路径 annotations module 将lib/jetty-annotations-<jetty.version>.jarlib/annotations/*.jar 添加到服务器类路径中 annotations 模块依赖于plus 模块 annotations 模块选择 etc/jetty-annotations.xml 在启动时执行 annotations 模块按名称添加 JPMS 模块org.objectweb.asm plus module 将lib/jetty-plus-<jetty.version>.jar 添加到服务器类路径 plus 模块选择 etc/jetty-plus.xml 在启动时执行 plus 模块依赖于 serversecurityjndiwebapptransactions 模块

(我将跳过以这种方式选择的其余模块)

简而言之,只需添加 websocket 模块,您就可以获得以下服务器类路径条目

lib/websocket/*.jar
lib/jetty-client-<jetty.version>.jar
lib/jetty-annotations-<jetty.version>.jar
lib/annotations/*.jar
lib/jetty-plus-<jetty.version>.jar

以及以下 XML 文件

lib/jetty-annotations.xml
lib/jetty-plus.xml

这两个 XML 文件都只是修改了服务器端的默认 Configuration 列表,使它们引入的 Configuration 行为可用于所有已部署的 WebApp。

您也可以set the Configuration on the WebAppContext(在它开始之前)针对 web 应用的特定行为。

例子:

WebAppContext context = new WebAppContext();
context.setContextPath("/");
context.setBaseResource(Resource.newResource(rootResourceUrl));

context.setConfigurations(new Configuration[] 
            new AnnotationConfiguration(),
            new WebXmlConfiguration(),
            new WebInfConfiguration(),
            new PlusConfiguration(), 
            new MetaInfConfiguration(),
            new FragmentConfiguration(), 
            new EnvConfiguration());    

handlerList.addHandler(context);

注意:对于javax.websocket,您必须使用WebAppContext,因为为其初始化定义的行为需要完整的Web 应用程序才能运行。 虽然您可以将 ServletContextHandlerjavax.websocket 端点一起使用,但这种样式是 100% 手动定义、初始化和声明的,没有 JSR-356 依赖的自动字节码/注释扫描功能。

您也可以从命令行查看所有这些内容。

显示活动的$jetty.base 配置,XML 属性值是什么,服务器类路径是什么,以及将执行什么 XML(以及以什么顺序!!)

$ cd /path/to/mybase
$ java -jar /opt/jetty/jetty-home-9.4.14.v20181114/start.jar --list-config

显示模块列表以及它们之间的关系(以及在您的$jetty.base 配置中选择的模块)

$ cd /path/to/mybase
$ java -jar /opt/jetty/jetty-home-9.4.14.v20181114/start.jar --list-modules

【讨论】:

正如我在问题中所述,您在此处概述的路径对我来说是已知的并且正在工作--module=websocketWebSocketServerContainerInitializer.configureContext(context) 的一些味道。太糟糕了我的情况更困难:在历史上,我部署了 embedded Jetty 从 Java 配置为读取 XML 文件并由它们配置。对此有什么建议吗? “模块”是 Jetty 9.x 中一个纯粹独立的 Jetty 概念。 Jetty 10.x 中引入了嵌入式码头的“模块”,但作为正式的 JPMS 模块。 感谢您对看似不可能的事情的认可(Jetty 9.4..)。好吧,看起来我要放弃 JAVAX 方式并使用 WebSockets 'Jetty' 方式(servlet 等)。 如果您愿意,您仍然可以执行inferior javax way,您只需在为您的 JSR-356 (javax.websocket) 行为启动 WebAppContext 之前声明服务器 Configuration。注意:您必须使用WebAppContext,而不是ServletContextHandler

以上是关于Jetty 中的 JSR-356 javax websockets(嵌入式和非嵌入式)的主要内容,如果未能解决你的问题,请参考以下文章

带有 Spring Boot 的 JAVAX JSR 356 Websocket

码头 Websocket JSR 356 错误 403

尝试使用 jsr 356 时出现 java.lang.linkage 错误

jetty各个版本对应的jdk版本

将 Java WebSockets (JSR-356) 与 SpringBoot 集成

JSR 356 WebSocket 最大消息大小配置失败