4

I'm trying to plug my set of RESTful endpoints (which rely on Jersey 2 JAX-RS implementation) into a 3rd party Spring MVC web application running on top of Tomcat 9.

I'm not allowed to modify web.xml, nor is my plug-in available when the container starts, so annotation-based configuration is not an option, either. This means I can't deploy Jersey's ServletContainer directly.

What I am allowed is plugging into Spring MVC, namely

  1. creating a custom partial Spring application context and
  2. implementing a custom Controller, so I have written this naïve implementation which is actually close to the standard ServletWrappingController:
package com.example import org.glassfish.jersey.server.ResourceConfig import org.glassfish.jersey.servlet.ServletContainer import org.springframework.web.servlet.ModelAndView import org.springframework.web.servlet.mvc.AbstractController import java.io.IOException import java.util.Collections import java.util.Enumeration import javax.servlet.ServletConfig import javax.servlet.ServletContext import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse class JerseyServletInitializer(servletContext: ServletContext, componentClasses: List<Class<*>>, components: List<Any>): AbstractController() { private val servletContainer: ServletContainer init { val resourceConfig = ResourceConfig() .apply { // Register org.glassfish.jersey.media.multipart.MultiPartFeature componentClasses.forEach { clazz -> register(clazz) } // Register REST endpoints components.forEach { component -> register(component) } } // Provide a dummy ServletConfig val servletConfig = object : ServletConfig { override fun getInitParameter(name: String): String? = null override fun getInitParameterNames(): Enumeration<String> = emptyList<String>().let { Collections.enumeration(it) } override fun getServletName(): String = ServletContainer::class.java.name override fun getServletContext(): ServletContext = servletContext } // Create and initialize Jersey's ServletContainer servletContainer = ServletContainer(resourceConfig).apply { init(servletConfig) } setSupportedMethods("GET", "POST", "PUT", "OPTIONS") } @Throws(IOException::class) override fun handleRequestInternal(request: HttpServletRequest, response: HttpServletResponse): ModelAndView? { // Delegate all HTTP requests to Jersey servletContainer.service(request, response) return null } } 

The above works fine with the only exception of file upload scenario (i. e. HTTP POST with Content-Type: multipart/form-data).

Spring detects such requests and converts a regular HttpServletRequest into a MultipartHttpServletRequest, reading out and exhausting its input stream (mark()/reset() are not supported). The web application has a custom MultipartResolver which I can't affect in any way:

 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/> 

This means Jersey receives a multipart HTTP POST with an empty request body, throws a MIMEParsingException and responds with HTTP 400 Bad Request.

As far as I understand, multipart resolution can only be disabled globally, and not on a per-controller basis (1, 2, 3).

Questions:

  1. What is the best way to work the MultipartResolver around, so that Jersey receives an non-modified request?
  2. Alternatively, can you recommend an API to re-construct the request body (i. e. do the opposite of what MultipartResolver does and feed the new HttpServletRequest to Jersey)? This can, of course, be done by hand, but I'd rather rely on the existing libraries.

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.