我已经有十多年没有使用 Java 了,并且知道我正在尝试使用嵌入式 tomcat 8.5.95(由于 guacamole-common 而一直使用该版本)以及 websocket 支持。
尝试了各种方法让它工作,但我被困住了,我不知道是什么导致了问题。
问题是
(ServerContainer)ctx.getAttribute("javax.websocket.server.ServerContainer")
始终是 null
。main
中或在 ServletContainerInitializer
中调用上面的代码并不重要
主要
public static void main(String[] args) throws Exception {
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
// tried tomcat.addWebapp and tomcat.addContext
var ctx = tomcat.addWebapp("", new File(".").getAbsolutePath());
ctx.addServletContainerInitializer(new Init(), null);
// normal http implementation for guacamole
Tomcat.addServlet(ctx, "tunnel", new DummyGuacamoleTunnelServlet());
ctx.addServletMappingDecoded("/tunnel", "tunnel");
// tried this first, but according the internet ;) this should go in a
// String serverContainerClass = ServerContainer.class.getName();
// ServerEndpointConfig config =
// ServerEndpointConfig.Builder.create(DummyGuacamoleWebsocketEndpoint.class, "/websocket-tunnel")
// .subprotocols(Arrays.asList(new String[]{"guacamole"}))
// .build();
// String serverContainerClass = ServerContainer.class.getName();
// ServerEndpointConfig config =
// ServerEndpointConfig.Builder.create(DummyGuacamoleWebsocketEndpoint.class, "/websocket-tunnel")
// .subprotocols(Arrays.asList(new String[]{"guacamole"}))
// .build();
//
// ServerContainer container = (ServerContainer)ctx.getServletContext().getAttribute(serverContainerClass);
// if (container == null) {
// return;
// }
//
// try {
//
// // Add configuration to container
// container.addEndpoint(config);
//
// }
// catch (DeploymentException e) {
// System.out.println("Unable to deploy WebSocket tunnel. " + e);
// }
tomcat.start();
tomcat.getServer().await();
}
初始化(ServletContainerInitializer)
public class Init implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
String serverContainerClass = ServerContainer.class.getName();
ServerContainer container = (ServerContainer)ctx.getAttribute(serverContainerClass);
if (container == null) {
return;
}
ServerEndpointConfig config =
ServerEndpointConfig.Builder.create(DummyGuacamoleWebsocketEndpoint.class, "/websocket-tunnel")
.subprotocols(Arrays.asList(new String[]{"guacamole"}))
.build();
try {
// Add configuration to container
container.addEndpoint(config);
}
catch (DeploymentException e) {
System.out.println("Unable to deploy WebSocket tunnel. " + e);
}
}
}
端点
// GuacamoleWebSocketTunnelEndpoint is from guacamole-common, which I can't change
public class DummyGuacamoleWebsocketEndpoint extends GuacamoleWebSocketTunnelEndpoint {
@Override
protected GuacamoleTunnel createTunnel(javax.websocket.Session session, javax.websocket.EndpointConfig config) throws GuacamoleException {
return null;
}
}
gradle 依赖项
dependencies {
implementation("org.apache.guacamole:guacamole-common:1.5.3")
implementation("org.apache.tomcat.embed:tomcat-embed-core:8.5.95")
implementation("org.apache.tomcat.embed:tomcat-embed-websocket:8.5.95")
compileOnly("javax.websocket:javax.websocket-api:1.1")
compileOnly("javax.websocket:javax.websocket-all:1.1")
}
编辑 我还尝试实现一个
ServletContextListener
,其中我尝试获取 ServerContainer
,但无济于事。
如何获得正确的
ServerContainer
?
好的,经过进一步的搜索和调查,我找到了解决方案:)
我不需要这两个
javax.websocket...
依赖项
添加端点基本上有两种可能性
带注释
创建这样的上下文(在
main
中)
var ctx = tomcat.addWebapp("", new File(".").getAbsolutePath());
这还将添加对 jsp 的支持(但需要额外的依赖项,例如 jasper)
添加
org.apache.tomcat.embed:tomcat-embed-websocket
提供的ServletContainerInitializer(在main
中)
ctx.addServletContainerInitializer(new WsSci(), null);
用
@ServerEndpoint("/path")
注释端点
或者您可以显式地将端点添加到初始值设定项
ctx.addServletContainerInitializer(new WsSci(), new HashSet<>(List.of(GuacamoleWebSocketTunnelEndpoint.class)));
那么你就不需要注释了
无注释
像这样创建上下文(在 main 中)
var ctx = tomcat.addContext("", new File(".").getAbsolutePath());
Wrapper defaultServlet = ctx.createWrapper();
defaultServlet.setName("default");
defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
defaultServlet.setLoadOnStartup(1);
ctx.addChild(defaultServlet);
ctx.addServletMappingDecoded("/", "default");
您必须按照此处
所述添加默认 servlet
您将无法获得 JSP 支持。
添加
org.apache.tomcat.embed:tomcat-embed-websocket
提供的ServletContainerInitializer(在main
中)
ctx.addServletContainerInitializer(new WsSci(), null);
添加监听者
public class Listener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
String serverContainerClass = ServerContainer.class.getName();
ServerContainer container = (ServerContainer)event.getServletContext().getAttribute(serverContainerClass);
ServerEndpointConfig config =
ServerEndpointConfig.Builder.create(DummyGuacamoleWebsocketEndpoint.class, "/websocket-tunnel")
.build();
try {
container.addEndpoint(config);
} catch (DeploymentException e) {
throw new RuntimeException(e);
}
}
@Override
public void contextDestroyed(ServletContextEvent event) {
// Do stuff during server shutdown.
}
}
在
main
中注册监听器
ctx.addApplicationListener("full.name.to.ListenerClass");