Streaming Content in JSF with JSFTemplating's FileStreamer

 

FileStreamer

I have received questions from several people about how to use the "FileStreamer" feature of JSFTemplating.  So I thought a blog would be the best way to demonstrate how it works. 

FileStreamer provides the ability for the FacesServlet to stream content to the client (i.e. web browser).  If that sounds generic, it is because FileStreamer is very generic.  It allows you to define a ContentSource that is capable of getting content from just about anywhere.  You might choose to get content from a database, retrieve it via a web service, generate it in code, access it from the filesystem or the classpath, or just about anywhere else.  The ContentSource interface allows you to specify the content and information about it so that appropriate http headers will be set, causing the client (browser) to treat it correctly (i.e. Content-type, Content-Disposition, etc.).  In addition to this, FileStreamer works in the context of JSF, meaning you will have access to managed beans or anything you require from your JSF environment.  (NOTE: FileStreamer actually provides a Context which interacts with its environment.  This allows different Context implementations to be provided for different environments; Servlet and JSF Contexts are currently available, see: ServletStreamerContext and FacesStreamerContext).

Lets look at a couple of examples.

Setup

First you have to have your JSFTemplating evironment setup.  Follow these instructions for this. 

Next to configure FileStreamer for JSF, add the following to your web.xml file:

  <context-param>
    <param-name>ContentSources</param-name>
    <param-value>org.example.contentSources.ExampleContentSource,org.example.contentSources.ProxyContentSource</param-value>
  </context-param>
  <servlet-mapping>
    <servlet-name>FacesServlet</servlet-name>
    <url-pattern>/resource/\*</url-pattern>
  </servlet-mapping>

The context-param registers 2 ContentSources.  The source to both of these is checked into JSFTemplating's demo application.  You can browse that source online here.  The servlet-mapping requires a prefix mapping and needs its own dedicated FacesServlet mapping.  "/resource/\*" is the default, however, this can be configured, see RESOURCE_PREFIX for more info.

ExampleContentSource

Let's take a look at the key part of the ExampleContentSource to see how it works.


ExampleContentSource.java:

public InputStream getInputStream(Context ctx) throws IOException {
// See if we already have it.
InputStream in = (InputStream) ctx.getAttribute("inputStream");
if (in == null) {
// Create some content...
in = new ByteArrayInputStream(("<b>Hello! You requested: '"
+ ctx.getAttribute(Context.FILE_PATH) + "'</b>").getBytes());


// Set the extension so it can be mapped to a MIME type
ctx.setAttribute(Context.CONTENT_TYPE, "text/plain");

// Save in case method is called multiple times
ctx.setAttribute("inputStream", in);
}

// Return the InputStream
return in;

The above ContentSource (ExampleContentSource) is very simple, it generates its content from a String (see green text above).  The String is some text with the request path (which is the PATH_INFO of the request, in other words the part of the URL after the "/resource").  Notice I added some HTML tags to show how they're treated.  The red text shows that the Content-type is being explicitly set to "text/plain".  This should cause the browser not to parse any html (so we should see those <b> tags on the screen).

 ExampleContentSource in action

As you can see, this simple ContentSource produces plain text in the browser.  You also see that the URL requires "contentSourceId=example".  "example" comes from the "id" of ExampleContentSource.

ProxyContentSource

Let's take a look at 1 more example ContentSource.  We'll use the same URL, except we'll use the contentSourceId of "proxy" to target our other ContentSource.  Below is the interesting part of the source code for ProxyContentSource.java:

ProxyContentSource.java:

public InputStream getInputStream(Context ctx) throws IOException {
// See if we already have it.
InputStream in = (InputStream) ctx.getAttribute("inputStream");
if (in == null) {
// Get the path...
String path = (String) ctx.getAttribute(Context.FILE_PATH);
while (path.startsWith("/")) {
path = path.substring(1);
}

// Get the URL...
URL url = new URL("http://" + path);

// Set the extension so it can be mapped to a MIME type
int index = path.lastIndexOf('.');
if (index > 0) {
ctx.setAttribute(Context.EXTENSION, path.substring(index + 1));
}

// Open the InputStream
in = url.openStream();

// Save in case method is called multiple times
ctx.setAttribute("inputStream", in);
}

// Return the InputStream
return in;

Again we are creating an InputStream, however, this time we are getting it via a URL.  This time instead of hard-coding the Content-type, we're setting the extension of the file so that it will be mapped to an appropriate Content-type.  Here's the output for the same URL as before (except w/ our "proxy" contentSourceId):

ProxyContentSource in action!

In this example, the content is pulled from java.sun.com from the server (not the client), then streamed to the client.  The appropriate Content-type of "image/gif" was sent to the browser so that it could treat the content correctly.  If you run this example, try other urls and types of media (html, pdf, doc, etc.).

I hope this blog gives you an idea of how FileStreamer functionality is useful.  Please leave a comment and let me know what you think!  Below is one more section describing how to configure FileStreamer in a Servlet environment (doesn't need JSF):

Servlet Setup

  <servlet>
    <servlet-name>servletStreamer</servlet-name>
    <servlet-class>com.sun.jsftemplating.util.fileStreamer.ServletStreamer</servlet-class>
    <init-param>
        <param-name>ContentSources</param-name>
        <param-value>org.example.contentSources.ExampleContentSource,org.example.contentSources.ProxyContentSource</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>servletStreamer</servlet-name>
    <url-pattern>/resource/\*</url-pattern>
  </servlet-mapping>

 

That's it... the rest is the same as above.  You can change the url mapping directly in the web.xml file in this case.  Oh... and yes, you can use the same ContentSources in both environments!

Have Fun!

 

Comments:

Really knowledgeable article. I do agree with the author that AJAX is the future of web. I hope to see some blogs on AJAX..

Posted by Robins Tomar on May 26, 2007 at 02:04 PM PDT #

Thanks! Really nice article, which helped me a lot! ,-)

Posted by Hiro on May 26, 2009 at 03:58 PM PDT #

Thanks for your article. I exacctly need this feature. I want to display the content of a remote website in my web application so I send a http request and want to return the result as an stream to my page! Where can I get this JSFTemplating? I clicked on the link but there was nothing to download. Can you tell me how can I implement this?
Thanks

Posted by Mohammad on January 06, 2011 at 12:37 PM PST #

Post a Comment:
Comments are closed for this entry.
About

paulsen

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today