Skip to content

ServerEndpointExporter causes application context refresh to fail with an NPE when used in a Spring Boot app [SPR-12109] #16725

Closed
@spring-projects-issues

Description

@spring-projects-issues

Andy Wilkinson opened SPR-12109 and commented

This Boot app illustrates the problem:

package sample;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import org.springframework.web.socket.server.standard.SpringConfigurator;

@ComponentScan
@EnableAutoConfiguration
@Configuration
public class Application {
	
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
    @Bean
    public EchoEndpoint echoEndpoint() {
        return new EchoEndpoint();
    }
    
    @Bean
    public ServerEndpointExporter endpointExporter() {
    	return new ServerEndpointExporter();
    }
    
    @ServerEndpoint(value = "/echo", configurator = SpringConfigurator.class)
    private static class EchoEndpoint {

        @OnMessage
        public void handleMessage(Session session, String message) throws IOException {
            session.getBasicRemote().sendText("echo: " + message);
        }
    }
}

Running it fails with a NullPointerException:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'endpointExporter' defined in class sample.Application: Initialization of bean failed; nested exception is java.lang.IllegalStateException: Failed to get javax.websocket.server.ServerContainer via ServletContext attribute
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:232)
	at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:618)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:467)
	at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:120)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:952)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:941)
	at sample.Application.main(Application.java:26)
Caused by: java.lang.IllegalStateException: Failed to get javax.websocket.server.ServerContainer via ServletContext attribute
	at org.springframework.web.socket.server.standard.ServerEndpointExporter.getServerContainer(ServerEndpointExporter.java:113)
	at org.springframework.web.socket.server.standard.ServerEndpointExporter.setApplicationContext(ServerEndpointExporter.java:86)
	at org.springframework.context.support.ApplicationContextAwareProcessor.invokeAwareInterfaces(ApplicationContextAwareProcessor.java:119)
	at org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization(ApplicationContextAwareProcessor.java:94)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:407)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1545)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
	... 14 more
Caused by: java.lang.NullPointerException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.springframework.web.socket.server.standard.ServerEndpointExporter.getServerContainer(ServerEndpointExporter.java:110)
	... 20 more

ServerEndpointExporter assumes that WebApplicationContext.getServletContext() will return a non-null value when it's called from within setApplicationContext(context). This assumption doesn't hold true in a Boot application as the embedded Tomcat server hasn't been started yet.

A work around is to replace the ServerEndpointExporter bean with the following:

@Bean
public ServletContextAware endpointExporterInitializer(final ApplicationContext applicationContext) {
    return new ServletContextAware() {

		@Override
		public void setServletContext(ServletContext servletContext) {
			ServerEndpointExporter serverEndpointExporter = new ServerEndpointExporter();
				serverEndpointExporter.setApplicationContext(applicationContext);
			try {
				serverEndpointExporter.afterPropertiesSet();
			} catch (Exception e) {
				throw new RuntimeException(e);
			}				
		}    		
    };
}

This defers ServerEndpointExporter's processing until a time when the ServletContext is available


Affects: 4.0.6

Reference URL: http://stackoverflow.com/questions/25390100/using-java-api-for-websocket-jsr-356-with-spring-boot/25425384#25425384

Issue Links:

Referenced from: commits 379e5ab, 11805b6

Backported to: 4.0.7

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)status: backportedAn issue that has been backported to maintenance branchestype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions