JEUS 환경에서 Spring WebSocket 을 구현하여 사용하려고 하면 다음과 같은 오류들이 출력되면서 배포에 실패하거나 Application 이 정상 동작하지 않는 문제가 발생하게 됩니다.
Root Exception stack trace:
java.lang.IllegalStateException: No suitable default RequestUpgradeStrategy found
at org.springframework.web.socket.server.support.AbstractHandshakeHandler.initRequestUpgradeStrategy(AbstractHandshakeHandler.java:160)
at org.springframework.web.socket.server.support.AbstractHandshakeHandler.<init>(AbstractHandshakeHandler.java:123)
at org.springframework.web.socket.server.support.DefaultHandshakeHandler.<init>(DefaultHandshakeHandler.java:35)
at org.springframework.web.socket.config.annotation.AbstractWebSocketHandlerRegistration.getOrCreateHandshakeHandler(AbstractWebSocketHandlerRegistration.java:176)
at org.springframework.web.socket.config.annotation.AbstractWebSocketHandlerRegistration.getMappings(AbstractWebSocketHandlerRegistration.java:163)
at org.springframework.web.socket.config.annotation.ServletWebSocketHandlerRegistry.getHandlerMapping(ServletWebSocketHandlerRegistry.java:118)
at org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport.webSocketHandlerMapping(WebSocketConfigurationSupport.java:52)
간혹 다음과 같은 오류가 출력되기도 합니다.
Root Exception stack trace:
java.lang.IllegalArgumentException: No 'javax.websocket.server.ServerContainer' ServletContext attribute. Are you running in a Servlet container that supports JSR-356?
at org.springframework.util.Assert.notNull(Assert.java:134)
at org.springframework.web.socket.server.standard.AbstractStandardUpgradeStrategy.getContainer(AbstractStandardUpgradeStrategy.java:68)
at org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy.getContainer(TomcatRequestUpgradeStrategy.java:85)
at org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy.getContainer(TomcatRequestUpgradeStrategy.java:47)
at org.springframework.web.socket.server.standard.AbstractStandardUpgradeStrategy.getSupportedExtensions(AbstractStandardUpgradeStrategy.java:88)
at org.springframework.web.socket.server.support.AbstractHandshakeHandler.doHandshake(AbstractHandshakeHandler.java:273)
at org.springframework.web.socket.sockjs.transport.handler.WebSocketTransportHandler.handleRequest(WebSocketTransportHandler.java:124)
at org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService.handleTransportRequest(TransportHandlingSockJsService.java:314)
at org.springframework.web.socket.sockjs.support.AbstractSockJsService.handleRequest(AbstractSockJsService.java:433)
at org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler.handleRequest(SockJsHttpRequestHandler.java:132)
at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:51)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
그 이유는, Spring Framework 에서 제공하는 WebSocket 라이브러리가 JEUS 환경을 지원하지 않기 때문에 그렇습니다.
Spring Framework GitHub (https://github.com/spring-projects/spring-framework) 에서 코드를 확인해보면 각 WAS 제품별로 하드코딩되어, 지원하는 WAS 가 명시적으로 선언되어 있는 것을 확인할 수 있습니다.
아래 AbstractHandshakeHandler 클래스의 코드를 보면 Tomcat, Jetty, Undertow, GlassFish, WebLogic, WebSphere 제품만 지원하는 것을 볼 수 있습니다. (아래 예시는 5.3.x 버전의 코드입니다.)
package org.springframework.web.socket.server.support;
public abstract class AbstractHandshakeHandler implements HandshakeHandler, Lifecycle {
private static RequestUpgradeStrategy initRequestUpgradeStrategy() {
String className;
if (tomcatWsPresent) {
className = "org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy";
}
else if (jettyWsPresent) {
className = "org.springframework.web.socket.server.jetty.JettyRequestUpgradeStrategy";
}
else if (jetty10WsPresent) {
className = "org.springframework.web.socket.server.jetty.Jetty10RequestUpgradeStrategy";
}
else if (undertowWsPresent) {
className = "org.springframework.web.socket.server.standard.UndertowRequestUpgradeStrategy";
}
else if (glassfishWsPresent) {
className = "org.springframework.web.socket.server.standard.GlassFishRequestUpgradeStrategy";
}
else if (weblogicWsPresent) {
className = "org.springframework.web.socket.server.standard.WebLogicRequestUpgradeStrategy";
}
else if (websphereWsPresent) {
className = "org.springframework.web.socket.server.standard.WebSphereRequestUpgradeStrategy";
}
else {
throw new IllegalStateException("No suitable default RequestUpgradeStrategy found");
}
try {
Class<?> clazz = ClassUtils.forName(className, AbstractHandshakeHandler.class.getClassLoader());
return (RequestUpgradeStrategy) ReflectionUtils.accessibleConstructor(clazz).newInstance();
}
catch (Exception ex) {
throw new IllegalStateException(
"Failed to instantiate RequestUpgradeStrategy: " + className, ex);
}
}
}
* Spring Framework 7 (Spring Boot 4) 부터는 하드코딩이 아닌 StandardWebSocketUpgradeStrategy 를 이용한 표준화된 방식으로 변경될 예정입니다.
JEUS 는 Spring WebSocket 을 지원하기 위해 제품 내부에 라이브러리가 포함되어 있습니다.
$JEUS_HOME/lib/shared/spring-support/4.2.0.RELEASE 디렉토리 내부에 있는 spring-support-4.2.0.RELEASE.jar 파일을 사용하시면 됩니다.
[jeus85@was1 4.2.0.RELEASE]$ pwd
/sw/jeus85/lib/shared/spring-support/4.2.0.RELEASE
[jeus85@was1 4.2.0.RELEASE]$ ls -l
합계 8
-rwx------. 1 jeus85 was 6542 10월 19 2021 spring-support-4.2.0.RELEASE.jar
spring-support 라이브러리를 프로젝트 IDE 로 가져와서 개발에 사용하시면 되며, Application 배포시에는 해당 라이브러리가 /WEB-INF/lib 경로에 같이 포함되어 배포되어야 합니다.
다음과 같은 방법으로 HandshakeHandler 또는 RequestUpgradeStrategy 를 JEUS 에서 제공하는 클래스로 변경하여 구현해주시면 됩니다.
1) @Configuration Annotation (WebSocketConfigurer) 으로 설정하는 경우
package com.tmaxsoft.test;
import jeus.spring.websocket.JeusHandshakeHandler;
import jeus.spring.websocket.JeusRequestUpgradeStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
// JeusHandshakeHandler 를 사용하는 경우
@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
private final MyWebSocketHandler wshandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(wshandler, "/ws")
.setHandshakeHandler(new JeusHandshakeHandler())
.setAllowedOrigins("*");
}
}
// JeusRequestUpgradeStrategy 를 사용하는 경우
@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
private final MyWebSocketHandler wshandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(wshandler, "/ws")
.setHandshakeHandler(getHandshakeHandler())
.setAllowedOrigins("*");
}
@Bean
public DefaultHandshakeHandler getHandshakeHandler() {
return new DefaultHandshakeHandler(new JeusRequestUpgradeStrategy());
}
}
2) XML 파일로 Bean 선언을 하는 경우 (spring-context.xml 등)
// JeusHandshakeHandler 를 사용하는 경우
<beans>
<websocket:handlers>
<websocket:mapping path="/ws" handler="wsHandler"/>
<websocket:handshake-handler ref="handshakeHandler"/>
</websocket:handlers>
<bean id="handshakeHandler" class="jeus.spring.websocket.JeusHandshakeHandler"/>
</beans>
// JeusRequestUpgradeStrategy 를 사용하는 경우
<beans>
<websocket:handlers>
<websocket:mapping path="/ws" handler="wsHandler"/>
<websocket:handshake-handler ref="handshakeHandler"/>
</websocket:handlers>
<bean id="handshakeHandler" class="org.springframework.web.socket.server.support.DefaultHandshakeHandler">
<constructor-arg ref="upgradeStrategy"/>
</bean>
<bean id="upgradeStrategy" class="jeus.spring.websocket.JeusRequestUpgradeStrategy"/>
</beans>
*참고 : 중요점은 registry.setHandshakeHandler() 메소드를 이용해 JEUS 제공 클래스를 가져오는 것입니다.
JeusHandshakeHandler 를 사용하더라도 생성자에서 new JeusRequestUpgradeStrategy() 가 return 되므로 두 방법 간 차이점은 없으나 간헐적으로 문제가 발생하는 사례가 있으므로, Application 의 필요에 따라 적절한 방식을 선택하여 구현하시면 됩니다.
** 주의! : WebtoB 를 같이 사용하는 환경인 경우, 일반적인 WebtoB - JEUS 연결 (WJP 방식) 환경에서는 WebSocket 연결이 정상 동작하지 않습니다. 반드시 WebtoB - JEUS 연결을 Reverse Proxy 연결 구성으로 별도 구성해주어야 합니다.
'Middleware > JEUS' 카테고리의 다른 글
JEUS 내장 CORS Filter 사용 가이드 (0) | 2025.02.05 |
---|---|
JEUS DataSource 구성에서 log4jdbc 사용 설정 방법 (0) | 2024.08.14 |
JEUS 환경에 Spring Boot 배포 시 내장 WAS(Tomcat) 제거하는 방법 (0) | 2023.11.07 |
JEUS 에 Application 배포하기 (0) | 2023.04.10 |
JEUS MS(컨테이너) 추가 방법 (0) | 2023.03.14 |
댓글