Integrating JSR-356 WebSocket @ServerEndpoint with Spring 3 beans
Asked Answered
M

5

8

I'm using Spring 3.2.5 without full new JSR-356 WebSockets support.

I would like to have singleton-bean reference in my @ServerEndpoint WebSocket server, which is instantiated by servlet container itself, not in Spring context.

What is the clean way to do it?

My current solution: I made @Service singleton bean with instance in static field:

@Service
public class WebSocketSupportBean {
    private volatile static WebSocketSupportBean instance = null;

    public static WebSocketSupportBean getInstance() {
        return instance;
    }

    public WebSocketSupportBean() {
        instance = this;
    }

and just getting it in @ServerEndpoint by static method, disconnecting user if null returned (if bean not jet created during server startup but user connects):

Merrill answered 16/3, 2014 at 10:20 Comment(0)
F
7

You can setup websockets with spring framework 3.x

I developed a small proof-of-concept application to demonstrate how, based on Rossen Stoyanchev's SpringConfiguration released with spring-core 4.0.

The application sets up a websocket server endpoint with uri /wstest which will use a @Autowired spring bean to select a greeting word and reply to a websocket message.

The websocket connection is initiated and messages sent by an html page (index.html) running in a browser that supports websockets.

The Endpoint registration is made by a ServletContextListener at context initialization and when the endpoint is instantiated it will be wired with spring:

@WebListener
public class MyApplication implements ServletContextListener {

    private final static String SERVER_CONTAINER_ATTRIBUTE = "javax.websocket.server.ServerContainer";

    @Override
    public void contextInitialized(ServletContextEvent sce) {

        ServletContext container = sce.getServletContext();

        final ServerContainer serverContainer = (ServerContainer) container.getAttribute(SERVER_CONTAINER_ATTRIBUTE);
        try {
            serverContainer.addEndpoint(new MyEndpointConfig(MyEndpoint.class, "/wstest"));
        } catch (DeploymentException e) {
            e.printStackTrace();
        }
    }
}

And the Endpoint is:

@Component
public class MyEndpoint extends Endpoint {

    @Autowired
    MyService myService;

    @Override
    public void onOpen(Session session, EndpointConfig config) {

        session.addMessageHandler(new MyMessageHandler(session));
    }


    class MyMessageHandler implements MessageHandler.Whole<String> {

        final Session session;

        public MyMessageHandler(Session session) {
            this.session = session;
        }

        @Override
        public void onMessage(String message) {
            try {
                String greeting = myService.getGreeting();
                session.getBasicRemote().sendText(greeting + ", got your message (" + message + "). Thanks ! (session: " + session.getId() + ")");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Checkout the full source and ready to run example on my Github page.

Falito answered 20/3, 2014 at 19:26 Comment(2)
Great example! As a good practice, I would enclose the cast to (ServerContainer) in the try catch too. I (lazily) copied your snippet and was having a completely silent class cast exception while deploying on Tomcat due to dependency problems. It took me a couple hours to find the problem.Subtemperate
can you help to check my question in github. there may exist bug in this codeAnthem
G
2

You have to add bean definition in the configuration of spring.

The solution i found to integrate JSR 356 websocket @ServerEndpoint is to turn off the Servlet container's scan for WebSocket endpoints by spring which can be done by registering @Bean in your Spring Configuration. By this spring not overrides normal JSR 356 websocket by spring STOMP websocket which is the part of the websocket.

@ServerEndpoint(value="/chatMessage")
public class ChatEndpoint{
// Normal websocket body goes here.
}

Adding Beans in your Configuration as:

@Configuration
public class WebsocketConfig{
  @Bean
  public ChatEndpoint chatEndpoint(){
     return new ChatEndpoint();
  }
  // main one is ServerEndpointExporter which prevents Servlet container's scan for WebSocket
  @Bean
  public ServerEndpointExporter endpointExporter(){
     return new ServerEndpointExporter();
  }
}

This all done for you. But you should remove configurator = SpringConfigurator.class from @ServerEndpoint. I am using Spring Websocket 4.0.0 and it works fine. You can also see this Link.

If you alright then follow this Link also for concept.

Note that, Normally you should make websocket configuration separately from the main configuration of your spring.

Graeae answered 15/8, 2018 at 13:30 Comment(0)
T
1

Try

@ServerEndpoint(value = "/ws", configurator = SpringConfigurator.class)

And add maven dependency

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
</dependency>
Tonsorial answered 27/4, 2017 at 2:40 Comment(1)
spring-websocket is post 4.0, not spring 3.Unsearchable
C
1

You can make your @ServerEndpoint object extend SpringBeanAutowiringSupport. Then just make it aware of beans that gets constructed within a Spring-based web application this way:

  @PostConstruct
    public void init() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    }

This way @Autowired annotation will worl correctly:

@Autowired MyService myService;
Countess answered 16/6, 2017 at 9:34 Comment(0)
O
1

try this,it works for me

@Component
@ServerEndpoint(value = "/instantMessageServer",configurator = SpringConfigurator.class)
public class InstantMessageServer{
private static IChatService chatService;
    @Autowired
    public InstantMessageServer(IChatService chatService){
        this.chatService = chatService;
    }
    public InstantMessageServer(){}
}

I found this solution on https://spring.io/blog/2013/05/23/spring-framework-4-0-m1-websocket-support but there is one more glitch,the class annotated with @ServerEndpoint cant acquire httpsession with SpringConfigurator,there is no a override of method modifyhandler in it.Maybe we create a seperate Configurator extends SpringConfigurator and override that method would be a workaroud. It is better to build a real-time web application with spring-websocket and messaging api,I think.


public class ModifiedServerEndpointConfigurator extends SpringConfigurator{
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
        super.modifyHandshake(sec, request, response);
    }
}
Outright answered 18/2, 2018 at 2:15 Comment(1)
I dont know whether there are more bugs in the future.Newscast

© 2022 - 2024 — McMap. All rights reserved.