Monday May 07, 2007
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).

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):

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!
Posted by Ken Paulsen
( May 07 2007, 04:26:46 AM PDT )
Permalink
Wednesday February 08, 2006
Configuring the Security Manager in GlassFish
Have you ever attempted to install an application on GlassFish, SJS Application Server, or another Java EE application server and run into an exception like this (Click here to skip this long stack trace):
javax.servlet.ServletException
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:313)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:178)
java.security.AccessController.doPrivileged(Native Method)
com.jivesoftware.util.SetResponseCharacterEncodingFilter.doFilter(SetResponseCharacterEncodingFilter.java:53)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:234)
java.security.AccessController.doPrivileged(Native Method)
com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:118)
com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:52)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:234)
java.security.AccessController.doPrivileged(Native Method)
com.jivesoftware.base.PresenceFilter.doFilter(PresenceFilter.java:113)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:234)
java.security.AccessController.doPrivileged(Native Method)
com.jivesoftware.util.SetRequestCharacterEncodingFilter.doFilter(SetRequestCharacterEncodingFilter.java:48)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:234)
java.security.AccessController.doPrivileged(Native Method)
com.jivesoftware.util.JiveSetupFilter.doFilter(JiveSetupFilter.java:44)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:234)
root cause
java.lang.ExceptionInInitializerError
java.lang.Class.forName0(Native Method)
java.lang.Class.forName(Class.java:164)
com.jivesoftware.util.ClassUtils.loadNormalClass(ClassUtils.java:121)
com.jivesoftware.util.ClassUtils.loadClass(ClassUtils.java:83)
com.jivesoftware.util.ClassUtils.access$100(ClassUtils.java:37)
com.jivesoftware.util.ClassUtils$JiveObjectFactory.getClassInstance(ClassUtils.java:245)
com.opensymphony.xwork.ObjectFactory.buildBean(ObjectFactory.java:105)
com.opensymphony.xwork.ObjectFactory.buildResult(ObjectFactory.java:159)
com.opensymphony.xwork.DefaultActionInvocation.createResult(DefaultActionInvocation.java:147)
com.opensymphony.xwork.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:255)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:182)
com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.jivesoftware.forum.action.JiveExceptionInterceptor.intercept(JiveExceptionInterceptor.java:63)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.jivesoftware.base.action.JiveObjectLoaderInterceptor.intercept(JiveObjectLoaderInterceptor.java:56)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.opensymphony.webwork.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:71)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35)
com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:164)
com.opensymphony.xwork.DefaultActionProxy.execute(DefaultActionProxy.java:116)
com.opensymphony.webwork.dispatcher.ServletDispatcher.serviceAction(ServletDispatcher.java:272)
com.jivesoftware.base.util.JiveWebWorkServlet.service(JiveWebWorkServlet.java:64)
javax.servlet.http.HttpServlet.service(HttpServlet.java:822)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:178)
java.security.AccessController.doPrivileged(Native Method)
com.jivesoftware.util.SetResponseCharacterEncodingFilter.doFilter(SetResponseCharacterEncodingFilter.java:53)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:234)
java.security.AccessController.doPrivileged(Native Method)
com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:118)
com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:52)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:234)
java.security.AccessController.doPrivileged(Native Method)
com.jivesoftware.base.PresenceFilter.doFilter(PresenceFilter.java:113)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:234)
java.security.AccessController.doPrivileged(Native Method)
com.jivesoftware.util.SetRequestCharacterEncodingFilter.doFilter(SetRequestCharacterEncodingFilter.java:48)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:234)
java.security.AccessController.doPrivileged(Native Method)
com.jivesoftware.util.JiveSetupFilter.doFilter(JiveSetupFilter.java:44)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:585)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:262)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:295)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:234)
This is what happenned to me while I was attempting to try out
Jive Forums from
Jive Software. Jive Forums provide (you guessed it!) forums, or message boards, in which a community can collaborate. While evaluating several java-based Forum packages, I found Jive Forums to be significantly ahead of their competition -- if you need forum software, make sure you take a look at this package.
Anyway, back to the error. This stack trace appeared in my web browser and wasn't particularly useful. However, I am well trained to look in the
server.log file to see if there is more information... and in this case there is. So,
Tip #1: look in the server.log file. This can be accomplished by using your favorite text editor (open
<glassfish-install-root>/domains/domain1/logs/server.log). Or, you may use the admin GUI (http://localhost:4848 -- replace localhost & 4848 if necessary) to view the Logs. Just Login and click on "Search Log Files." A much more useful message in the log file shows:
[#|2006-02-07T09:11:31.124-0800|INFO|sun-appserver-pe9.0|javax.enterprise.system.core.security|_ThreadID=11;_ThreadName=httpWorkerThread-8080-2;_ApplicationName=URI:/jive/index.jspa;|JACC Policy Provider: PolicyWrapper.implies, context(jive__jive)- permission((ognl.OgnlInvokePermission invoke.com.jivesoftware.base.action.ConditionalInterceptor.setDisallow)) domain that failed(ProtectionDomain (file:/glassfish/pe/publish/glassfish/domains/domain1/applications/j2ee-modules/jive/WEB-INF/lib/webwork.jar <no signer certificates>
WebappClassLoader
delegate: true
repositories:
/WEB-INF/classes/
----------> Parent Classloader:
EJBClassLoader : urlSet = []doneCalled = false Parent -> java.net.URLClassLoader@1386918 This message indicates that permission "ognl.OgnlInvokePermission" is needed for webwork.jar.
So why does this deploy and run without errors on some other Java EE Servers, but GlassFish complains? Answer: GlassFish is much more security conscious. The GlassFish server uses a security manager to protect your machine from the apps (and more importantly from the users of those applications). However, this means you need to configure security when you need to relax the permissions so that certain pieces of functionality work (as in this case). It is worth noting that GlassFish can run without a security manager (just like Tomcat, JBoss and other JavaEE application servers) by simply removing the following line from the domain.xml file:
-Djava.security.policy=${com.sun.aas.instanceRoot}/config/server.policy
However, while this is possible (and is the quickest way to solve this exception)... consider the security risks you may be introducing! You will have to evaluate what your security requirements are and whether you can afford to take that risk. You've been warned.

Now lets look at a couple other ways to address this:
1) Turn off security for a single application
2) Grant permission to a single jar file
Both of these options involve editing the "server.policy" file, or the "generated/policy/<application-name>/granted.policy".
Beware that if you redeploy, the granted.policy file will be overwritten. However, the granted.policy file is more efficient because the changes you make will only apply to the single application. Changes to the global server.policy will apply to all applications.
When editing these files, you may choose to use a text editor... or the J2SE "
policytool" (swing based gui). Here is an example server.policy entry that grants all permissions to webwork.jar:
grant codeBase "file:${com.sun.aas.installRoot}/domains/domain1/applications/j2ee-modules/jive/WEB-INF/lib/webwork.jar" { permission java.security.AllPermission;
};
This will solve the exception above. After making this change you will have to restart the server.
Depending on the package you are configuring, you may find (as I did while getting Jive to run) that several of the .jar files require additional security settings. You may choose to use a "wildcard" to match all paths / files, and turn off security for the whole application:
grant codeBase "file:${com.sun.aas.installRoot}/domains/domain1/applications/j2ee-modules/jive/-" { permission java.security.AllPermission;
};
grant codeBase "file:${com.sun.aas.installRoot}/domains/domain1/generated/jsp/j2ee-modules/jive/-" { permission java.security.AllPermission;
};
The above 2 grant statements will give all the permissions Jive needs to run on GlassFish (and more). The '-' character at the end will match any file in the "jive" directory or a subdirectory. While this approach doesn't expose your whole server, it does open security for this application wide open.
You may want to experiment and grant only the specific settings that are required by the application -- I was only evaluating Jive and got too lazy to configure it for a secure deployment scenario.
These same techniques apply to any software package (including WebWork and OpenSymphony, which are used by Jive Forums).
If you made it all the way to the bottom of this blog, please take the time to post a comment about your opinion as to how important a security manager is to you and your applications.
Thanks!
Ken
Posted by Ken Paulsen
( Feb 08 2006, 05:21:02 AM PST )
Permalink