Gili wrote:
> Craig McClanahan wrote:
>
>> Hmm ... maybe something analogous to the MediaType class (perhaps
>> HeaderValue) that gives you an accessor to get to the value itself
>> ("form-data" in this case), plus a Map<String,String> giving you access
>> to all the parameters (just one in the case shown here, but there could
>> be several for general header values)? We'd still want convenient
>> string-only ways to *construct* the header value, but it would be nice
>> to have something like this on BodyPart:
>>
>> MultivaluedMap<String,HeaderType> getHeaders() { ... }
>>
>> instead of the current return value of "MultivaluedMap<String,String>".
>> Then you could call:
>>
>> HeaderType header =
>> bodyPart.getHeaders().getFirst("Content-Disposition");
>> System.out.println("Disposition value is " + header.getValue()); //
>> "form-data"
>> System.out.println("Name is " + header.getParameters().get("name"));
>> // "files"
>>
>> What do you think?
>>
>>
>
> That sounds exactly like the kind of thing I was looking for.
>
>
Cool. I love it when great minds think alike :-).
> BTW, I just discovered this related discussion:
> http://forums.sun.com/thread.jspa?threadID=5333195
> This, in turn, led me to javax.mail.internet.ContentDisposition and more
> importantly javax.mail.internet.HeaderTokenizer. We should be able to use
> these fairly easily to implement the aforementioned APIs.
>
>
Ugh ... that (parsing MIME headers) is something else that I didn't have
to worry about when I was "constructively lazy" and depended on JavaMail
for parsing. We'd definitely need to build something on top of the
MultipartStream from Commons FileUpload, since that only gives you body
parts as a whole, and doesn't deal with the headers.
> Craig McClanahan wrote:
>
>> Dealing with the configuration seems solvable fairly easily, although
>> the interesting bit is to find a solution that works for both a servlet
>> deployment (where there could be multiple independent apps deployed in
>> the same server, so system properties don't work well) and a non-servlet
>> deployment (where we don't have any access to servlet APIs.
>>
>>
>
> Why do you need to support non-servlet deployments?
>
>
Two reasons:
* On the server side, JAX-RS explicitly declares support for non-servlet
deployments
(although lots of details are left as "exercise for the implementor"
in the 1.0 spec).
There is no a priori reason I can think of that we should restrict
multipart support to
only work for servlet based server side deployments.
* A lot of my use cases involve *client* applications using RESTful web
services.
Jersey has a very nice client API that can leverage much of the JAX-RS
infrastructure
on the client side as well, and it would be really weird to "import
javax.servlet.*" into
an applet, or a RIA app based on Swing or Java FX.
> Craig McClanahan wrote:
>
>> Probably the best bet is an optional properties file loaded via the thread
>> context class loader (if any; otherwise the class loader that loaded the
>> jersey-multipart classes) that can be used to set configuration stuff
>> like this.
>>
>>
>
> As soon as we start talking about class loaders things get ugly real quick.
> Ideally I'd like to cheat by simplifing the requirements if possible ;)
>
>
>
I know that pain :-), having been pretty heavily involved in the servlet
container inside Tomcat from version 4.x -- the basic architecture today
is pretty similar although quite refined. But let me state a
requirement I think we must satisfy in a different way: it must be
possible to deploy two or more different server apps, each using
jersey-multipart, in the same instance of a servlet container, with
different configuration settings for things like the threshold size
before a body part gets spooled to a disk file.
The algorithm I described above is quite typical of the way your average
web framework (including Struts) locates application specific
resources. Fortunately, we can make it work transparently in a
non-servlet world too, by using things like
ClassLoader.getResourceAsStream() instead of
ServletContext.getResourceAsStream().
> Craig McClanahan wrote:
>
>> Regarding the need for the cleanup call, I'm open to suggestion for how
>> to improve this. I've started looking at Jersey filters (which would
>> impose a Jersey-specific implementation dependency), but haven't settled
>> on anything yet.
>>
>>
>
> FileUpload has a background thread that monitors when its File instances get
> garbage-collected (Guice does something similar) and when this happens it
> goes and deletes the files off the hard-drive automatically. It also uses a
> Servlet Filter to hook servlet shutdown to force immediate deletion of all
> outstanding files. At least, that's what I understood from their Javadoc.
>
>
I'm not usually a fan of "background thread cleanup" type solutions
because they are (a) asynchronous, and therefore consume resources for
longer than they should be consumed, and (b) they encourage sloppy
coding -- "someone else will clean up my mess, so I don't have to worry
about it." It might not be quite so bad in this use case, although we'd
need to again deal separately with the servlet versus non-servlet case
(servlet based temp files should be cleaned up when the app is
undeployed, not just when the container is shut down), but that might be
feasible.
Unless performance considerations dictate otherwise, I'm much more into
synchronously cleaning up after myself after each request. But it's
definitely not optimal to make the app developer responsible for
ensuring that this happens.
> Craig McClanahan wrote:
>
>> Separately, I took your suggestion to look at Commons FileUpload. It
>> turns out that there *is* a single important class
>> (org.apache.commons.fileupload.MultipartStream) -- plus a couple of
>> small helpers -- that performs the only real task I'm currently
>> delegating to JavaMail. That's the actual parsing of the multipart/*
>> input stream. I need to do some experiments, and take heed of the
>> potential concerns in the javadocs about nested multipart/* body parts,
>> but it may well be that we could incorporate a variant of just this
>> class and not even need the entire Commons FileUpload package (or it's
>> dependence on Commons IO).
>>
>>
>
> The only reason I suggested looking at FileUpload is I thought it was an
> active project that it handles temporary files and configuration under the
> hood for us. Now I'm not so sure anymore. If you believe that adding their
> configuration on top of Javamail isn't much hassle I think I'd prefer that
> than relying on their code. I say this because I noticed that Javamail has
> been open-sourced and is now part of Glassfish and I trust those guys a heck
> of a lot more than I do your average Apache project.
>
>
I'm afraid I'm biased both ways so can't help you much in determining
who to trust more.
I work for Sun (originally in the J2EE (now Java EE) group where
Glassfish now comes from), and have also been heavily involved in many
Apache projects -- most particularly Tomcat and Struts, the latter being
where many of the Commons projects got a lot of initial developers and
initial codebases.
I trust them both :-).
> My main concern now is stability and simplicity. I guess I no longer care
> about the underlying JAR size ;)
>
>
+1 for stability and simplicity. But this is a case where the "reuse"
argument might tends towards fork-and-copy-a-few-classes (with
appropriate attributions, of course, to satisfy the relevant open source
licenses), rather than importing reasonably large packages when we're
only using a few classes from them.
On the other hand, the JavaMail based support for parsing multipart
messages "just works" ...
> Gili
>
Craig