Inserting a file in a JSF view with a custom component

By Stephane Carrez 1 comments

My problem was to give some flexibility in a JSF page to customize some blocks so that a marketing team could easily change those blocks. Basically the marketing team needs to insert some promotion, advertising or specific presentation blocks. For this, they ask a designer to make those blocks. The designer will give us some piece of HTML (with Javascript) that must be inserted in the page.

Putting all this in a database is of course possible but it means that some administration interface is necessary when we change the blocks. Since the blocks are not intended to be changed too often, I prefered to have some simple solution and use plain text files for the storage.

The goal is now to make a JSF component that reads the file and insert it in the output page.

Step1: Write the UI Component class

The JSF component must inherit from UIComponentBase class. The class must implement the getFamily method to tell to what component family the component belongs.

 package example;
 public class UIFile extends UIComponentBase {
    public String getFamily() {
        return "UIFile";
    }
   ...
 }

The component only needs a file name as parameter. To make it simple, the file component is an attribute that cannot be bound to a value expression (ie, a #{xxx}). Define the file member and the accessor methods:

  public class UIFile extends UIComponentBase {
    protected String file;
    public String getFile() {
        return file;
    }
    public void setFile(String file) {
        this.file = file;
    }
  }

Now, the JSF component is ready and we need a renderer. To make it simple, you can make the component provide and implement the renderer. This is easier but you loose some flexibility. In our case, implementing the rendering in the JSF encodeBegin method is enough. To keep the example small, the file path must be absolute (in reality I'm using a servlet context parameter to configure the directory; this is left as an exercise :-)). To copy the file, I'm using the famous Apache IO library.

   import org.apache.commons.io.IOUtils;
   public class UIFile extends UIComponentBase {
    private static final char[] STOP = new char[0];
    public void encodeBegin(final FacesContext context)
      throws IOException {
        final String name = getFile();
        if (name == null || name.length() == 0) {
            return;
        }
        File f = new File(name);
        if (!f.exists()) {
            return;
        }
        final ResponseWriter rw = context.getResponseWriter();
        rw.writeText(STOP, 0, 0);
        FileInputStream is = null;
        try {
            is = new FileInputStream(f);
            final char[] data = IOUtils.toCharArray(is, "UTF-8");
            rw.write(data);
        } catch (IOException ex) {
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ex) {
                }
            }
        }
   }
 }

For all this to work, there is a trick. The trick is that before writing the file content, we have to close any opened HTML element. This is done by calling writeText with an empty array. After that, the file data is written as is and unescaped.

Step2: Register the component in faces-config.xml

Once your UI component class is written, you have to register it in your faces-config.xml file:

    <component>
        <component-type>example.UIFile</component-type>
        <component-class>example.UIFile</component-class>
    </component>

Step3: Create the taglib

Then, write a taglib file. This is an XML file that tells Facelet the name of the tag in the XHTML file and how to bind that name to the new component.

 <facelet-taglib>
    <namespace>http://www.example.com/tool</namespace>
    <tag>
        <tag-name>insert</tag-name>
        <component>
            <component-type>example.UIFile</component-type>
        </component>
    </tag>
  </facelet-taglib>

Step4: Use the component

The last step is to use our new component in the XHTML file, this is as simple as this:

  <tool:insert file="myfile.html"/>

Add a comment

To add a comment, you must be connected. Login

1 comments

guyr-ml1@burntmail.com
Guy Rouillier on 2010-03-04 05:50:51 said:

Thanks very much for this post, which I found via Google. I've been trying to figure out how to embed portlet content on a facelet, and you gave me the information necessary to do so in an elegant way. The Portlet Container puts the contents into the request map; the servlet they provide reads it back out using JSTL c:out, which does not work on facelets. I borrowed your technique to implement a custom tag function to do the job (sorry about the lack of formatting - I can't figure out your markup language):

public static void getPortletContent(String portletWindowName) {
try {
Object obj = FacesContext.getCurrentInstance().getExternalContext()
.getRequestMap().get(portletWindowName);

if (obj != null) {
String portletWindowContent = BeanUtils.getProperty(obj, "content");
if (StringUtils.isNotEmpty(portletWindowContent)) {
ResponseWriter rw = FacesContext.getCurrentInstance()
.getResponseWriter();
rw.write(portletWindowContent);
}
}
}
catch (Exception e) {
log.error("getPortletContent exception: " + e.getMessage());
}
}