본문 바로가기
Middleware/JEUS

JEUS 에서 Spring WebSocket 사용하기

by nxCore 2025. 2. 4.

 

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 연결 구성으로 별도 구성해주어야 합니다.

 

댓글