Skip to content

Allow @WebListener ServletContextListeners to programmatically add servlets and filters #18303

@janbartel

Description

@janbartel

SpringBoot version 2.1.8.RELEASE

Please refer to jetty issue jetty/jetty.project#4103.
See also StackOverflow reports of same problem for Tomcat: https://stackoverflow.com/questions/44389716/spring-boot-embedded-tomcat-weblistener-scanned-by-servletcomponentscan

The issue is that a ServletContextListener that is annotated with @WebListener is not able to programmatically add Servlets (and I suppose Filters, but I didn't test that), but throws an UnsupportedOperationException. Here's an example of such an exception for jetty:

org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Jetty web server
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:156) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) ~[spring-context-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:744) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:391) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1204) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at com.example.demo.DemoApplication.main(DemoApplication.java:13) ~[classes!/:0.0.1-SNAPSHOT]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:51) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
	at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:52) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Jetty web server
	at org.springframework.boot.web.embedded.jetty.JettyWebServer.initialize(JettyWebServer.java:114) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.boot.web.embedded.jetty.JettyWebServer.<init>(JettyWebServer.java:86) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory.getJettyWebServer(JettyServletWebServerFactory.java:398) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory.getWebServer(JettyServletWebServerFactory.java:153) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:180) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:153) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]
	... 16 common frames omitted
Caused by: java.lang.UnsupportedOperationException: null
	at org.eclipse.jetty.servlet.ServletContextHandler$Context.checkDynamic(ServletContextHandler.java:1088) ~[jetty-servlet-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.servlet.ServletContextHandler$Context.addServlet(ServletContextHandler.java:1245) ~[jetty-servlet-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at com.example.demo.MyServletContextListener.contextInitialized(MyServletContextListener.java:16) ~[classes!/:0.0.1-SNAPSHOT]
	at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:930) ~[jetty-server-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:555) ~[jetty-servlet-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:889) ~[jetty-server-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:357) ~[jetty-servlet-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1443) ~[jetty-webapp-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1407) ~[jetty-webapp-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:822) ~[jetty-server-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:276) ~[jetty-servlet-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:524) ~[jetty-webapp-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:72) ~[jetty-util-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:169) ~[jetty-util-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.server.Server.start(Server.java:407) ~[jetty-server-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:110) ~[jetty-util-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:106) ~[jetty-server-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.server.Server.doStart(Server.java:371) ~[jetty-server-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:72) ~[jetty-util-9.4.21-SNAPSHOT.jar!/:9.4.21-SNAPSHOT]
	at org.springframework.boot.web.embedded.jetty.JettyWebServer.initialize(JettyWebServer.java:108) ~[spring-boot-2.1.8.RELEASE.jar!/:2.1.8.RELEASE]

This problem occurs because springboot is processing the @WebListener annotation and then using the ServletContext programmatic interfaces to add the listener: this classifies the listener as being programmatically added, and thus not permitted to add servlets/filters/listeners in its contextInitialized() method. See the ServletSpecification section 4.4:

If the ServletContext passed to the ServletContextListener’s
contextInitialized method where the ServletContextListener was neither
declared in web.xml or web-fragment.xml nor annotated with @WebListener
then an UnsupportedOperationException MUST be thrown for all the methods
defined in ServletContext for programmatic configuration of servlets, filters and
listeners.

Here is the springboot stacktrace showing where the @WebListener is being added as a programmatic listener:

	at org.eclipse.jetty.server.handler.ContextHandler.addProgrammaticListener(ContextHandler.java:718)
	at org.eclipse.jetty.servlet.ServletContextHandler.access$000(ServletContextHandler.java:89)
	at org.eclipse.jetty.servlet.ServletContextHandler$Context.addListener(ServletContextHandler.java:1439)
	at org.springframework.boot.web.servlet.ServletListenerRegistrationBean.register(ServletListenerRegistrationBean.java:116)
	at org.springframework.boot.web.servlet.RegistrationBean.onStartup(RegistrationBean.java:53)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:228)
	at org.springframework.boot.web.embedded.jetty.ServletContextInitializerConfiguration.callInitializers(ServletContextInitializerConfiguration.java:65)
	at org.springframework.boot.web.embedded.jetty.ServletContextInitializerConfiguration.configure(ServletContextInitializerConfiguration.java:54)
	at org.eclipse.jetty.webapp.WebAppContext.configure(WebAppContext.java:498)
	at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1402)
	at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:822)
	at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:276)
	at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:524)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:72)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:169)
	at org.eclipse.jetty.server.Server.start(Server.java:407)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:110)
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:106)
	at org.eclipse.jetty.server.Server.doStart(Server.java:371)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:72)
	at org.springframework.boot.web.embedded.jetty.JettyWebServer.initialize(JettyWebServer.java:108)
	at org.springframework.boot.web.embedded.jetty.JettyWebServer.<init>(JettyWebServer.java:86)
	at org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory.getJettyWebServer(JettyServletWebServerFactory.java:398)
	at org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory.getWebServer(JettyServletWebServerFactory.java:153)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:180)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:153)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:744)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:391)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1204)
	at com.example.demo.DemoApplication.main(DemoApplication.java:13)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:51)
	at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:52)

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions