Giter Club home page Giter Club logo

jersey-webmvc's Introduction

Workaround Spring Boot unability to make Jersey and Spring MVC co-exist

In the current code if the Jersey component is active (with the jersey profile) then all Spring Web MVC endpoint are not available anymore, if both are using the same root /. Run tests in com.github.bric3.jerseywebmvc.JerseyWebmvcApplicationTests.

without jersey profile
  1. GET /jaxrs404 Not Found but Jersey is not active

  2. GET /actuator/status200 OK

  3. GET /favicon.ico200 OK

  4. GET /doc/200 OK

  5. GET /doc/index.html200 OK

  6. GET /rest/200 OK

with jersey profile
  1. GET /jaxrs200 OK

  2. GET /actuator/status404 Not Found

  3. GET /favicon.ico404 Not Found

  4. GET /doc/404 Not Found

  5. GET /doc/index.html404 Not Found

  6. GET /rest/404 Not Found

While having a two technology to expose endpoints may seem brittle, this is quite useful if :

  • One want to expose a few static resources like documentation alongside JAXRS / Jersey endpoints.

  • One want to have actuator endpoints available from the root path.

  • One want to migrate one endpoint at a time from - to Spring WebMVC.

The following workaround covers part that Spring Boot does not.

Workarounds

Servlet filter mitigation

Using the filter from this issue. And configuring this filter with the right prefixes work.

with jersey and filter-mitigation profiles
  1. GET /jaxrs200 OK

  2. GET /actuator/status200 OK

  3. GET /favicon.ico200 OK

  4. GET /doc/404 Not Found it doesn’t work because when Spring MVC forwards the request it aks the container, however the request is forwarded to the Jersey servlet.

  5. GET /doc/index.html200 OK

  6. GET /rest/200 OK

The idea is to have a filter that can forward request to the correct servlet. Using the standard JEE servletContext it is possible to ask for a RequestDispatcher based the servlet name that should receive the request. For that this code needs the dispatcherServlet registration name which happens to be a public constant of org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME.

Then if the request URI path starts with the configured prefixes the request will be forwarded to the Spring dispatcherServlet. By doing so this bypasses any subsequent servlet filters.

However some Spring WebMVC mechanisms don’t work, like forward because they ask the container a RequestDispatcher based on the request, the JEE container will return a dispatcher based on url-patterns, and Jersey servlet is configured with /* so it receives all request by default.

  • [plus circle] Quite simple to configure

  • [plus circle] Quite simple to maintain

  • [plus circle] Barely rely on Spring MVC

  • [minus circle] May prevents some servlet filter to execute

  • [minus circle] Does not support all Spring Web MVC features like forwarding

See SpringMvcPrefixEnforcerFilter mitigation code.

Spring Web MVC configuration hack mitigation

Is it possible to configure Spring Boot to do the right thing ? Turns out, yes it’s possible.

with jersey and filter-mitigation profiles
  1. GET /jaxrs200 OK

  2. GET /actuator/status200 OK

  3. GET /favicon.ico200 OK

  4. GET /doc/200 OK

  5. GET /doc/index.html200 OK

  6. GET /rest/200 OK

So the Spring’s DispatcherServlet servlet url-mapping is hardwired by Spring Boot to spring.mvc.servlet.path, which in our case is /. And the jersey servlet url-mapping is /* which means that the Jersey servlet is configured to receive all request.

The idea is that JEE servlets can have multiple url-mappings, which means it is should be possible to tell the container to forward HTTP request that follow the configured url-mappings patterns to the DispatcherServlet.

The auto-configuration is located in DispatcherServletAutoConfiguration, and more precisely spring boot allows to override some of the beans and especially the dispatcherServlet dedicated ServletRegistrationBean (which is the Spring way to tell the container to register the given servlet). However it happens that Spring Boot have a specialized version of the ServletRegistrationBean named org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean who implements org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath. Spring Boot uses the bean with this interface to (auto) configure certain part of their web framework.

Extending DispatcherServletRegistrationBean to configure url-mappings is not gonna work, because setUrlMappings and addUrlMappings throws unsupported operation. The only options are

  • Either to implements another specialized ServletRegistrationBean that allows to configure url-mappings for the dispatcherServlet and of course implements DispatcherServletPath.

  • Or register two beans, the ServletRegistrationBean and the DispatcherServletPath.

However doing so is not enough, the Spring Web MVC infrastructure needs to be told how to resolve URIs, this should work with WebMvcConfigurationSupport and the companion bean WebMvcConfigurer.configurePathMatch. The PathMatchConfigurer is supposed to tell if a request URI path matches a WebMVC resource via another essential sub-component, the UrlPathHelper.

The method of interest is UrlPathHelper.getLookupPathForRequest is by default (alwaysUseFullPath is false) configured to look for sub-path of the url-mappings (UrlPathHelper.getPathWithinServletMapping), and as such the returned lookup path is stripped form the first part, hence Spring Web MVC cannot match any of these resources against incomplete URLs.

We need to configure this UrlPathHelper to return the full path via WebMvcConfigurer.configurePathMatch.

Unfortunately this configurer only affects the configuration of (the reactive) RequestMappingHandlerMapping and a few other types, but Spring Web MVC has many other HandlerMapping types. To workaround this, the AbstractHandlerMapping are post-processed to set the UrlPathHelper with the needed configuration.

And finally this was again not enough, for some mapping like WebMvcEndpointHandlerMapping because this parent’s type uses a private static final configuration non customizable RequestMappingInfo.BuilderConfiguration builderConfig with defaults helpers only, its urlPathHelper is null, this triggers the creation of a`PatternsRequestCondition` with a new instance of UrlPathHelper that has the default configuration. In order to bypass this behavior it is necessary to use reflection before WebMvcEndpointHandlerMapping bean post initialization and set the RequestMappingInfo.BuilderConfiguration with the UrlPathHelper with the needed configuration.

  • [plus circle] Seems to properly configure Spring MVC

  • [plus circle] And as such more robust

  • [plus circle] Integrates well with Servlets

  • [minus circle] Difficult to understand

  • [minus circle] Difficult to maintain

  • [minus circle] May break upon Spring MVC code changes

See SpringWebMvcHackConfiguration mitigation code.

jersey-webmvc's People

Contributors

bric3 avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.