users@jersey.java.net

Re: [Jersey] Re: Re: container request and multipart/form-data

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Tue, 04 May 2010 11:34:35 +0200

Hi Geoffrey,

I just added a buffering test and it passes (see below).

What version of Jersey are you using? i am guessing you are using a
fairly old version that utilized JavaMail.

Paul.

On May 4, 2010, at 8:58 AM, geoffrey hendrey wrote:

/*
  *
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
  * Copyright 1997-2010 Sun Microsystems, Inc. All rights reserved.
  *
  * The contents of this file are subject to the terms of either the GNU
  * General Public License Version 2 only ("GPL") or the Common
Development
  * and Distribution License("CDDL") (collectively, the "License"). You
  * may not use this file except in compliance with the License. You
can obtain
  * a copy of the License at https://jersey.dev.java.net/CDDL+GPL.html
  * or jersey/legal/LICENSE.txt. See the License for the specific
  * language governing permissions and limitations under the License.
  *
  * When distributing the software, include this License Header Notice
in each
  * file and include the License file at jersey/legal/LICENSE.txt.
  * Sun designates this particular file as subject to the "Classpath"
exception
  * as provided by Sun in the GPL Version 2 section of the License
file that
  * accompanied this code. If applicable, add the following below the
License
  * Header, with the fields enclosed by brackets [] replaced by your own
  * identifying information: "Portions Copyrighted [year]
  * [name of copyright owner]"
  *
  * Contributor(s):
  *
  * If you wish your version of this file to be governed by only the
CDDL or
  * only the GPL Version 2, indicate your decision by adding
"[Contributor]
  * elects to include this software in this distribution under the
[CDDL or GPL
  * Version 2] license." If you don't indicate a single choice of
license, a
  * recipient has the option to distribute your version of this file
under
  * either the CDDL, the GPL Version 2 or to extend the choice of
license to
  * its licensees as provided above. However, if you add GPL Version
2 code
  * and therefore, elected the GPL Version 2 license, then the option
applies
  * only if the new code is made subject to such option by the copyright
  * holder.
  */

package com.sun.jersey.multipart.impl;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.container.ContainerException;
import com.sun.jersey.api.core.DefaultResourceConfig;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.core.util.ReaderWriter;
import com.sun.jersey.multipart.FormDataMultiPart;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

/**
  *
  * @author Paul.Sandoz_at_Sun.Com
  */
public class FormDataMultiPartBufferTest extends
AbstractGrizzlyServerTester {

     public FormDataMultiPartBufferTest(String testName) {
         super(testName);
     }

     Client client = null;

     @Override
     protected void setUp() throws Exception {
         super.setUp();

         ClientConfig config = new DefaultClientConfig();
         config.getClasses().add(MultiPartBeanProvider.class);
         client = Client.create(config);
     }

     @Override
     public void tearDown() {
         super.tearDown();
         client = null;
     }

     public class MyFilter implements ContainerRequestFilter {

         @Override
         public ContainerRequest filter(ContainerRequest request) {
            // Buffer
             InputStream in = request.getEntityInputStream();
             if (in.getClass() != ByteArrayInputStream.class) {
                 // Buffer input
                 ByteArrayOutputStream baos = new
ByteArrayOutputStream();
                 try {
                     ReaderWriter.writeTo(in, baos);
                 } catch (IOException ex) {
                     throw new ContainerException(ex);
                 }
                 in = new ByteArrayInputStream(baos.toByteArray());
                 request.setEntityInputStream(in);
             }

             // Read entity
             FormDataMultiPart multiPart =
request.getEntity(FormDataMultiPart.class);

             assertEquals(3, multiPart.getBodyParts().size());
             assertNotNull(multiPart.getField("foo"));
             assertEquals("bar", multiPart.getField("foo").getValue());
             assertNotNull(multiPart.getField("baz"));
             assertEquals("bop", multiPart.getField("baz").getValue());

             assertNotNull(multiPart.getField("bean"));
             MultiPartBean bean =
multiPart.getField("bean").getValueAs(MultiPartBean.class);
             assertEquals("myname", bean.getName());
             assertEquals("myvalue", bean.getValue());

             // Reset buffer
             ByteArrayInputStream bais = (ByteArrayInputStream)in;
             bais.reset();

             request.getProperties().put("filtered", "true");
             return request;
         }

     }
     @Path("/")
     public class ConsumesFormDataResource {

         @Context HttpContext hc;

         @PUT
         @Consumes("multipart/form-data")
         @Produces("text/plain")
         public Response get(FormDataMultiPart multiPart) {
             Object p = hc.getProperties().get("filtered");
             assertNotNull(p);
             assertEquals("true", p);

             if (!(multiPart.getBodyParts().size() == 3)) {
                 return Response.ok("FAILED: Number of body parts is
" + multiPart.getBodyParts().size() + " instead of 3").build();
             }
             if (multiPart.getField("foo") == null) {
                 return Response.ok("FAILED: Missing field
'foo'").build();
             } else if
(!"bar".equals(multiPart.getField("foo").getValue())) {
                 return Response.ok("FAILED: Field 'foo' has value '"
+ multiPart.getField("foo").getValue() + "' instead of 'bar'").build();
             }
             if (multiPart.getField("baz") == null) {
                 return Response.ok("FAILED: Missing field
'baz'").build();
             } else if
(!"bop".equals(multiPart.getField("baz").getValue())) {
                 return Response.ok("FAILED: Field 'baz' has value '"
+ multiPart.getField("baz").getValue() + "' instead of 'bop'").build();
             }
             if (multiPart.getField("bean") == null) {
                 return Response.ok("FAILED: Missing field
'bean'").build();
             }
             MultiPartBean bean =
multiPart.getField("bean").getValueAs(MultiPartBean.class);
             if (!bean.getName().equals("myname")) {
                 return Response.ok("FAILED: Second part name = " +
bean.getName()).build();
             }
             if (!bean.getValue().equals("myvalue")) {
                 return Response.ok("FAILED: Second part value = " +
bean.getValue()).build();
             }
             return Response.ok("SUCCESS: All tests passed").build();
         }
     }

     public void testConsumesFormDataResource() {
         ResourceConfig rc = new
DefaultResourceConfig(ConsumesFormDataResource.class,
                 MultiPartBeanProvider.class);
         List l = rc.getContainerRequestFilters();
         l.add(MyFilter.class);
         startServer(rc);

         WebResource.Builder builder = client.resource(getUri()).
                 accept("text/plain").type("multipart/form-data");
         try {
             MultiPartBean bean = new MultiPartBean("myname",
"myvalue");
             FormDataMultiPart entity = new FormDataMultiPart().
                 field("foo", "bar").
                 field("baz", "bop").
                 field("bean", bean, new MediaType("x-application", "x-
format"));
             String response = builder.put(String.class, entity);
             if (!response.startsWith("SUCCESS:")) {
                 fail("Response is '" + response + "'");
             }
         } catch (UniformInterfaceException e) {
             report(e);
             fail("Caught exception: " + e);
         }
     }

     private void report(UniformInterfaceException e) {
         System.out.println("Got UniformInterfaceException: " +
e.getMessage());
         e.printStackTrace(System.out);
         ClientResponse r = e.getResponse();
         System.out.println("Response:");
         System.out.println(" Location=" + r.getLocation());
         System.out.println(" Status=" + r.getStatus());
         MultivaluedMap<String,String> metadata = r.getMetadata();
         for (Map.Entry<String,List<String>> entry :
metadata.entrySet()) {
             for (String value : entry.getValue()) {
                 System.out.println(" Header=" + entry.getKey() + ",
Value=" + value);
             }
         }
     }

}


> Hi Paul,
>
> I tried your suggestion. I am able to read the multipart/form-data
> fields from my filter. However, after my filter returns the
> response, I get this stack trace.
> Looks like somehow the request is getting borked, even though I
> buffered it as you suggested. Thoughts?
>
>
> javax.ws.rs.WebApplicationException: javax.mail.MessagingException:
> Missing start boundary
> at
> com
> .sun
> .jersey.multipart.impl.MultiPartReader.readFrom(MultiPartReader.java:
> 196)
> at
> com
> .sun
> .jersey.multipart.impl.MultiPartReader.readFrom(MultiPartReader.java:
> 74)
>
> at
> com
> .sun
> .jersey
> .spi.container.ContainerRequest.getEntity(ContainerRequest.java:393)
> at
> com
> .sun
> .jersey.server.impl.model.method.dispatch.EntityParamDispatchProvider
> $EntityInjectable.getValue(EntityParamDispatchProvider.java:139)
>
> at
> com
> .sun
> .jersey
> .server
> .impl
> .inject
> .InjectableValuesProvider
> .getInjectableValues(InjectableValuesProvider.java:43)
> at
> com
> .sun
> .jersey
> .server
> .impl.model.method.dispatch.AbstractResourceMethodDispatchProvider
> $
> EntityParamInInvoker
> .getParams(AbstractResourceMethodDispatchProvider.java:126)
>
> at
> com
> .sun
> .jersey
> .server
> .impl.model.method.dispatch.AbstractResourceMethodDispatchProvider
> $
> ResponseOutInvoker
> ._dispatch(AbstractResourceMethodDispatchProvider.java:173)
> at
> com
> .sun
> .jersey
> .server
> .impl
> .model
> .method
> .dispatch
> .ResourceJavaMethodDispatcher
> .dispatch(ResourceJavaMethodDispatcher.java:67)
>
> at
> com
> .sun
> .jersey
> .server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:166)
> at
> com
> .sun
> .jersey
> .server.impl.uri.rules.SubLocatorRule.accept(SubLocatorRule.java:108)
> at
> com
> .sun
> .jersey
> .server
> .impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:114)
>
> at
> com
> .sun
> .jersey
> .server.impl.uri.rules.SubLocatorRule.accept(SubLocatorRule.java:108)
> at
> com
> .sun
> .jersey
> .server
> .impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:114)
> at
> com
> .sun
> .jersey
> .server
> .impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:74)
>
> at
> com
> .sun
> .jersey
> .server
> .impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:114)
> at
> com
> .sun
> .jersey
> .server
> .impl
> .uri
> .rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:66)
> at
> com
> .sun
> .jersey
> .server
> .impl
> .application
> .WebApplicationImpl._handleRequest(WebApplicationImpl.java:709)
>
> at
> com
> .sun
> .jersey
> .server
> .impl
> .application
> .WebApplicationImpl.handleRequest(WebApplicationImpl.java:667)
> at
> com
> .sun
> .jersey
> .server
> .impl
> .application
> .WebApplicationImpl.handleRequest(WebApplicationImpl.java:658)
> at
> com
> .sun
> .jersey.spi.container.servlet.WebComponent.service(WebComponent.java:
> 318)
>
> at
> com
> .sun
> .jersey
> .spi
> .container.servlet.ServletContainer.service(ServletContainer.java:425)
> at
> com
> .sun
> .jersey
> .spi
> .container.servlet.ServletContainer.service(ServletContainer.java:604)
> at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
>
> at
> org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502)
> at
> org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:
> 365)
> at
> org
> .mortbay
> .jetty
> .security
> .ConstraintsSecurityHandler.handle(ConstraintsSecurityHandler.java:
> 220)
>
> at
> org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:
> 181)
> at
> org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:
> 822)
> at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:
> 305)
>
> at
> org
> .mortbay
> .jetty
> .handler
> .ContextHandlerCollection.handle(ContextHandlerCollection.java:229)
> at
> org
> .mortbay
> .jetty.handler.HandlerCollection.handle(HandlerCollection.java:113)
> at
> org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:
> 152)
>
> at org.mortbay.jetty.Server.handle(Server.java:324)
> at
> org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:
> 550)
> at org.mortbay.jetty.HttpConnection
> $RequestHandler.content(HttpConnection.java:890)
>
> at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:743)
> at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:215)
> at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:407)
> at
> org
> .mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:
> 421)
>
> at org.mortbay.thread.QueuedThreadPool
> $PoolThread.run(QueuedThreadPool.java:520)
> Caused by: javax.mail.MessagingException: Missing start boundary
> at javax.mail.internet.MimeMultipart.parsebm(MimeMultipart.java:799)
>
> at javax.mail.internet.MimeMultipart.parse(MimeMultipart.java:466)
> at javax.mail.internet.MimeMultipart.getCount(MimeMultipart.java:242)
> at
> com
> .sun
> .jersey.multipart.impl.MultiPartReader.readFrom(MultiPartReader.java:
> 163)
>
> ... 39 more
>
>
> --
> http://nextdb.net - RESTful Relational Database
> http://www.nextdb.net/wiki/en/REST