Mapping conflict when deploying with spring boot Jersey starter deployed as WAR - spring-boot

We're using Spring Boot with its Jersey Starter and deploy it as a WAR, programmatically deployed into another application's embedded Tomcat.
After our application startup, in some environments, a mapping conflict occurs and is logged as follows:
o.g.j.s.i.JerseyServletContainerInitializer : Mapping conflict. A Servlet registration exists with same mapping as the Jersey servlet application, named com.vidal.pmsi.config.PmsiResourceConfiguration, at the servlet mapping, /*.
The resource configuration is as follows:
#ApplicationPath("/")
#ExposedApplication
#Component
public class PmsiResourceConfiguration extends ResourceConfig {
public PmsiResourceConfiguration() {
packages("com.vidal.pmsi.api");
packages("com.vidal.pmsi.config");
property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
}
As far as I understand, Spring Boot Jersey Starter will register a 'jerseyServlet'-named servlet mapping to '/*'.
In some environments, Jersey's own JerseyServletContainerInitializer will trigger after SpringApplication startup, failing to register PmsiResourceConfiguration because of the existing jerseyServlet mapping.
This is a problem because of our own open-source library that tries (and crashes at) getting the context path at startup:
// compile-time generated Linkers.java
#WebListener
#Generated("fr.vidal.oss.jax_rs_linker.LinkerAnnotationProcessor")
public final class Linkers implements ServletContextListener {
private static String contextPath = "";
private static String applicationName = ApplicationName.get();
#Override
public void contextInitialized(ServletContextEvent sce) {
//applicationName = FQCN of PmsiResourceConfiguration
contextPath = ContextPaths.contextPath(sce.getServletContext(), applicationName);
}
// [...]
}
// ContextPaths.java
public static String contextPath(ServletContext servletContext, String registeredKey) {
// registeredKey is therefore the FQCN of PmsiResourceConfiguration
String mappedPath = stripWildcard(servletContext.getServletRegistration(registeredKey).getMappings().iterator().next());
return servletContext.getContextPath() + mappedPath;
}
The last snippet of code will fail as there is no mapping for the registered resource configuration class (there is only one for 'jerseyServlet' key).
This does not fail when there isn't any mapping conflict reported.
Why?

I was running my head against a similar problem where I have a Spring Boot Application with a Jersey JAX-RS Webservice. Everything worked fine when using the embeddedTomcat but it went to hell when I tried deploying the war on a regular Tomcat of the same version (Tomcat8).
The problem is that by default the embeddedTomcat does not scan for ServletContainerInitializer in jar files, yet the regular one does and that conflicted with the ServletContainer/Config that Spring sets up.
Apart from excluding the jar containing the JerseyServletContainerInitializer I found an option to tell tomcat to filter out this particular ServletContainerInitializer (SCI). Setting the containerSciFilter attribute on the context helped:
<Context containerSciFilter="JerseyServletContainerInitializer">
...
</Context>
I did not define any SCI in my META-INF/services but the jar that contains the JerseySCI has defined it and it was on the right path to be found by Tomcat.
Considering that this was the closest matchinb question and without an answer I don't repost my question and try to answer this one as I believe that the cause is the same.

Related

Embedded servlet container does not handle META-INF/resources in Spring Boot

My WAR application use non-Spring library (for JSF). The library initializes using servletContext.getResource("page.html"). page.html is within a JAR in WEB-INF/lib, packaged as META-INF/resources/page.html
That works excellent when I deploy WAR on servlet container. But when I run application as executable WAR it does not work because embedded servlet container does not scan classpath META-INF/resources.
For example for Undertow classpath resource manager is not used:
private ResourceManager getDocumentRootResourceManager() {
File root = getCanonicalDocumentRoot();
if (root.isDirectory()) {
return new FileResourceManager(root, 0);
}
if (root.isFile()) {
return new JarResourceManager(root);
}
return ResourceManager.EMPTY_RESOURCE_MANAGER;
}
https://github.com/spring-projects/spring-boot/blob/master/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java#L466
Time for question: why embedded servlet container ignores META-INF/resources? It is a problem to make executable Servlet 3.0 applications.
Similar issues:
Embedded Tomcat, executable jar, ServletContext.getRealPath()
https://github.com/spring-projects/spring-boot/issues/4218
I solved my problem with the following configuration:
#Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
UndertowEmbeddedServletContainerFactory factory =
new UndertowEmbeddedServletContainerFactory();
factory.addDeploymentInfoCustomizers(new UndertowDeploymentInfoCustomizer() {
#Override
public void customize(DeploymentInfo deploymentInfo) {
deploymentInfo.setResourceManager(
new ClassPathResourceManager(deploymentInfo.getClassLoader(),
"META-INF/resources"));
});
return factory;
}
Generally, I think that it is underdocumented how embedded web container behaves. Spring Boot developers are happy if embdedded web container offers some subset of features to application, people who migrate existing application expects embedded container provides all features of regular container. For example ServletContainerInitializers are ignored in embedded: https://github.com/spring-projects/spring-boot/issues/321

Spring Boot + Jersey + view controller not working together [duplicate]

I am getting a HTTP 404 error when trying to serve index.html ( located under main/resources/static) from a spring boot app. However if I remove the Jersey based JAX-RS class from the project, then http://localhost:8080/index.html works fine.
The following is main class
#SpringBootApplication
public class BootWebApplication {
public static void main(String[] args) {
SpringApplication.run(BootWebApplication.class, args);
}
}
I am not sure if I am missing something here.
Thanks
The problem is the default setting of the Jersey servlet path, which defaults to /*. This hogs up all the requests, including request to the default servlet for static content. So the request is going to Jersey looking for the static content, and when it can't find the resource within the Jersey application, it will send out a 404.
You have a couple options around this:
Configure Jerse runtime as a filter (instead of as a servlet by default). See this post for how you can do that. Also with this option, you need to configure one of the ServletProperties to forward the 404s to the servlet container. You can use the property that configures Jersey to forward all request which results in a Jersey resource not being found, or the property that allows you to configure a regex pattern for requests to foward.
You can simply change the Jersey servlet pattern to something else other than the default. The easiest way to do that is to annotate your ResourceConfig subclass with #ApplicationPath("/root-path"). Or you can configure it in your application.properties - spring.jersey.applicationPath.

How to enable swagger in a jersey + spring-boot web application [duplicate]

I am getting a HTTP 404 error when trying to serve index.html ( located under main/resources/static) from a spring boot app. However if I remove the Jersey based JAX-RS class from the project, then http://localhost:8080/index.html works fine.
The following is main class
#SpringBootApplication
public class BootWebApplication {
public static void main(String[] args) {
SpringApplication.run(BootWebApplication.class, args);
}
}
I am not sure if I am missing something here.
Thanks
The problem is the default setting of the Jersey servlet path, which defaults to /*. This hogs up all the requests, including request to the default servlet for static content. So the request is going to Jersey looking for the static content, and when it can't find the resource within the Jersey application, it will send out a 404.
You have a couple options around this:
Configure Jerse runtime as a filter (instead of as a servlet by default). See this post for how you can do that. Also with this option, you need to configure one of the ServletProperties to forward the 404s to the servlet container. You can use the property that configures Jersey to forward all request which results in a Jersey resource not being found, or the property that allows you to configure a regex pattern for requests to foward.
You can simply change the Jersey servlet pattern to something else other than the default. The easiest way to do that is to annotate your ResourceConfig subclass with #ApplicationPath("/root-path"). Or you can configure it in your application.properties - spring.jersey.applicationPath.

Spring boot not serving static content when Jersey REST is included

I am getting a HTTP 404 error when trying to serve index.html ( located under main/resources/static) from a spring boot app. However if I remove the Jersey based JAX-RS class from the project, then http://localhost:8080/index.html works fine.
The following is main class
#SpringBootApplication
public class BootWebApplication {
public static void main(String[] args) {
SpringApplication.run(BootWebApplication.class, args);
}
}
I am not sure if I am missing something here.
Thanks
The problem is the default setting of the Jersey servlet path, which defaults to /*. This hogs up all the requests, including request to the default servlet for static content. So the request is going to Jersey looking for the static content, and when it can't find the resource within the Jersey application, it will send out a 404.
You have a couple options around this:
Configure Jerse runtime as a filter (instead of as a servlet by default). See this post for how you can do that. Also with this option, you need to configure one of the ServletProperties to forward the 404s to the servlet container. You can use the property that configures Jersey to forward all request which results in a Jersey resource not being found, or the property that allows you to configure a regex pattern for requests to foward.
You can simply change the Jersey servlet pattern to something else other than the default. The easiest way to do that is to annotate your ResourceConfig subclass with #ApplicationPath("/root-path"). Or you can configure it in your application.properties - spring.jersey.applicationPath.

WebApplicationInitializer being called repeatedly

I have a Spring 3.1 based application hosted under Tomcat 7.x (latest version). The application is configured exclusively with Java (no web.xml, no Spring XML configuration). All unit tests are passing, including ones using the Spring Java configuration (#ContextConfiguration).
The problem is that when the application is deployed, the WebApplicationInitializer implementation is being called multiple times. Repeated registrations of filters and listeners causes exceptions and the application never starts.
I was not expecting WebApplicationInitializer.onStartup() to be called repeatedly and would like to eliminate that behavior if possible. If anyone has suggestions on why this might be happening, and how to stop it, I'd really appreciate it.
Update I believe the problem is external to the initialization class itself, but here it is in case I am mistaken...
public class DeploymentDescriptor implements WebApplicationInitializer {
private static final Logger LOGGER = LoggerFactory.getLogger("org.ghc.web-app-initializer");
#Override
public void onStartup (ServletContext servletContext) throws ServletException {
// This is the programmatic way of declaring filters. This allows you to order
// Filters. The order of these security filters DOES MATTER!
FilterRegistration.Dynamic mockSecurityFilter = servletContext.addFilter ("mockSecurityFilter", "org.ghc.security.MockSecurityFilter");
mockSecurityFilter.addMappingForUrlPatterns (EnumSet.of (REQUEST), true, "/*");
FilterRegistration.Dynamic siteMinderSecurityFilter = servletContext.addFilter ("siteMinderSecurityFilter", "org.ghc.security.SiteMinderSecurityFilter");
siteMinderSecurityFilter.addMappingForUrlPatterns (EnumSet.of (REQUEST), true, "/*");
FilterRegistration.Dynamic userDetailsStoreFilter = servletContext.addFilter ("userDetailsStoreFilter", "org.ghc.security.UserDetailsStoreFilter");
userDetailsStoreFilter.addMappingForUrlPatterns (EnumSet.of (REQUEST), true, "/*");
// Static resource handling using "default" servlet
servletContext.getServletRegistration ("default").addMapping ("*.js", "*.css", "*.jpg", "*.gif", "*.png");
// Map jspf files to jsp servlet
servletContext.getServletRegistration ("jsp").addMapping ("*.jspf");
// Spin up the Spring 3.1 class that can scan a package tree for classes
// annotated with #Configuration. See org.ghc.spring3.ControllerConfiguration for
// this example.
final AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext ();
dispatcherContext.setServletContext (servletContext);
dispatcherContext.register(ScProviderDirectory.class);
dispatcherContext.refresh ();
// Spin up the Spring DispatcherServlet (just like before) passing the just built
// application context. Load it like the regular Servlet that it is!
final ServletRegistration.Dynamic servlet = servletContext.addServlet ("spring", new DispatcherServlet(dispatcherContext));
servlet.setLoadOnStartup (1);
servlet.addMapping ("/"); // Make sure this is NOT "/*"!
}
}
Update 2 This is just weird. The Tomcat logs appear to identify two instances of my DeploymentDescriptor class. I verified that there is only one instance of this class in my .war file though. I have no idea where the second (phantom) instance is coming from, but at least this explains why the class is being scanned twice...
logs/localhost.2012-10-09.log:INFO: Spring WebApplicationInitializers detected on classpath: [org.ghc.configuration.DeploymentDescriptor#3b29642c]
logs/localhost.2012-10-09.log:INFO: Spring WebApplicationInitializers detected on classpath: [org.ghc.configuration.DeploymentDescriptor#432c4c7a]
The problem here was a Maven Overlay dumping crap a Spring xml configuration file into my application. For whatever reason this caused the WebApplicationInitializer.onStartup() to be called twice. Probably an initialization for the application context, and for the servlet context. Killed off the overlay, application is initializing as expected.
I had the same problem. The issue was that I had multiple spring-web*.jar like Biju K. suggested (one as part of the war, the other in shared/tomcat library).
I had the same problem. Running mvn clean in the web app module's directory and then starting up Tomcat solved it for me.

Resources