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

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.

Related

How to serve single-page app in Spring Boot?

I'm trying to do something very simple in Spring Boot:
Serve my api calls from /api
Serve a single-page app located in src/main/resources/static
Shouldn't be that hard, but this question has been asked a dozen times in different ways, and there doesn't seem to be an answer. It's very easy to do in Dropwizard, or when you wire up Jersey and Jetty together directly.
The best answer so far is here:
#Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/{spring:\\w+}")
.setViewName("forward:/");
registry.addViewController("/**/{spring:\\w+}")
.setViewName("forward:/");
registry.addViewController("/{spring:\\w+}/**{spring:?!(\\.js|\\.css)$}")
.setViewName("forward:/");
}
}
The problem with this is that calls to /api/bad_missing_path get routed to the SPA, and not to an error message that says this is a bad api call. The other problem with it is that I just don't understand it. Yet another problem is it explicitly mentions .js and .css file types, and I'm going to have many more types than that. I want everything that starts with /api to be treated as an api call, and everything that doesn't start with /api to get passed through to the /static directory.
I have also tried to mount the DefaultServlet on "/*" but it didn't catch anything, either using the default Tomcat server or when I switched to Jetty.
How do I get this to work?

How do I set a context path with embedded jetty?

I'm banging my head against the wall on this. I wrote a tiny embedded jetty server (jetty 9.4.18, jersey 1.19.4) and I cannot for the life of me get it to respect the context path and my REST services deployed.
I tried setContextPath, but that never worked on ServletContextHandler, so I went with WebAppContext.
This is about as simple as I can get it:
Server jettyServer = new Server(9999);
// set up the web app
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/app/");
webapp.setResourceBase("web");
jettyServer.setHandler(webapp);
// add REST service
ServletHolder jerseyServlet = webapp.addServlet(ServletContainer.class, "/service/*");
final ResourceConfig resourceConfig = new ResourceConfig(RestService.class);
resourceConfig.register(MultiPartFeature.class);
jerseyServlet.setInitParameter("jersey.config.server.provider.packages", "org.futureboy.app.server.rest");
try {
jettyServer.start();
jettyServer.join();
} catch (Exception e) {
e.printStackTrace();
//jettyServer.stop();
jettyServer.destroy();
}
The static HTML files are served up in the web directory, and this works fine if I keep this line as follows:
ServletHolder jerseyServlet = webapp.addServlet(ServletContainer.class, "/service/*");
However this means the REST service (which operates under /app/service) does not work. If I swap this line out:
ServletHolder jerseyServlet = webapp.addServlet(ServletContainer.class, "/*");
Then the REST service works fine, but now the static HTML content does NOT work fine. So I either get a working static HTML service on /app/ or a working REST service underneath /app/service, but I can't have both. What am I doing wrong? The RestService.java file starts like:
#Path("/service")
public class RestService {
Any help would be appreciated, for I feel I am stuck on the one-yard line here.
Why do you want Jersey to serve static content?
That's not the role of a JAX-B server.
What do do ...
Switch from WebAppContext to ServletContextHandler (you don't need the overhead of WebAppContext or any of the bytecode scanning or annotation scanning deployment techniques that a full blown WebAppContext brings to the table.
Prevent Jersey from serving static content.
Setup a proper ResourceBase - use a fully qualified path or URI (no relative paths, no partial URIs)
Add a DefaultServlet on the default url-pattern "/" named "default".
Which these changes Jetty will serve static content, from your Resource Base.
Some prior answers/examples of the Jetty side configuration and DefaultServlet usage:
Deploying Jetty server via .jar. Why can't I access the index?
How to get embedded Jetty serving html files from a Jar, not a War
Serving static files from alternate path in embedded Jetty
What is difference between ServletContextHandler.setResourceBase and ResourceHandler.setResourceBase when using Jetty embedded container?
Jetty: default servlet context path
Display static HTML file from resources folder in Jetty Serverlet doGet method
And there's many many answers on how to configure Jersey to not serve static content.
One my favs is the solution provided at ...
Servlet for serving static content

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.

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

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.

Resources