
org.icefaces.application.ResourceRegistry Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icefaces Show documentation
Show all versions of icefaces Show documentation
${icefaces.product.name} Core Framework Library
The newest version!
/*
* Copyright 2004-2014 ICEsoft Technologies Canada Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS
* IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.icefaces.application;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;
import java.util.logging.Level;
import javax.el.ELContext;
import javax.faces.application.*;
import javax.faces.context.FacesContext;
import javax.faces.context.ExternalContext;
import javax.faces.event.PhaseId;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.Serializable;
import org.icefaces.impl.application.SessionAwareResourceHandlerWrapper;
import org.icefaces.util.EnvUtils;
import org.icefaces.impl.util.Util;
import org.icefaces.impl.application.WindowScopeManager;
/**
*
* The ResourceRegistry
allows an application to register
* javax.faces.application.Resource instances at runtime. Each Resource
* is registered in a specified scope (Application, Session, View, Flash
* Window) so that the resource can be garbage collected when the scope
* expires.
*
*/
public class ResourceRegistry extends SessionAwareResourceHandlerWrapper {
private static final String VIEW_ID_PARAMETER = "ice.view";
private static final String JAVAX_FACES_VIEW_STATE = "javax.faces.ViewState";
private static final String WINDOW_ID_PARAMETER = "ice.window";
private static final String WINDOW_SCOPE = "w";
private static final String VIEW_SCOPE = "v";
private static final String SESSION_SCOPE = "s";
private static final String APPLICATION_SCOPE = "a";
private static final Logger log = Logger.getLogger(ResourceRegistry.class.getName());
private ResourceHandler wrapped;
private static String RESOURCE_PREFIX = "/javax.faces.resource/";
private static String MAP_PREFIX = "org.icefaces.resource-";
private static String BYTES_PREFIX = "bytes=";
private static String CONTENT_LENGTH = "Content-Length";
private static String CONTENT_RANGE = "Content-Range";
private static String RANGE = "Range";
public ResourceRegistry(ResourceHandler wrapped) {
this.wrapped = wrapped;
}
public ResourceHandler getWrapped() {
return wrapped;
}
public boolean isSessionAwareResourceRequest(FacesContext context) {
return wrapped.isResourceRequest(context);
}
public void handleSessionAwareResourceRequest(FacesContext facesContext) throws IOException {
ExternalContext externalContext = facesContext.getExternalContext();
String key = extractResourceId(facesContext);
log.finest("extractResourceId: " + key);
String viewID = externalContext.getRequestParameterMap().get(VIEW_ID_PARAMETER);
if (viewID != null) {
//restore view root with its associated view map
facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
facesContext.getApplication().getViewHandler().restoreView(facesContext, viewID);
}
boolean useRanges = false;
int rangeStart = 0;
int rangeEnd = 0;
String rangeHeader = externalContext.getRequestHeaderMap()
.get(RANGE);
if (null != rangeHeader) {
try {
if (rangeHeader.startsWith(BYTES_PREFIX)) {
String range = rangeHeader
.substring(BYTES_PREFIX.length() );
int splitIndex = range.indexOf("-");
String startString = range.substring(0, splitIndex);
String endString = range.substring(splitIndex + 1);
rangeStart = Integer.parseInt(startString);
// ICE-9256 rangeEnd == 0 means use contentLength below
if (!"".equals( endString )) {
rangeEnd = Integer.parseInt(endString);
}
useRanges = true;
}
} catch (Exception e) {
useRanges = false;
if (log.isLoggable(Level.FINE)) {
log.fine("Unable to decode range header " + rangeHeader);
}
}
}
if (useRanges) {
externalContext.setResponseStatus(206);
}
if (log.isLoggable(Level.FINE)) {
log.fine("handleResourceRequest " + key + " path: " +
externalContext.getRequestServletPath() + " info: " +
externalContext.getRequestPathInfo());
}
if (null == key) {
wrapped.handleResourceRequest(facesContext);
return;
}
ELContext elContext = facesContext.getELContext();
ResourceRegistryHolder holder = (ResourceRegistryHolder) elContext
.getELResolver().getValue(elContext, null, MAP_PREFIX + key);
log.finest("ELResolver ResourceRegistryHolder: " + holder);
//For portlets it may be necessary to use a backup plan for setting and getting
//session based dynamic resources
if (null == holder) {
HttpSession session = EnvUtils.getSafeSession(facesContext, false);
if (session != null) {
holder = (ResourceRegistryHolder) session.getAttribute(MAP_PREFIX + key);
log.finest("Session ResourceRegistryHolder: " + holder);
if (null != holder) {
//workaround for ICE-7685 suspecting PFB bug
if (log.isLoggable(Level.FINE)) {
log.fine("Resource lookup required direct sesssion access");
}
}
}
}
if (null == holder) {
wrapped.handleResourceRequest(facesContext);
return;
}
//TODO: also check the name
Resource resource = holder.resource;
log.finest("Resource: " + resource);
String contentType = resource.getContentType();
if (contentType != null) {
externalContext.setResponseContentType(resource.getContentType());
}
Map headers = resource.getResponseHeaders();
String contentLength = "";
for (String header : headers.keySet()) {
if (useRanges) {
if (CONTENT_LENGTH.equals(header)) {
contentLength = headers.get(CONTENT_LENGTH);
continue;
}
}
externalContext.setResponseHeader(header, headers.get(header));
}
InputStream in = resource.getInputStream();
OutputStream out = externalContext.getResponseOutputStream();
if (Util.acceptGzip(externalContext) &&
EnvUtils.isCompressResources(facesContext) &&
Util.shouldCompress(resource.getContentType()) ) {
externalContext.setResponseHeader("Content-Encoding", "gzip");
Util.compressStream(in, out);
} else {
//ranges can be used for subsequent uncompressed responses
externalContext.setResponseHeader("Accept-Ranges", "bytes");
try {
if (useRanges) {
int cl = Integer.parseInt(contentLength);
rangeEnd = (rangeEnd == 0) ? cl-1: rangeEnd;
externalContext.setResponseHeader(CONTENT_RANGE,
"bytes " + rangeStart + "-" + rangeEnd + "/" +
contentLength );
externalContext.setResponseHeader(CONTENT_LENGTH,
"" + (1 + rangeEnd - rangeStart));
Util.copyStream(in, out, rangeStart, rangeEnd);
} else {
Util.copyStream(in, out);
}
} catch (IOException e) {
final String message = e.getMessage();
final Throwable cause = e.getCause();
//test if connection was aborted before the entire response was sent (Servlet 3.0 or Jetty 9.*)
if ((message != null && message.contains("Connection close")) || (cause != null && cause.toString().contains("EofException"))) {
// client left the page
log.log(Level.FINE, "Connection closed by client.", e);
} else {
throw e;
}
}
}
}
/**
* Add the provided resource to the custom scope Map. This is intended to
* be used only in cases not covered by the other scope-specific methods.
*
* @param scopeMap the scopemap
* @param resource the resource
* @return the requestPath of the resource
*/
public static String addResource(Map scopeMap, Resource resource) {
return addResource("r", scopeMap, resource );
}
private static String addResource(String prefix, Map scopeMap,
Resource resource) {
String name = resource.getResourceName();
String key;
if ( (null != name) && (name.length() > 0) ) {
key = name;
} else {
key = prefix + UUID.randomUUID().toString();
}
ResourceRegistryHolder holder = new ResourceRegistryHolder(key, resource);
scopeMap.put(MAP_PREFIX + key, holder);
FacesContext context = FacesContext.getCurrentInstance();
String[] pathTemplate = EnvUtils.getPathTemplate();
String path = pathTemplate[0] + key + pathTemplate[1];
if (SESSION_SCOPE == prefix) {
EnvUtils.getSafeSession(context).setAttribute(MAP_PREFIX + key, holder);
}
if (WINDOW_SCOPE == prefix) {
String windowID = WindowScopeManager.lookupAssociatedWindowID(context.getExternalContext().getRequestMap());
if (path.indexOf('?') > 0) {
path = path + "&" + WINDOW_ID_PARAMETER + "=" + windowID;
} else {
path = path + "?" + WINDOW_ID_PARAMETER + "=" + windowID;
}
}
if (VIEW_SCOPE == prefix) {
String viewState = context.getApplication().getStateManager().getViewState(context);
String viewId = context.getViewRoot().getViewId();
if (path.indexOf('?') > 0) {
path = path + "&" + JAVAX_FACES_VIEW_STATE + "=" + viewState + "&" + VIEW_ID_PARAMETER + "=" + viewId;
} else {
path = path + "?" + JAVAX_FACES_VIEW_STATE + "=" + viewState + "&" + VIEW_ID_PARAMETER + "=" + viewId;
}
}
path = context.getExternalContext().encodeResourceURL(path);
log.finest("\nresourceName: " + name + "\nkey: " + key + "\nholder: " + holder + "\npath: " + path);
return path;
}
private static String extractResourceId(FacesContext facesContext) {
ExternalContext externalContext = facesContext.getExternalContext();
int markerStart = -1;
String path = externalContext.getRequestServletPath();
if( path != null ){
markerStart = path.indexOf(RESOURCE_PREFIX);
}
if (-1 == markerStart) {
path = externalContext.getRequestPathInfo();
if( path != null ){
markerStart = path.indexOf(RESOURCE_PREFIX);
}
}
// With Liferay (and likely portals in general), the reference to javax.faces.resource
// gets set to a parameter rather than part of the URL so we need a slightly different algorithm.
if (-1 == markerStart) {
final String resId = "javax.faces.resource";
Iterator names = externalContext.getRequestParameterNames();
while (names.hasNext()) {
String name = (String)names.next();
// With the older bridge, the resource identifier was not be encoded in any way
// but with the newer bridge it is so we need to check both cases.
if( name.equalsIgnoreCase(resId) || name.endsWith(resId) ){
String res = externalContext.getRequestParameterMap().get(name);
//Need to strip the file extension (e.g. .jsf) if it's there because the original key
//did not use it.
String[] pathTemplate = EnvUtils.getPathTemplate();
String suffix = pathTemplate[1];
if(suffix != null && suffix.trim().length() > 0 ){
int suffixStart = res.indexOf(suffix);
if(suffixStart > 0){
res = res.substring(0,suffixStart);
}
}
return res;
}
}
}
if (-1 == markerStart) {
return null;
}
try {
//strip off the javax.faces.resource prefix and remove
//any extension found in the path template
String key = path.substring(
markerStart + RESOURCE_PREFIX.length(),
path.length() - EnvUtils.getPathTemplate()[1].length());
return key;
} catch (Exception e) {
log.log(Level.FINE, "could not extract resource id", e);
return null;
}
}
public static Resource getResourceByName(FacesContext facesContext, String resName) {
log.finest("resName: " + resName);
log.finest("lookup : '" + MAP_PREFIX + resName + "'");
if (resName == null) {
return null;
}
ELContext elContext = facesContext.getELContext();
ResourceRegistryHolder holder = (ResourceRegistryHolder) elContext
.getELResolver().getValue(elContext, null, MAP_PREFIX + resName);
log.finest("ELResolver ResourceRegistryHolder: " + holder);
if (holder == null) {
HttpSession session = EnvUtils.getSafeSession(facesContext, false);
if (session != null) {
holder = (ResourceRegistryHolder) session.getAttribute(MAP_PREFIX + resName);
log.finest("Session ResourceRegistryHolder: " + holder);
}
}
if (null == holder) {
return null;
}
return holder.resource;
}
/**
* Add the provided resource in application scope.
*
* @param resource the resource
* @return the requestPath of the resource
*/
public static String addApplicationResource(Resource resource) {
return addResource(APPLICATION_SCOPE, FacesContext.getCurrentInstance()
.getExternalContext().getApplicationMap(), resource );
}
/**
* Add the provided resource in session scope. Note that session scope
* resources should be Serializable to support cluster replication
* and session passivation.
*
* @param resource the resource
* @return the requestPath of the resource
*/
public static String addSessionResource(Resource resource) {
return addResource(SESSION_SCOPE, FacesContext.getCurrentInstance()
.getExternalContext().getSessionMap(), resource );
}
/**
* Add the provided resource in view scope.
*
* @param resource the resource
* @return the requestPath of the resource
*/
public static String addViewResource(Resource resource) {
return addResource(VIEW_SCOPE, FacesContext.getCurrentInstance()
.getViewRoot().getViewMap(), resource );
}
/**
* Add the provided resource in window scope.
*
* @param resource the resource
* @return the requestPath of the resource
*/
public static String addWindowResource(Resource resource) {
return addResource(WINDOW_SCOPE, WindowScopeManager.lookupWindowScope(
FacesContext.getCurrentInstance()), resource );
}
}
//Hold the resources in an instance of this private class to provide
//security by ensuring that only resources stored via this API are served
class ResourceRegistryHolder implements Serializable {
public String key;
public Resource resource;
ResourceRegistryHolder(String key, Resource resource) {
this.key = key;
this.resource = resource;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy