All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.icefaces.application.ResourceRegistry Maven / Gradle / Ivy

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