users@jersey.java.net

Re: [Jersey] MIMEParsingException (IOException: Stream closed) when accessing a multipart form from a filter

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Wed, 15 Jul 2009 09:59:08 +0200

On Jul 15, 2009, at 1:02 AM, Igor Minar wrote:

> Hi Paul,
>
> Thanks for the answer, but I'm afraid that due to the expected size
> of the requests handled in this way, I don't think that buffering
> things in the memory will be feasible.
>
> If I read the jersey multipart docs/emails on the list correctly
> then the multipart implementation buffers large requests on the
> disk, which is something that I'd like to take advantage of.
>

That is the problem, the filter may not necessarily process the same
Java type as required by the resource method.

Are you using a resource filter and specifically associating it with
the resource method or a container request filter that would apply to
all resource methods?


> In the meantime I did implement a workaround where I pass the
> FormDataMultiPart instance from filter to my resource via a thread
> local class, but I still would like to see a nicer solution to this
> problem.
>

Yes, this of course does not work for @FormDataParam though.


>> In general when using Jersey filters one should not assume that the
>> Java type the filter wants for an entity is the same as the Java
>> type for the resource method. But there is probably some way i
>> could optimize this, for the case of when it is. Could you log an
>> enhancement issue?
>
> You mean that you would cache the entity within the provider, so
> that when it is requested the second time provider just retrieves
> the entity from the cache? Or are you thinking of something else?
>

Cache it within the ContainerRequest. But this will not work if one
requests a different Java type for the entity the second time.

The right way to do this would i think be for the filter to extend
ContainerRequest with such functionality by overriding the getEntity
method. Then the filter can adapt the ContainerRequest passed in and
return the adapted instance. Unfortunately the ContainerRequest class
is currently set up to easily do this so we need to modify this class
so it can adapted (likewise for ContainerResponse). Could you log an
issue?


> As an alternative to thread local hack, I also looked for an api
> similar to servlet's request.setAttribute but I didn't find anything
> like that in jersey. Am I just not looking hard enough or there
> really isn't anything like that in Jersey.
>

See:

https://jersey.dev.java.net/nonav/apidocs/1.1.0-ea/jersey/com/sun/jersey/api/core/HttpContext.html
#getProperties()
See:

and you can inject HttpContext e.g. @Context HttpContext hc.

Note that the same properties are accessible that ContainerRequest as
well:

   https://jersey.dev.java.net/nonav/apidocs/1.1.0-ea/jersey/com/sun/jersey/spi/container/ContainerRequest.html
#getProperties()

So in your filter you do not need to inject HttpContext.

Paul.

> thanks,
> Igor
>
>
> On Jul 13, 2009, at 12:11 AM, Paul Sandoz wrote:
>
>> H Igor,
>>
>> Your filter is consuming the request entity, and unless you buffer
>> the entity it can only be consumed once.
>>
>> For now you will have to get the entity stream read it into a byte
>> array input stream, set that as the stream, read the entity in the
>> filter, then, most importantly, reset the byte array input stream
>> so the entity can be re-read.
>>
>> In general when using Jersey filters one should not assume that the
>> Java type the filter wants for an entity is the same as the Java
>> type for the resource method. But there is probably some way i
>> could optimize this, for the case of when it is. Could you log an
>> enhancement issue?
>>
>> Thanks,
>> Paul.
>>
>> On Jul 12, 2009, at 3:09 AM, Igor Minar wrote:
>>
>>> Hi there,
>>>
>>> I have a application that needs to process a multipart-form
>>> request. Everything works great, until I try to access my form
>>> fields both in a jersey filter as well as in my resource.
>>>
>>> In my filter I have:
>>>
>>> private Credentials attemptBrowserBasedToken(HttpRequestContext
>>> context) {
>>> FormDataMultiPart form =
>>> context.getEntity(FormDataMultiPart.class);
>>> String key = form.getField("AccessKey").getValue(); //
>>> TODO NPEs!!!
>>> String signature =
>>> Base64Coder.decode(form.getField("signature").getValue());
>>> ...
>>> }
>>>
>>> In my resource I have:
>>>
>>> @POST
>>> @Consumes(MediaType.MULTIPART_FORM_DATA)
>>> public Response processForm(@PathParam("bucketName") final String
>>> bucketName,
>>> @FormDataParam("policy") final
>>> String policyEncoded,
>>> @FormDataParam("key") String key,
>>>
>>> @FormDataParam("success_action_redirect") final String
>>> successRedirect,
>>> @FormDataParam("acl") final
>>> String acl,
>>> final FormDataMultiPart formData,
>>> @Context final SecurityContext
>>> securityContext,
>>> @Context final HttpHeaders
>>> headers) {
>>> ...
>>> }
>>>
>>> When I run a request against this resource method, I get the
>>> exception below some time after the filter returns and before
>>> processForm in the resource is called.
>>>
>>> It looks like when the filter reads the form, it doesn't cache the
>>> result, but closes the request stream when it reads it. As a
>>> result, when jersey tries to initialize resource method arguments,
>>> it can't do it any more.
>>>
>>> If I remove all FromData* stuff from the resource method
>>> signature, the exceptions is not thrown.
>>>
>>> Is this a known limitation or a result of a design decision?
>>>
>>> Do you know if there is some workaround for this? Maybe a way to
>>> store the data retrieved in the filter somewhere in the request
>>> context and then retrieve it somehow in the resource? I suppose I
>>> could do this via a thread local hack, but I'd rather avoid that
>>> if possible.
>>>
>>> thanks,
>>> Igor
>>>
>>>
>>>
>>>
>>> Jul 11, 2009 4:19:55 PM
>>> com.sun.jersey.server.impl.application.WebApplicationImpl
>>> onException
>>> SEVERE: Internal server error
>>> javax.ws.rs.WebApplicationException:
>>> org.jvnet.mimepull.MIMEParsingException: java.io.IOException:
>>> Stream closed
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .multipart.impl.MultiPartReader.readFrom(MultiPartReader.java:203)
>>> 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
>>> .spi.container.ContainerRequest.getEntity(ContainerRequest.java:402)
>>> at
>>> com.sun.jersey.multipart.impl.FormDataMultiPartDispatchProvider
>>> $
>>> FormDataInjectableValuesProvider
>>> .getInjectableValues(FormDataMultiPartDispatchProvider.java:112)
>>> 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:
>>> 163)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .server
>>> .impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:71)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .server
>>> .impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:111)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .server
>>> .impl
>>> .uri
>>> .rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:
>>> 63)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .server
>>> .impl
>>> .application
>>> .WebApplicationImpl._handleRequest(WebApplicationImpl.java:654)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .server
>>> .impl
>>> .application
>>> .WebApplicationImpl.handleRequest(WebApplicationImpl.java:612)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .server
>>> .impl
>>> .application
>>> .WebApplicationImpl.handleRequest(WebApplicationImpl.java:603)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .spi.container.servlet.WebComponent.service(WebComponent.java:309)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .spi
>>> .container.servlet.ServletContainer.service(ServletContainer.java:
>>> 425)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .spi
>>> .container.servlet.ServletContainer.service(ServletContainer.java:
>>> 590)
>>> at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
>>> at
>>> com
>>> .google
>>> .inject.servlet.ServletDefinition.doService(ServletDefinition.java:
>>> 216)
>>> at
>>> com
>>> .google
>>> .inject.servlet.ServletDefinition.service(ServletDefinition.java:
>>> 141)
>>> at
>>> com
>>> .google
>>> .inject
>>> .servlet
>>> .ManagedServletPipeline.service(ManagedServletPipeline.java:93)
>>> at
>>> com
>>> .google
>>> .inject
>>> .servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:
>>> 63)
>>> at
>>> com
>>> .google
>>> .inject
>>> .servlet.ManagedFilterPipeline.dispatch(ManagedFilterPipeline.java:
>>> 122)
>>> at
>>> com.google.inject.servlet.GuiceFilter.doFilter(GuiceFilter.java:110)
>>> at
>>> com
>>> .sun
>>> .grizzly
>>> .http.servlet.FilterChainImpl.doFilter(FilterChainImpl.java:172)
>>> at
>>> com
>>> .sun
>>> .grizzly
>>> .http
>>> .servlet.FilterChainImpl.invokeFilterChain(FilterChainImpl.java:137)
>>> at
>>> com
>>> .sun
>>> .grizzly.http.servlet.ServletAdapter.service(ServletAdapter.java:
>>> 322)
>>> at
>>> com
>>> .sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:
>>> 165)
>>> at
>>> com
>>> .sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:
>>> 746)
>>> at
>>> com
>>> .igorminar
>>> .grizzlysendfile.SendfileFilter.doFilter(SendfileFilter.java:128)
>>> at
>>> com
>>> .sun
>>> .grizzly
>>> .arp.DefaultAsyncExecutor.invokeFilters(DefaultAsyncExecutor.java:
>>> 147)
>>> at
>>> com
>>> .sun
>>> .grizzly
>>> .arp.DefaultAsyncExecutor.interrupt(DefaultAsyncExecutor.java:126)
>>> at
>>> com
>>> .sun.grizzly.arp.AsyncProcessorTask.doTask(AsyncProcessorTask.java:
>>> 88)
>>> at com.sun.grizzly.http.TaskBase.run(TaskBase.java:189)
>>> at java.util.concurrent.ThreadPoolExecutor
>>> $Worker.runTask(ThreadPoolExecutor.java:886)
>>> at java.util.concurrent.ThreadPoolExecutor
>>> $Worker.run(ThreadPoolExecutor.java:908)
>>> at java.lang.Thread.run(Thread.java:637)
>>> Caused by: org.jvnet.mimepull.MIMEParsingException:
>>> java.io.IOException: Stream closed
>>> at org.jvnet.mimepull.MIMEParser.fillBuf(MIMEParser.java:436)
>>> at org.jvnet.mimepull.MIMEParser.skipPreamble(MIMEParser.java:
>>> 302)
>>> at org.jvnet.mimepull.MIMEParser.access$300(MIMEParser.java:62)
>>> at org.jvnet.mimepull.MIMEParser
>>> $MIMEEventIterator.next(MIMEParser.java:138)
>>> at org.jvnet.mimepull.MIMEParser
>>> $MIMEEventIterator.next(MIMEParser.java:123)
>>> at
>>> org.jvnet.mimepull.MIMEMessage.makeProgress(MIMEMessage.java:193)
>>> at org.jvnet.mimepull.MIMEMessage.parseAll(MIMEMessage.java:
>>> 176)
>>> at
>>> org.jvnet.mimepull.MIMEMessage.getAttachments(MIMEMessage.java:101)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .multipart.impl.MultiPartReader.readFrom(MultiPartReader.java:166)
>>> ... 37 more
>>> Caused by: java.io.IOException: Stream closed
>>> at
>>> com
>>> .sun
>>> .grizzly
>>> .tcp.http11.GrizzlyInputBuffer.read(GrizzlyInputBuffer.java:343)
>>> at
>>> com
>>> .sun
>>> .grizzly
>>> .tcp.http11.GrizzlyInputStream.read(GrizzlyInputStream.java:234)
>>> at
>>> com
>>> .sun
>>> .grizzly
>>> .http
>>> .servlet.ServletInputStreamImpl.read(ServletInputStreamImpl.java:91)
>>> at org.jvnet.mimepull.MIMEParser.fillBuf(MIMEParser.java:434)
>>> ... 45 more
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>