com.sun.jsftemplating.util.fileStreamer.FacesStreamerContext Maven / Gradle / Ivy
/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the license at
* https://jsftemplating.dev.java.net/cddl1.html or
* jsftemplating/cddl1.txt.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at jsftemplating/cddl1.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* you own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.jsftemplating.util.fileStreamer;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
/**
* This class encapsulates servlet specific objects so that the
* {@link FileStreamer} class is not servlet-specific.
*
* This implementation will look for the following attributes
* ({@link BaseContext#setAttribute(String, Object)}):
*
* - {@link Context#CONTENT_TYPE} -- The "Content-type:" of the response.
* - {@link Context#CONTENT_DISPOSITION} -- Disposition of the streamed content.
* - {@link Context#CONTENT_FILENAME} -- Filename of the streamed content.
* - {@link Context#EXTENSION} -- The file extension of the response.
*
*/
public class FacesStreamerContext extends BaseContext {
/**
* Constructor.
*/
public FacesStreamerContext(FacesContext ctx) {
setFacesContext(ctx);
init();
}
/**
* This method initializes {@link FileStreamer}
* {@link ContentSource}s. It looks for the
* {@link Context#CONTENT_SOURCES} context parameter (JSF doesn't
* provide a way to get to the ServletConfig init parameters, as of
* JSF 1.2).
*/
protected synchronized void init() {
FacesContext ctx = getFacesContext();
if (ctx == null) {
// Should not happen in a normal env...
return;
}
ExternalContext extCtx = ctx.getExternalContext();
boolean initDone = extCtx.getApplicationMap().containsKey(INIT_DONE);
if (initDone) {
return;
}
// Register ContentSources
String sources = extCtx.getInitParameter(CONTENT_SOURCES);
FileStreamer fs = getFileStreamer();
if ((sources != null) && (sources.trim().length() != 0)) {
StringTokenizer tokens = new StringTokenizer(sources, " \t\n\r\f,;:");
while (tokens.hasMoreTokens()) {
fs.registerContentSource(tokens.nextToken());
}
}
// Set Valid path parameters...
String allow = extCtx.getInitParameter(ALLOW_PATHS);
List paths = new ArrayList();
if (allow != null) {
StringTokenizer tok = new StringTokenizer(allow, ",:;");
while (tok.hasMoreTokens()) {
paths.add(ResourceContentSource.normalize(tok.nextToken()));
}
} else {
paths.add("");
}
setAllowedPaths(extCtx, paths);
// Set invalid paths...
String deny = extCtx.getInitParameter(DENY_PATHS);
paths = new ArrayList();
if (deny != null) {
StringTokenizer tok = new StringTokenizer(deny, ",:;");
while (tok.hasMoreTokens()) {
paths.add(ResourceContentSource.normalize(tok.nextToken()));
}
} else {
paths.add("WEB-INF/");
paths.add("META-INF/");
}
setDeniedPaths(extCtx, paths);
if (ctx != null) {
// Mark initialization as complete
extCtx.getApplicationMap().put(INIT_DONE, true);
}
}
/**
* Accessor to get the {@link FileStreamer} instance.
*/
public FileStreamer getFileStreamer() {
return FileStreamer.getFileStreamer(getFacesContext());
}
/**
* This method locates the appropriate {@link ContentSource} for this
* {@link Context}. It uses the FacesContext
's
* ExternalContext
to look for a request parameter
* named {@link Context#CONTENT_SOURCE_ID}. This value is used as the
* key when looking up registered {@link ContentSource}
* implementations.
*/
public ContentSource getContentSource() {
ContentSource src = (ContentSource) getAttribute("_contentSource");
if (src != null) {
return src;
}
// Get the ContentSource id
FacesContext ctx = getFacesContext();
String id = ctx.getExternalContext().getRequestParameterMap().
get(Context.CONTENT_SOURCE_ID);
if (id == null) {
// Use the default ContentSource
id = Context.DEFAULT_CONTENT_SOURCE_ID;
}
// Get the ContentSource
src = getFileStreamer().getContentSource(id);
if (src == null) {
throw new RuntimeException("The ContentSource with id '" + id
+ "' is not registered!");
}
// Return the ContentSource
setAttribute("_contentSource", src);
return src;
}
/**
* This method allows the Context to restrict access to resources.
* It returns true
if the user is allowed to view the
* resource. It returns false
if the user should not
* be allowed access to the resource.
*/
public boolean hasPermission(ContentSource src) {
String filename = src.getResourcePath(this);
ExternalContext extCtx = getFacesContext().getExternalContext();
boolean ok = false;
// Ensure it is in our list of OK paths...
List paths = getAllowedPaths(extCtx);
for (String path : paths) {
if (filename.startsWith(path)) {
ok = true;
break;
}
}
// ...and ensure it is not in our blacklisted paths...
if (ok) {
// Only check if ok so far...
paths = getDeniedPaths(extCtx);
for (String path : paths) {
if (filename.startsWith(path)) {
ok = false;
break;
}
}
}
return ok;
}
/**
* This methods sets the allowed paths for resources. Paths may be
* further restricted using the {@link #setDeniedPaths} method.
*/
public List getAllowedPaths(ExternalContext extCtx) {
return (List) extCtx.getApplicationMap().get(ALLOWED_PATHS_KEY);
}
/**
* This methods sets the allowed paths for resources. Paths may be
* further restricted using the {@link #setDeniedPaths} method.
*/
public void setAllowedPaths(ExternalContext ctx, List paths) {
ctx.getApplicationMap().put(ALLOWED_PATHS_KEY, paths);
}
/**
* This methods sets the list of paths in which resources should not
* be served.
*/
public void setDeniedPaths(ExternalContext ctx, List paths) {
ctx.getApplicationMap().put(DENIED_PATHS_KEY, paths);
}
/**
* This methods sets the list of paths for resources.
*/
public List getDeniedPaths(ExternalContext extCtx) {
return (List) extCtx.getApplicationMap().get(DENIED_PATHS_KEY);
}
/**
* This method is responsible for setting the response header
* information.
*/
public void writeHeader(ContentSource source) {
// FIXME: Portlet
ServletResponse resp = (ServletResponse)
getFacesContext().getExternalContext().getResponse();
// Set the "Last-Modified" Header
// First check context
long longTime = source.getLastModified(this);
if (longTime != -1) {
HttpServletResponse httpResponse = ((HttpServletResponse) resp);
httpResponse.setDateHeader("Last-Modified", longTime);
httpResponse.setDateHeader("Expires",
new java.util.Date().getTime() + Context.EXPIRY_TIME);
}
// First check CONTENT_TYPE
String contentType = (String) getAttribute(CONTENT_TYPE);
if (contentType == null) {
// Not found yet, check EXTENSION
String ext = (String) getAttribute(EXTENSION);
if (ext != null) {
contentType = FileStreamer.getMimeType(ext);
}
if (contentType == null) {
// Default Content-type is: application/octet-stream
contentType = FileStreamer.getDefaultMimeType();
}
}
((HttpServletResponse) resp).setHeader("Content-type", contentType);
// Check disposition/filename to associate a name with the stream
String disposition = (String) getAttribute(CONTENT_DISPOSITION);
String filename = (String) getAttribute(CONTENT_FILENAME);
if (disposition == null) {
// No disposition set, see if we have a filename
if (filename != null) {
((HttpServletResponse) resp).setHeader("Content-Disposition",
DEFAULT_DISPOSITION + ";filename=\"" + filename + "\"");
}
} else {
// Disposition set, see if we also have a filename
if (filename != null) {
disposition += ";filename=\"" + filename + "\"";
}
((HttpServletResponse) resp).setHeader("Content-Disposition",
disposition);
}
}
/**
* This method is responsible for sending an error.
*/
public void sendError(int code, String msg) throws IOException {
// FIXME: JSF 2.0 now provides: externalContext.responseSendError(int, String)
// FIXME: Portal
HttpServletResponse resp = (HttpServletResponse)
getFacesContext().getExternalContext().getResponse();
if (msg == null) {
resp.sendError(code);
} else {
resp.sendError(code, msg);
}
}
/**
* This method is returns the ServletOutputStream
.
*/
public OutputStream getOutputStream() throws IOException {
return getFacesContext().getExternalContext().getResponseOutputStream();
}
/**
* This returns the FacesContext
. This is the same
* as calling:
*
* getAttribute({@link #FACES_CONTEXT})
*/
public FacesContext getFacesContext() {
return (FacesContext) getAttribute(FACES_CONTEXT);
}
/**
* This sets the FacesContext
. This is the same as
* calling:
*
* setAttribute({@link #FACES_CONTEXT}, ctx)
*/
protected void setFacesContext(FacesContext ctx) {
setAttribute(FACES_CONTEXT, ctx);
}
/**
* Flag indicating initialization for this class has been completed.
*/
private static final String INIT_DONE = "__jsft_StreamContextInitialized";
/**
* The attribute value to access the FacesContext
. See
* {@link #getFacesContext()}.
*/
public static final String FACES_CONTEXT = "facesContext";
/**
* The default Content-Disposition. It is only used when a filename
* is provided, but a disposition is not. The default is
* "attachment". This will normally cause a browser to prompt the
* user to save the file. This is the default since setting a
* filename implies that the user may want to save this file. You
* must explicitly set the disposition for "inline" behavior with a
* filename.
*/
public static final String DEFAULT_DISPOSITION = "attachment";
}