May 15th, 2011 by Christian Kaltepoth

Serving dynamic file content with PrettyFaces

PrettyFaces offers a very simple and flexible way to create bookmark-able RESTful URLs in JSF applications. This short article will show you how easy it is to use PrettyFaces for serving other dynamic content like images or PDF files. Many applications are required to generate some kind of “dynamic” content for the user. A typical example are product images in a web shop. These are mostly not deployed with the application archive but rather stored in an external directory or a database. Another example are PDF reports that are generated on demand when a user requests them. Some component suite offer custom components for these kinds of requirements, for example PrimeFaces with <p:graphicImage> or RichFaces with <rich:paint2d>. But these components are often only able to render images and don’t offer full control over the HTTP response headers. Another option is to write a custom servlet, but this would require to manually parse the request URL and doesn’t integrate well with the rest of the JSF application. Interested in a very pretty way of implementing dynamic content? I will present a simple class that demonstrates how to use PrettyFaces to dynamically creating barcodes.
example barcode
Take a look at this class:
@ManagedBean
@RequestScoped
@URLMapping(id = "barcode", viewId = "/empty.jsf", 
    pattern = "/barcode/#{ /[0-9]+/ barcodeBean.value }.png")
public class BarcodeBean {
 
  private String value;
 
  @URLAction
  public void render() throws IOException {
 
    // get HttpServletResponse
    FacesContext context = FacesContext.getCurrentInstance();
    HttpServletResponse response = 
      (HttpServletResponse) context.getExternalContext().getResponse();
 
    // set correct content type
    response.setContentType("image/png");
 
    // setup CanvasProvider to render bitmap to response stream
    BitmapCanvasProvider canvas = new BitmapCanvasProvider(
        response.getOutputStream(), "image/png", 150,
        BufferedImage.TYPE_BYTE_BINARY, false, 0);
 
    // render barcode
    Interleaved2Of5Bean barcode = new Interleaved2Of5Bean();
    barcode.generateBarcode(canvas, value);
    canvas.finish();
 
    // mark response as completed
    context.responseComplete();
 
  }
 
  public String getValue() {
    return value;
  }
 
  public void setValue(String value) {
    this.value = value;
  }
 
}
If you have some experience with PrettyFaces, this code will look very familiar. The class defines an URL mapping using the annotation support added in PrettyFaces 3.1.0. The URL pattern matches URLs that look like regular PNG files (for example /barcode/1234567890.png). The file name (excluding the extension) is automatically injected into the value field of the bean. The pattern restricts the value to be numeric, which is required for the barcode we want to generate. Please note that the bean specifies /empty.jsf as the view id to render for the mapping. Unfortunately we need a valid view here to be able to enter the JSF lifecycle with our mapping. As this view won’t be rendered, it is possible to use a minimalistic empty view. For this example I created a file /empty.xhtml with the following content:
<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns="http://www.w3.org/1999/xhtml" />
The central method of the bean is called “render” and is annotated with @URLAction. This tells PrettyFaces to execute it if a file matching the URL pattern is accessed. We will use Barcode4J in this example to render the barcode. The method shows the required steps to send the dynamic content to the client:
  1. Obtain the HttpServletResponse from the FacesContext
  2. Set the correct content type and headers for the response
  3. Generate the dynamic content and write it to the response stream
  4. Call FacesContext.responseComplete()
That’s it! Straight forward, isn’t it? You can now embed such a barcode into a JSF page of our application like this:
<pretty:urlbuffer var="url" mappingId="barcode" >
  <f:param value="1234567890" />
</pretty:urlbuffer>
<img src="#{url}" alt="barcode" />
We are using <pretty:urlbuffer> to generate the required URL. The <f:param> tag sets the value of the path parameter in the mapping. Of cause you can also refer to other bean properties using an EL expression instead of setting a constant value. The generated URL can now be used in a HTML <img> element to actually insert the image into the page. As you can see serving other resources than HTML files is very easy using PrettyFaces. I’ve already used this pattern successfully in a number of scenarios like:
  • Serving images from a database
  • Rendering of chemical molecules using the CDK
  • Dynamically creation of Microsoft ODC connection files
  • On-Demand rendering of PDF files
The possibilities are limitless, and if you are able to share, we would love for you to post below and let us know how you’ve used this feature, or used PrettyFaces in general!
UPDATE – Example of this same barcode server using a single Rewrite configuration rule (does not require backing bean or blank JSF view):
Rewrite configuration barcode Rule
public class ExampleConfigurationProvider extends HttpConfigurationProvider
{

   @Override
   public Configuration getConfiguration(final ServletContext context)
   {
      return ConfigurationBuilder.begin()
         .addRule()
         .when(Direction.isInbound()
                      .and(Path.matches("/barcode/{id}.png")))
         .perform(new HttpOperation() {
             public void performHttp(HttpServletRewrite event, EvaluationContext context) {
                 String id = (String) Evaluation.property("id").retrieve(event, context);

                 HttpServletResponse response = event.getResponse();
                 // set correct content type
                 response.setContentType("image/png");
 
                 // setup CanvasProvider to render bitmap to response stream
                 BitmapCanvasProvider canvas = new BitmapCanvasProvider(response.getOutputStream(), "image/png", 150, BufferedImage.TYPE_BYTE_BINARY, false, 0);
 
                 // render barcode
                 Interleaved2Of5Bean barcode = new Interleaved2Of5Bean();
                 barcode.generateBarcode(canvas, id);
                 canvas.finish();
             }
         });
   }
}

Posted in PrettyFaces

17 Comments

  1. Scott Carnett says:

    I used this very same technique to generate captcha’s here – http://www.funeraldirectorslife.com/clients/consultation

  2. Ulisses says:

    Is It possible did a generic file server like this?

    /files/#{file}

    where /files/path/img.png

    and my bean return this?

  3. Sure! It would actually be very easy to do! You should give PrettyFaces a try! :)

  4. Ulisses says:

    I try.

    But see the problem:

    pattern: /files/#{file}

    When i request this: /files/path/image.png

    i can’t get ‘path/image.png’ because of ‘/’

    I resolve this using: /files/path:image.png
    and replace ‘:’ -> ‘/’

    Has a better sugestion?

    1. Use a regex discriminator in your pattern: http://ocpsoft.com/docs/prettyfaces/snapshot/en-US/html_single/#config.pathparams.regex

      So your pattern would look like this:

      pattern=”/files/#{ /.*/ file }”

      That will allow the pattern to match over the ‘/’ character. By default this is restricted, but that’s how you get around it.

  5. Ulisses says:

    Thanks

    it is solved!

  6. Christian. How did you include Barcode4j in this example? I only see 2.0 in Maven. Also having problems deploying on AS6 with 2.0.

  7. Actually, Christian, you don’t need to point to a valid JSF file. The view-id just needs to map into the Faces Servlet, so even something like “/null.jsf” will work!

  8. Thanks Lincoln! You are right! I just tested this with Mojarra and MyFaces on Tomcat and it works fine!

    If I remember correctly I tested this back then with one of our applications and it didn’t work there! Weird!

    However! Great that its working this way! It makes things even more simple! :)

  9. Btw. This was a popular demo when I showed it at JAXConf last week!

  10. Great! Nice to hear that I’m not the only one loving this pattern! :)

  11. Oleg says:

    Nice article. But I don’t like empty.jsf. We’ve done a similar task with a quite normal Servlet and CDI injection. CDI / CODI allows to inject beans into servlets. Servlet-Mapping is done via Servlet 3 annotation @WebServlet. Well, you have to extract the file name from URL manually, but it is not a problem. So, I don’t see any advantage to use PrettyFaces here :-).

    1. Hey Oleg, I agree! If you don’t want an empty JSF page, then you should consider using Rewrite for this. http://ocpsoft.org/rewrite/ , where you can serve the image directly from your rewrite rule. Actually I should write up an example of this :)

      1. Oleg says:

        Hi Lincoln,

        Yeah, an example how to stream down the image by a Rewrite rule would be really great!

      2. Here you go… something like this should work:

        public class ExampleConfigurationProvider extends HttpConfigurationProvider
        {
        
           @Override
           public Configuration getConfiguration(final ServletContext context)
           {
              return ConfigurationBuilder.begin()
                 .addRule()
                 .when(Direction.isInbound()
                              .and(Path.matches("/barcode/{id}.png")))
                 .perform(new HttpOperation() {
                     public void performHttp(HttpServletRewrite event, EvaluationContext context) {
                         String id = (String) Evaluation.property("id").retrieve(event, context);
        
                         HttpServletResponse response = event.getResponse();
                         // set correct content type
                         response.setContentType("image/png");
         
                         // setup CanvasProvider to render bitmap to response stream
                         BitmapCanvasProvider canvas = new BitmapCanvasProvider(response.getOutputStream(), "image/png", 150, BufferedImage.TYPE_BYTE_BINARY, false, 0);
         
                         // render barcode
                         Interleaved2Of5Bean barcode = new Interleaved2Of5Bean();
                         barcode.generateBarcode(canvas, id);
                         canvas.finish();
                     }
                 });
           }
        }
  12. Oleg says:

    Great! perform(new HttpOperation() { … }) rocks. I was missing this.

Leave a Comment




Please note: In order to submit code or special characters, wrap it in

[code lang="xml"][/code]
(for your language) - or your tags will be eaten.

Please note: Comment moderation is enabled and may delay your comment from appearing. There is no need to resubmit your comment.