Tuesday, October 19, 2010

JSR 286 and crying developers

JSR 286 is probably a primary way of how any web-related stuff should be done. Definitely. However, it has some nasty limitations, that would lead novice to hit the wall, step on rake, step on rake again etc. One of such limitations (well, not really, but still) is to return a file. Normally, JSR286 wants you to return HTML. But what if you want to click on some button and get a PDF file or TIFF or DOC or MP3 and so on? Usually, developers are setting content type, putting some content disposition to define filename etc, put some headers to flush cache, write to the output stream, recompile and deploy.

Problem solved.
No, not really: once they call their portlet, entire application disappears and log is filled with devil's 666 lines of Java traceback from Hell. So, random desperate rants over forums begins usually with "me too!" or "pass me some code!" messages.
The real thing is: you need to read JSR286 properly. See, the JSR 168 does not allows us to use something else than just HTML. Pretty much silly. But JSR 286 allows. How? Easy: serve it as a resource. Hence the recipe:
  1. Implement/override method in GenericPortlet:
    serveResource(ResourceRequest request, ResourceResponse response)
  2. In your JSP (or whatever you use) refer to it with Resource URL. In JSP you do it like this (use it on your own way):
    renderResponse.createResourceURL().toString()
  3. In that "serveResource" thing check your request parameters and then return your content into an InputStream.
Keep in mind, that normally you're dealing with ResourceResponse, which is different than HTTP response in a servlet. So after you've put content type in usual way, "setHeader" method is actually not available. But you can always use "setProperty" instead.
Now, if you are smart enough and using Liferay :-) instead of something else, then you simply could use one-liner PortletResponseUtil class, wher you can find a bunch of "sendFile" or "write" methods. They are just for your convenience: get from byte array, InputStream etc. As an example how it works, watch:
PortletResponseUtil.sendFile(response,
"test.pdf",
new FileInputStream(new File("/tmp/test.pdf")),
"application/pdf");
Now use your own imagination how to make it actually right (by replacing FileInputStream with something more valuable, as a starter).
This is it. Enjoy. :-)

No comments: