Skip to content

Commit d213f6a

Browse files
noeargitee-org
authored andcommitted
!10 尝试为tomcat提供websocket适配支持
Merge pull request !10 from 小xu中年/dev
2 parents a5ce66e + 6fef62f commit d213f6a

File tree

19 files changed

+5887
-17
lines changed

19 files changed

+5887
-17
lines changed

solon-jakarta-projects/solon-server/solon-server-tomcat-jakarta/pom.xml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@
5252
</exclusion>
5353
</exclusions>
5454
</dependency>
55+
56+
<dependency>
57+
<groupId>org.apache.tomcat.embed</groupId>
58+
<artifactId>tomcat-embed-websocket</artifactId>
59+
<version>${tomcat.jakarta.version}</version>
60+
<scope>provided</scope>
61+
</dependency>
5562

5663
<dependency>
5764
<groupId>org.noear</groupId>
@@ -69,5 +76,6 @@
6976
<artifactId>solon-view-jsp-jakarta</artifactId>
7077
<scope>test</scope>
7178
</dependency>
72-
</dependencies>
79+
80+
</dependencies>
7381
</project>

solon-jakarta-projects/solon-server/solon-server-tomcat-jakarta/src/main/java/org/noear/solon/server/tomcat/TomcatServer.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
*/
1616
package org.noear.solon.server.tomcat;
1717

18+
import java.io.IOException;
19+
20+
import javax.net.ssl.SSLContext;
21+
1822
import org.apache.catalina.Context;
1923
import org.apache.catalina.Wrapper;
2024
import org.apache.catalina.connector.Connector;
@@ -29,12 +33,10 @@
2933
import org.noear.solon.server.handle.SessionProps;
3034
import org.noear.solon.server.prop.impl.HttpServerProps;
3135
import org.noear.solon.server.tomcat.http.TCHttpContextHandler;
32-
33-
import jakarta.servlet.MultipartConfigElement;
3436
import org.noear.solon.server.tomcat.ssl.TomcatSslContext;
37+
import org.noear.solon.server.tomcat.websocket.TcWebSocketManager;
3538

36-
import javax.net.ssl.SSLContext;
37-
import java.io.IOException;
39+
import jakarta.servlet.MultipartConfigElement;
3840

3941
/**
4042
* @author Yukai
@@ -73,6 +75,7 @@ protected Context getContext() {
7375
if (SessionProps.session_timeout > 0) {
7476
context.setSessionTimeout(SessionProps.session_timeout);
7577
}
78+
7679

7780
// for http
7881
MultipartConfigElement multipartConfig = new MultipartConfigElement(
@@ -86,7 +89,11 @@ protected Context getContext() {
8689
servlet.setMultipartConfigElement(multipartConfig);
8790

8891
context.addServletMappingDecoded("/", "solon");//Servlet与对应uri映射
89-
92+
93+
if (enableWebSocket) {
94+
TcWebSocketManager.init(context);
95+
}
96+
9097
return context;
9198
}
9299

@@ -158,4 +165,5 @@ private static SSLHostConfig createSSLHostConfig(final SSLContext sslContext) {
158165
sslHostConfig.addCertificate(sslHostConfigCertificate);
159166
return sslHostConfig;
160167
}
168+
161169
}

solon-jakarta-projects/solon-server/solon-server-tomcat-jakarta/src/main/java/org/noear/solon/server/tomcat/TomcatServerBase.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.noear.solon.server.http.HttpServerConfigure;
3636
import org.noear.solon.server.prop.impl.HttpServerProps;
3737
import org.noear.solon.server.ssl.SslConfig;
38+
import org.noear.solon.server.tomcat.websocket.TcWebSocketManager;
3839
import org.slf4j.Logger;
3940
import org.slf4j.LoggerFactory;
4041

@@ -109,7 +110,7 @@ public void start(String host, int port) throws Throwable {
109110
}
110111

111112
//初始化上下文
112-
initContext();
113+
Context _context = initContext();
113114

114115
//添加连接端口
115116
addConnector(port, true);
@@ -120,6 +121,10 @@ public void start(String host, int port) throws Throwable {
120121
}
121122

122123
_server.start();
124+
125+
if(TcWebSocketManager.isEnableWebSocket()) {
126+
TcWebSocketManager.registerEndpoints(_context);
127+
}
123128
}
124129

125130

@@ -131,8 +136,8 @@ public void stop() throws Throwable {
131136
}
132137
}
133138

134-
protected void initContext() throws IOException {
135-
getContext();
139+
protected Context initContext() throws IOException {
140+
return getContext();
136141
}
137142

138143
protected abstract Context getContext() throws IOException;

solon-jakarta-projects/solon-server/solon-server-tomcat-jakarta/src/main/java/org/noear/solon/server/tomcat/TomcatServerJsp.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public TomcatServerJsp(HttpServerProps props) {
4949
}
5050

5151
@Override
52-
protected void initContext() throws IOException {
52+
protected Context initContext() throws IOException {
5353
Context ctx = getContext();
5454

5555
//jsp
@@ -61,6 +61,7 @@ protected void initContext() throws IOException {
6161
} catch (Exception e) {
6262
log.debug(e.getMessage(), e);
6363
}
64+
return ctx;
6465
}
6566

6667

@@ -69,6 +70,7 @@ private void addJspSupport(Context context) throws IOException {
6970
Wrapper jspServlet = Tomcat.addServlet(context, "jsp", "org.apache.jasper.servlet.JspServlet");
7071
jspServlet.addInitParameter("fork", "false");
7172
jspServlet.addInitParameter("xpoweredBy", "false");
73+
jspServlet.setLoadOnStartup(3);
7274
// jspServlet.addInitParameter("development", "true"); // 开发模式,便于调试
7375

7476
// 设置JSP文件映射
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package org.noear.solon.server.tomcat.websocket;
2+
3+
import java.net.InetSocketAddress;
4+
import java.nio.ByteBuffer;
5+
import java.util.concurrent.Future;
6+
7+
import org.noear.solon.net.websocket.WebSocket;
8+
import org.noear.solon.net.websocket.WebSocketBase;
9+
import org.noear.solon.net.websocket.WebSocketRouter;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
import jakarta.websocket.CloseReason;
14+
import jakarta.websocket.Endpoint;
15+
import jakarta.websocket.EndpointConfig;
16+
import jakarta.websocket.MessageHandler;
17+
import jakarta.websocket.Session;
18+
19+
/**
20+
* Tomcat WebSocket端点实现
21+
*
22+
*/
23+
public class TcWebSocketEndpoint extends Endpoint {
24+
private final String SESSION_KEY = "session";
25+
private static final WebSocketRouter webSocketRouter = WebSocketRouter.getInstance();
26+
27+
@Override
28+
public void onOpen(Session session, EndpointConfig endpointConfig) {
29+
WebSocket socket = new WebSocketImpl(session);
30+
session.getUserProperties().put(SESSION_KEY, socket);
31+
session.addMessageHandler(new BufferMessageHandler(socket));
32+
session.addMessageHandler(new TextMessageHandler(socket));
33+
34+
// 触发onOpen事件
35+
webSocketRouter.getListener().onOpen(socket);
36+
}
37+
38+
@Override
39+
public void onClose(Session session, CloseReason closeReason) {
40+
WebSocket socket = (WebSocket) session.getUserProperties().get(SESSION_KEY);
41+
if (socket != null) {
42+
webSocketRouter.getListener().onClose(socket);
43+
}
44+
}
45+
46+
@Override
47+
public void onError(Session session, Throwable thr) {
48+
WebSocket socket = (WebSocket) session.getUserProperties().get(SESSION_KEY);
49+
if (socket != null) {
50+
webSocketRouter.getListener().onError(socket, thr);
51+
}
52+
}
53+
54+
private static class TextMessageHandler implements MessageHandler.Whole<String> {
55+
private WebSocket socket;
56+
57+
public TextMessageHandler(WebSocket socket) {
58+
this.socket = socket;
59+
}
60+
61+
@Override
62+
public void onMessage(String s) {
63+
try {
64+
webSocketRouter.getListener().onMessage(socket, s);
65+
} catch (Throwable e) {
66+
webSocketRouter.getListener().onError(socket, e);
67+
}
68+
}
69+
}
70+
71+
private static class BufferMessageHandler implements MessageHandler.Whole<ByteBuffer> {
72+
private WebSocket socket;
73+
74+
public BufferMessageHandler(WebSocket socket) {
75+
this.socket = socket;
76+
}
77+
78+
@Override
79+
public void onMessage(ByteBuffer s) {
80+
try {
81+
webSocketRouter.getListener().onMessage(socket, s);
82+
} catch (Throwable e) {
83+
webSocketRouter.getListener().onError(socket, e);
84+
}
85+
}
86+
}
87+
88+
private static class WebSocketImpl extends WebSocketBase {
89+
private static final Logger log = LoggerFactory.getLogger(WebSocketImpl.class);
90+
private Session real;
91+
92+
public WebSocketImpl(Session real) {
93+
this.real = real;
94+
this.init(real.getRequestURI());
95+
}
96+
97+
@Override
98+
public boolean isValid() {
99+
return isClosed() == false && real.isOpen();
100+
}
101+
102+
@Override
103+
public boolean isSecure() {
104+
return real.isSecure();
105+
}
106+
107+
@Override
108+
public InetSocketAddress remoteAddress() {
109+
// Tomcat的Session不直接提供远程地址,这里返回null
110+
return null;
111+
}
112+
113+
@Override
114+
public InetSocketAddress localAddress() {
115+
// Tomcat的Session不直接提供本地地址,这里返回null
116+
return null;
117+
}
118+
119+
@Override
120+
public long getIdleTimeout() {
121+
return real.getMaxIdleTimeout();
122+
}
123+
124+
@Override
125+
public void setIdleTimeout(long idleTimeout) {
126+
real.setMaxIdleTimeout(idleTimeout);
127+
}
128+
129+
@Override
130+
public Future<Void> send(String text) {
131+
return real.getAsyncRemote().sendText(text);
132+
}
133+
134+
@Override
135+
public Future<Void> send(ByteBuffer binary) {
136+
return real.getAsyncRemote().sendBinary(binary);
137+
}
138+
139+
@Override
140+
public void close() {
141+
super.close();
142+
try {
143+
real.close();
144+
} catch (Throwable ignore) {
145+
if (log.isDebugEnabled()) {
146+
log.debug("Close failure: {}", ignore.getMessage());
147+
}
148+
}
149+
}
150+
151+
@Override
152+
public void close(int code, String reason) {
153+
super.close(code, reason);
154+
try {
155+
real.close(new CloseReason(CloseReason.CloseCodes.getCloseCode(code), reason));
156+
} catch (Throwable ignore) {
157+
if (log.isDebugEnabled()) {
158+
log.debug("Close failure: {}", ignore.getMessage());
159+
}
160+
}
161+
}
162+
}
163+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.noear.solon.server.tomcat.websocket;
2+
3+
import java.util.Collection;
4+
5+
import org.apache.catalina.Context;
6+
import org.apache.tomcat.websocket.server.WsSci;
7+
import org.noear.solon.net.websocket.WebSocketRouter;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
11+
import jakarta.websocket.server.ServerContainer;
12+
import jakarta.websocket.server.ServerEndpointConfig;
13+
14+
/**
15+
* Tomcat WebSocket 管理器
16+
*
17+
* @author noear
18+
* @since 3.7.3
19+
*/
20+
public class TcWebSocketManager {
21+
private static final Logger log = LoggerFactory.getLogger(TcWebSocketManager.class);
22+
23+
private TcWebSocketManager() {}
24+
25+
private static boolean enableWebSocket = false;
26+
27+
/**
28+
* 在Tomcat上下文中初始化WebSocket支持
29+
* 注意:此方法需要在上下文初始化之前调用
30+
*/
31+
public static void init(Context context) {
32+
try {
33+
// 注册WebSocket容器初始化器
34+
context.addServletContainerInitializer(new WsSci(), null);
35+
enableWebSocket = true;
36+
log.info("Tomcat WebSocket initialized");
37+
} catch (Exception e) {
38+
log.error("Failed to initialize Tomcat WebSocket", e);
39+
}
40+
}
41+
42+
/**
43+
* 注册WebSocket端点
44+
* 注意:此方法需要在服务器启动后调用
45+
*/
46+
public static void registerEndpoints(Context context) {
47+
try {
48+
if (context == null) {
49+
log.error("Tomcat Context is null, cannot register WebSocket endpoints");
50+
return;
51+
}
52+
// 获取ServerContainer
53+
ServerContainer serverContainer = (ServerContainer) context.getServletContext()
54+
.getAttribute("jakarta.websocket.server.ServerContainer");
55+
56+
if (serverContainer == null) {
57+
throw new IllegalStateException("Missing jakarta.websocket.server.ServerContainer");
58+
}
59+
60+
// 获取所有注册的路径
61+
Collection<String> paths = WebSocketRouter.getInstance().getPaths();
62+
63+
for (String path : paths) {
64+
if (path.startsWith("/")) {
65+
ServerEndpointConfig endpointConfig = ServerEndpointConfig.Builder
66+
.create(TcWebSocketEndpoint.class, path)
67+
.build();
68+
serverContainer.addEndpoint(endpointConfig);
69+
log.info("Tomcat Registered WebSocket endpoint: {}", path);
70+
}
71+
}
72+
} catch (Throwable e) {
73+
log.error("Failed to register WebSocket endpoints", e);
74+
throw new RuntimeException(e);
75+
}
76+
}
77+
78+
public static boolean isEnableWebSocket() {
79+
return enableWebSocket;
80+
}
81+
82+
}

solon-jakarta-projects/solon-server/solon-server-undertow-add-jsp-jakarta/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
<dependency>
6363
<groupId>jakarta.servlet.jsp</groupId>
6464
<artifactId>jakarta.servlet.jsp-api</artifactId>
65-
<version>3.1.1</version>
65+
<version>4.0.0</version>
6666
</dependency>
6767
<!--jsp end-->
6868

0 commit comments

Comments
 (0)