user3216983
user3216983

Reputation: 131

How to prevent HTTP Rest Jboss resteasy Fileupload from failing with 'Could not find message body reader' error?

I have a requirement to write a REST service to support a file upload; I'm using JBOSS / Resteasy / Jersey, as follows:

Server HTTP REST handler:

@Path("document")
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public Response uploadFile(@FormDataParam("file") InputStream fileInputStream,
                           @FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
                           @FormDataParam("selectedName") String selectedName,
                           @FormDataParam("name") String name,
                           @FormDataParam("notes") String notes) {
   String documentId;
   // upload the file
   String filePath = SERVER_UPLOAD_LOCATION_FOLDER + contentDispositionHeader.getFileName();
   try { fileUpload(fileInputStream, filePath); 
   }
   catch (IOException e) { throw new ApplicationRuntimeException( "Unable to upload file: " + selectedName); 
   }
   // create the document record
   documentId = create(selectedName, name, notes);
   return Response.ok("123456789").build();
}

Unit Test

In order to test the services I wrote the following unit test:

@Test
public void testCreateDocument() throws Exception {
    Assert.assertNotNull(file);  
    Assert.assertTrue(file.canRead());
    given()
    .multiPart("notes", "my notes") .multiPart("selectedName", "selectedName")    
    .multiPart("name", "test.txt") .multiPart("file",file) 
    .contentType(MediaType.APPLICATION_OCTET_STREAM) .expect().body(equalTo("This is an uploaded test file."))
    .when().post(DOCUMENT_URL);
}

The unit test fails. Following are the error message received by the unit test as well as the server logs

Unit Test Error Message

The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.

In addition, the following server logs are recorded when issuing the unit test above:

09:26:40,340 WARN  [org.jboss.resteasy.core.ExceptionHandler] (http-/127.0.0.1:8080-7) failed to execute: javax.ws.rs.NotSupportedException: Could not find message body reader for type: class com.sun.jersey.core.header.FormDataContentDisposition of content type: multipart/form-data;boundary=J6UnCyDNsA50mzrPqDb2ctHPBb6fEpFJRF
    at org.jboss.resteasy.core.interception.ServerReaderInterceptorContext.throwReaderNotFound(ServerReaderInterceptorContext.java:52) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:73) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:50) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.aroundReadFrom(GZIPDecodingInterceptor.java:59) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:53) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:150) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:89) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:112) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:288) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:242) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:229) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:356) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:179) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:220) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56) [resteasy-jaxrs-3.0.7.Final.jar:]
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51) [resteasy-jaxrs-3.0.7.Final.jar:]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) [jboss-servlet-api_3.0_spec-1.0.2.Final-redhat-1.jar:1.0.2.Final-redhat-1]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:295) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:149) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:169) [jboss-as-web-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:145) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:97) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:102) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:336) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:653) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:920) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at java.lang.Thread.run(Thread.java:744) [rt.jar:1.7.0_45]

I searched all over for references to the Could not find message body reader for type: class com.sun.jersey.core.header.FormDataContentDisposition of content type: multipart/form-data error but have not been able to find anything helpfull.

Upvotes: 3

Views: 6802

Answers (2)

I had the same issue and to fix it I have used the solution described in volkerbenders repository. Here is the solution that have worked for me:

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxrs</artifactId>
    <version>3.0.11.Final</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-multipart-provider</artifactId>
    <version>3.0.11.Final</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxb-provider</artifactId>
    <version>3.0.11.Final</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>

And this is the resource class:

import org.apache.commons.io.IOUtils;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

@Path("/file")
public class FileResource {

    public static final String UPLOADED_FILE_PARAMETER_NAME = "file";

    @Path("/upload")
    @POST
    @Consumes("multipart/form-data")
    public Response uploadFile(MultipartFormDataInput input) {

        Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
        List<InputPart> inputParts = uploadForm.get(UPLOADED_FILE_PARAMETER_NAME);

        for (InputPart inputPart : inputParts) {
            MultivaluedMap<String, String> headers = inputPart.getHeaders();
            try {
                InputStream inputStream = inputPart.getBody(InputStream.class, null);
                byte[] bytes = IOUtils.toByteArray(inputStream);
                String filename = getFileName(headers);
                //TODO: HERE you do whatever you want to do with the file
                //...
            } catch (IOException e) {
                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
            }
        }
        return Response.status(Response.Status.OK).build();
    }

    private String getFileName(MultivaluedMap<String, String> headers) {
        String[] contentDisposition = headers.getFirst("Content-Disposition").split(";");

        for (String filename : contentDisposition) {
            if ((filename.trim().startsWith("filename"))) {

                String[] name = filename.split("=");

                String finalFileName = sanitizeFilename(name[1]);
                return finalFileName;
            }
        }
        return "unknown";
    }

    private String sanitizeFilename(String s) {
        return s.trim().replaceAll("\"", "");
    }

}

Upvotes: 3

Vineet Reynolds
Vineet Reynolds

Reputation: 76709

Jersey is unnecessary in this context to support file uploads via multi-part POST. RESTEasy supports multi-part POST request processing in a different way.

Using Jersey is also the reason why you're seeing the warning in the first place - I suspect some classes (especially the FormDataContentDisposition class from Jersey and it's related classes are not accessible to the RESTEasy module in the JBoss application server. Just don't complicate things by adding in two JAX-RS providers without understanding the JBoss module system; this is pointless.

Upvotes: 1

Related Questions