; last updated - 4 minutes read

A great PrimeFaces component refusing to work properly

These days my team had a hard time implementing a simple file download in a PrimeFaces portlet. There's a nifty PrimeFaces component to do just that: <p:filedownload>. But it won't work.

Update June 5, 2014:Neils Griffing reports to have fixed the bug (see the comments section below). So the rest of this article may be outdated (if you use a current version of Liferay and the LiferayFaces bridge, that is).

Instead of showing our PDF file, it simply tells us not to set an illegal content type. We checked the MIME type settings both of Liferay and our portlet, added "application/pdf" to the portlet.xml file, but it didn't help.

We were puzzled until we looked at the source code of com.liferay.portlet.PortletRequestImpl, were the list of legal content type is defined:

public String getResponseContentType() { if (_wapTheme) { return ContentTypes.XHTML_MP; } else { return ContentTypes.TEXT_HTML; } } public Enumeration getResponseContentTypes() { List responseContentTypes = new ArrayList(); responseContentTypes.add(getResponseContentType()); return Collections.enumeration(responseContentTypes); }

At this moment the scales fell from our eyes. First of all we noticed no matter what we do Liferay won't acceppt any MIME type other than HTML.

It's a portlet, dude!

Actually, that's not a bug at all. It's a feature. Portal servers generate HTML pages by composing little HTML fragments. So portlets are expected to deliver HTML. Liferay includes the HTML fragment into the bigger portal HTML code. Maybe it even modifies the portlet HTML code.

If the portlet delivers something else than HTML portal servers don't know what to do. At first glance this sounds ridiculous. It can't be so difficult to deal with a PDF oder a video? Just embed it with some glue code into the portal page!

The catch is there are way to many file formats to deal with. It might be simple to deal with the most popular file format, but you can't possibly hope to be able to deal with each and every possible file.

Even worse, our idea was not to embed a PDF file, but to download it. That's fine with traditional web applications, but it breaks the idea of a portal server. Is it ok to show a blank page and a download dialog just because one of your - say - five portlets on the page offers a file download? And what happens if two portlets want to push a file to the client in the same request?

And that's that. You simply can't change the MIME type of your portlet.

And what about our file download?

There are other ways to do the file download. First of all you'll want to open the file in a new browser window to avoid the portal page to "blank out". PrimeFaces allows you to call the corresponding Javascript code from the server side:

RequestContext context = RequestContext.getCurrentInstance(); context.execute("window.open(url,'_blank');");

Please note this code works only in AJAX mode.

The most simple (yet hardly recommendable) way to make a file available for download is to write it to a file on the server and to return an URL pointing to the file. There's a little catch with this approach, too: your portlet runs in a different context than the portal page, so you have to find out the proper URL first.

The preferred way is to register a resource handle in the faces-config.xml file:

your.pkg.CustomerResourceHandler

Implementing the resource handler is straight forward following Neil Griffin's PDF export demo on Github. There's also a discussion in the PrimeFaces forum on the topic.

Bad news

If you're using Liferay Faces 3.1.x as a JSF bridge to Liferay 6.1, I have bad news for you: Neil Griffin's demo doesn't work. You don't notice at first glance. The PDF file shows nicely. But no other resources can be loaded. PrimeFaces portlets look a little weird with the CSS and Javascript files missing.

Probably the bug is going to be fixed soon, but still: Best you download Neil Griffin's demo, deploy it to your Liferay installation and verify both the CSS files and the PDF file are there. If they aren't, you'll probably have to resort to the ugly local file solution.

Other ideas

Maybe I'm going to write an AngularFaces component to embed PDF files, images and videos in a portlet page.

By the way, if you've got a different solution, I'd like to hear from you. Just drop a comment below.

Further reading

Neil Griffin's PDF export demo on Github

a solution described in the PrimeFaces forum


Comments