users@glassfish.java.net

Re: Is there a portable way to create files from a servlet ?

From: <glassfish_at_javadesktop.org>
Date: Mon, 12 May 2008 10:56:00 PDT

There are a WHOLE BUNCH of issues with this, but most of them are surmountable.

The first issue, since you mentioned "portably" is that the Servlet spec guarantees you access to only a single writable file system, that being it's temp directory.

Of course, this directory being temporary means that anything written has an indeterminate lifetime, typically they don't survive server restarts. At a minimum there's simply no guarantee for when how long anything written to the temp directory will live.

The Servlet Spec gives you the API ability to get the "real path" of a resource through the ServletContext.getRealPath. This API gives you the actual file that represents the resource living on the file system. Once you know this, you can typically write files to the files system within the exploded WAR area and the server will 'see' them as actual URLs.

Of course, this can also return null, notably if your WAR is deployed to a server that doesn't explode it on deploy (this is something else that the Spec doesn't guarantee).

Mind, these are all corner cases. Most servers do, or can be readily configured to, explode the WAR on to the file system at deploy.

Mind, if you DO write to the explode WAR directory, you will most likely lose those files on a redeploy, as the servers tend to delete the exploded directory and create a new one for your new deploy.

You can mitigate this somewhat if you deploy an already exploded directory, rather than an actual WAR. In these scenarios, the server runs from the specified directory directly, and in the end you as the deployer get to manage that directory (including any new, dynamic content) yourself. Most servers honor this kind of configuration.

The only TRULY portable mechanism is to implement either a Filter or a Servlet that is designed to directly serve up your dynamic content. You can place the Servlet at any spot you like in the URL space within your application (like off of /dynamic/pdf), then you can store the files "where ever you want" (including in a database blob, or in a configured file system location). All your servlet needs to do is set the mime-type and stream the data. This is the only truly portable mechanic that you can use.

But, it's also a real pain because you need to effectively duplicate the capabilities of the "DefaultServlet" (as they call it in Glassfish). While on the surface, it's just a matter of streaming your content and setting a mime-type, that's only the most minimal details necessary to server a static resource. There's also header management, handling caching (like the If-Modified HTTP header), handling HTTP chunking, etc. By assuming the responsibility to server the resource, you also take on the responsibility for quite a bit of the HTTP spec. Which is silly, when you think about it, but there you are.

Now, Glassfish (and I believe Tomcat as well, though I don't recall the details) have server specific extensions that allow you to map an external directory within your application URL space. Effectively virtual directories. This does essentially what the custom Servlet would do. Once configured, you could write your files to this external directory and then the server will serve the resources up for you. However, this is not portable.

What I have done in the past, in order to get around the "rewriting stuff the server already does" problem of serving up my own static resource, is I've written filters that point to an external store, and then upon request, they copy from the external store in to the web apps deploy directory. The the filter forwards to the newly copied resource, and the server can serve it up however it wants.

Obviously I do things like check to make sure the local copy doesn't exist or is "older" (based on file date) than the one in the external directory, so as only having to copy it once.

This is portable (assuming an exploded WAR style deployment), and lets the server do the "hard work".

One nit I've found is that you can NOT copy the file to the actual URL you want it to be at (at least not in Glassfish).

For example, say the you want the file to be at /dynamic/pdf/report.pdf.

I found that with my filter, even though it intercepted the call to /dynamic/pdf/report.pdf, and created the report.pdf file "in time", the server already decided that the resource didn't exist (which it didn't) before calling my filter, and replied with a 404 error.

So, I map the filter to /dynamic/pdf, but place the files in /hidden/dynamic/pdf, and then copy files to THAT location and simply Forward to the hidden location within the filter. That seems to "fool" Glassfish enough to do the job.

I've stopped using this technique, and rely instead on the Glassfish external directory mapping scheme since portability isn't my primary concern, but my scheme does work, and seems to work well.
[Message sent by forum member 'whartung' (whartung)]

http://forums.java.net/jive/thread.jspa?messageID=273847