Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue Aug 21, 2014 · 4 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Aug 21, 2014

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

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

I'm about to commit a revision where ServletEndpointExporter implements ServletContextAware itself and performs ServerContainer detection at that point. This comes at the price of an immediate Servlet API dependency but talking to Rossen, this won't make much difference since that class is dependent on the Servlet API at runtime anyway. To be backported to 4.0.7 as well.

Two refinements that go with this in 4.1: afterPropertiesSet doesn't declare Exception anymore, and a setServerContainer method allows for specifying the container directly (avoiding the ServletContext retrieval}.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Teh Kok How commented

I am using Spring Boot 2.1.0.RELEASE with Java 11 and Tomcat 9 deploying WAR into tomcat webapp and still see the same error.

@spring-projects-issues
Copy link
Collaborator Author

Teh Kok How commented

I added the workaround code snippet suggested but doesn't help.

@spring-projects-issues
Copy link
Collaborator Author

Teh Kok How commented

It seems to happen only on Tomcat 9. Adding the suggested code snippet doesn't help but only gives a similar exception: 
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'endpointExporterInitializer' defined in class path resource [com/graphql/book/AppConfig.class]: Initialization of bean failed; nested exception is java.lang.RuntimeException: java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available|
 
eh3rrera/graphql-java-spring-boot-example#10
graphql-java-kickstart/graphql-spring-boot#165

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants