Skip to content

When using the Actuator, an app with a custom DispatcherServlet bean named dispatcherServlet fails to start #13527

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
wilkinsona opened this issue Jun 19, 2018 · 13 comments
Assignees
Labels
type: regression A regression from a previous release
Milestone

Comments

@wilkinsona
Copy link
Member

The changes made to fix #12934 and #13106 mean that an app like this:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.DispatcherServlet;

@SpringBootApplication
public class CustomDispatcherServletApplication {

	public static void main(String[] args) {
		SpringApplication.run(CustomDispatcherServletApplication.class, args);
	}

	@Bean
	public DispatcherServlet dispatcherServlet() {
		return new DispatcherServlet();
	}

}

Will fail to start with a failure like this:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.3.RELEASE)

2018-06-19 20:20:20.305  INFO 8473 --- [           main] c.e.d.CustomDispatcherServletApplication : Starting CustomDispatcherServletApplication on aw-rmbp.local with PID 8473 (/Users/awilkinson/dev/workspaces/spring/spring-boot/2.0.x/custom-dispatcher-servlet/target/classes started by awilkinson in /Users/awilkinson/dev/workspaces/spring/spring-boot/2.0.x/custom-dispatcher-servlet)
2018-06-19 20:20:20.308  INFO 8473 --- [           main] c.e.d.CustomDispatcherServletApplication : No active profile set, falling back to default profiles: default
2018-06-19 20:20:20.352  INFO 8473 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@35083305: startup date [Tue Jun 19 20:20:20 BST 2018]; root of context hierarchy
2018-06-19 20:20:21.517  INFO 8473 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2018-06-19 20:20:21.541  INFO 8473 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2018-06-19 20:20:21.542  INFO 8473 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.31
2018-06-19 20:20:21.550  INFO 8473 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/awilkinson/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]
2018-06-19 20:20:21.643  INFO 8473 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2018-06-19 20:20:21.643  INFO 8473 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1294 ms
2018-06-19 20:20:21.872 ERROR 8473 --- [ost-startStop-1] o.s.b.web.embedded.tomcat.TomcatStarter  : Error starting Tomcat context. Exception: org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name 'servletEndpointRegistrar' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.endpoint.web.ServletEndpointRegistrar]: Factory method 'servletEndpointRegistrar' threw exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' available
2018-06-19 20:20:21.889  INFO 8473 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2018-06-19 20:20:21.894  WARN 8473 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
2018-06-19 20:20:21.902  INFO 8473 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-19 20:20:22.007 ERROR 8473 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Method servletEndpointRegistrar in org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration required a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' that could not be found.
	- Bean method 'mainDispatcherServletPathProvider' not loaded because Default DispatcherServlet found dispatcher servlet bean dispatcherServlet


Action:

Consider revisiting the conditions above or defining a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' in your configuration.

I'm not entirely sure how to fix this. We need to get hold of the dispatcher servlet that's going to dispatch to any servlet endpoints and get the path to which it's mapped.

EndpointRequest falls back to "" if there's no DispatcherServletPathProvider but I'm not sure that's right either.

@wilkinsona wilkinsona added type: bug A general bug type: regression A regression from a previous release and removed type: bug A general bug labels Jun 19, 2018
@wilkinsona wilkinsona added this to the 2.0.4 milestone Jun 19, 2018
@wilkinsona wilkinsona changed the title When using the Actuator, and app with a DispatcherServlet bean named dispatcherServlet fails to start When using the Actuator, and app with a custom DispatcherServlet bean named dispatcherServlet fails to start Jun 19, 2018
@wilkinsona wilkinsona added the for: team-attention An issue we'd like other members of the team to review label Jun 19, 2018
@wilkinsona wilkinsona changed the title When using the Actuator, and app with a custom DispatcherServlet bean named dispatcherServlet fails to start When using the Actuator, an app with a custom DispatcherServlet bean named dispatcherServlet fails to start Jun 20, 2018
@philwebb philwebb removed the for: team-attention An issue we'd like other members of the team to review label Jun 20, 2018
@lpetit-yseop
Copy link

I am facing this issue at work.

The workaround I found at the moment is to add the missing bean, like this:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class DispatcherServletPathProvider implements org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider {

    @Value("${server.servlet.context-path}")
    private String servletContextPath;

    @Override
    public String getServletPath() {
        return servletContextPath;
    }

}

I don't know whether having a default implementation like this for this bean would solve the issue generically?

@wilkinsona
Copy link
Member Author

@lpetit-yseop That's a good workaround, but I think you want to use server.servlet.path rather than server.servlet.context-path.

A generic solution is more complicated if we want to cope with custom dispatcher servlet registration beans. They may not be using the server.servlet.path property to configure the servlet's URL mapping(s) so we'd need to find the registration bean and reverse engineer the path from its URL mapping.

@lpetit-yseop
Copy link

lpetit-yseop commented Jun 22, 2018

Thank you for the confirmation.

I am having a hard time truly understanding the relation between server.servlet.context-path and server.servlet.path.
Currently, I only find light descriptions in the Spring Boot documentation like server.servlet.path=/ # Path of the main dispatcher servlet., no mention in the Spring MVC documentation…

Did I miss some place where it is documented, like "the full real path for controllers results in /${server.servlet.context-path}/${server.servlet.path} being pre-pended to routes defined at the controller-level", etc.?

@lpetit-yseop
Copy link

So, if I want my workaround to be more resilient to future unexpected changes, should I be using a combination of server.servlet.context-path and server.servlet.path (like return servletContextPath + "/" + servletPath;) or only servletPath as you mention?

@mbhave mbhave self-assigned this Jun 22, 2018
@ranarula
Copy link
Contributor

ranarula commented Jun 22, 2018

I was looking at the fix done for #13106 . Unfortunately the fix is breaking a few of our integration test when we are trying to upgrade to SpringBoot 2.0.3 from 2.0.2. It's for scenarios when need MVC context with Jersey. We need MVC with Jersey for cases when we need to serve static files?

We are seeing the following error

Method servletEndpointRegistrar in org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration required a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' that could not be found.
    - Bean method 'mainDispatcherServletPathProvider' not loaded because Default DispatcherServlet found non dispatcher servlet bean dispatcherServlet

Action:

Consider revisiting the conditions above or defining a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' in your configuration.

I feel line 57 in ServletEndpointManagementContextConfiguration.java should have another condition as @Conditional(DefaultDispatcherServletCondition.class)

The work around we are using for now is to have the following config in our tests

@Bean
@Primary
public DispatcherServletPathProvider testDispatcherServletPathProvider() {
    return () -> "";
}

@mbhave
Copy link
Contributor

mbhave commented Jun 22, 2018

@ranarula I think that's a slightly different issue than the one being discussed here. Could you raise a separate issue with a minimal sample that reproduces the behavior you described?

@ranarula
Copy link
Contributor

ranarula commented Jun 22, 2018

Thanks @mbhave, Will create a sample and an issue but seems its related to this in a sense that in my case instead of a custom DispatcherServlet we have a real DispatcherServlet (class & not bean) in the context

Though looking at it closely seems just adding the the above suggested change might not work alone as there is @ConditionalOnMissingClass on Jersey config

@ltorzynski

This comment has been minimized.

@snicoll

This comment has been minimized.

@ltorzynski

This comment has been minimized.

@snicoll

This comment has been minimized.

@chrisleves

This comment has been minimized.

@philwebb

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A regression from a previous release
Projects
None yet
Development

No branches or pull requests

8 participants