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

com.sun.faces.application.resource.ResourceImpl Maven / Gradle / Ivy

There is a newer version: 4.1.1
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package com.sun.faces.application.resource;

import static com.sun.faces.util.Util.getFacesMapping;
import static com.sun.faces.util.Util.getFirstWildCardMappingToFacesServlet;
import static com.sun.faces.util.Util.getLastModified;
import static com.sun.faces.util.Util.isExactMapped;
import static com.sun.faces.util.Util.isPrefixMapped;
import static com.sun.faces.util.Util.isResourceExactMappedToFacesServlet;
import static java.util.Collections.emptyMap;
import static java.util.Locale.US;
import static java.util.logging.Level.FINEST;
import static javax.faces.application.ProjectStage.Development;
import static javax.faces.application.ProjectStage.Production;
import static javax.faces.application.ResourceHandler.JSF_SCRIPT_LIBRARY_NAME;
import static javax.faces.application.ResourceHandler.JSF_SCRIPT_RESOURCE_NAME;
import static javax.faces.application.ResourceHandler.RESOURCE_IDENTIFIER;

import java.io.Externalizable;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.net.URL;
import java.net.URLConnection;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.application.ProjectStage;
import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;

import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.util.FacesLogger;

/**
 * Default implementation of {@link javax.faces.application.Resource}.
 * The ResourceImpl instance itself has the same lifespan as the
 * request, however, the ResourceInfo instances that back this object
 * are cached by the ResourceManager to reduce the time spent scanning
 * for resources.
 */
public class ResourceImpl extends Resource implements Externalizable {

    // Log instance for this class
    private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();

    /* HTTP Date format required by the HTTP/1.1 RFC */
    private static final String RFC1123_DATE_PATTERN =
          "EEE, dd MMM yyyy HH:mm:ss zzz";

    private static final String IF_MODIFIED_SINCE = "If-Modified-Since";

    private static final TimeZone GMT = TimeZone.getTimeZone("GMT");


    /* The meta data on the resource */
    private transient ResourceInfo resourceInfo;

    /*
     * Response headers that need to be added by the ResourceManager
     * implementation.
     */
    private transient Map responseHeaders;

    
    /**
     * Time when this application was started.  This is used to generate
     * expiration headers.
     */
    private long initialTime;


    /**
     * Lifespan of this resource for caching purposes.
     */
    private long maxAge;


    // ------------------------------------------------------------ Constructors


    /**
     * Necessary for serialization.
     */
    @SuppressWarnings({"UnusedDeclaration"})
    public ResourceImpl() { }


    /**
     * Creates a new instance of ResourceBase
     */
    public ResourceImpl(ResourceInfo resourceInfo, String contentType, long initialTime, long maxAge) {

        this.resourceInfo = resourceInfo;
        super.setResourceName(resourceInfo.getName());
        super.setLibraryName(resourceInfo.getLibraryInfo() != null
                             ? resourceInfo.getLibraryInfo().getName()
                             : null);
        super.setContentType(contentType);
        this.initialTime = initialTime;
        this.maxAge = maxAge;
    }

    @Override
    public boolean equals(Object o) {

        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        ResourceImpl resource = (ResourceImpl) o;

        return resourceInfo.equals(resource.resourceInfo);
    }

    @Override
    public int hashCode() {
        return resourceInfo.hashCode();
    }


    // --------------------------------------------------- Methods from Resource


    /**
     * @see javax.faces.application.Resource#getInputStream()
     */
    @Override
    public InputStream getInputStream() throws IOException {
		initResourceInfo();
        return resourceInfo.getHelper().getInputStream(resourceInfo, FacesContext.getCurrentInstance());
    }

    /**
     * @see javax.faces.application.Resource#getURL()
     */
    @Override
    public URL getURL() {
        return resourceInfo.getHelper().getURL(resourceInfo, FacesContext.getCurrentInstance());
    }

    /**
     * 

* Implementation note. Any values added to getResponseHeaders() * will only be visible across multiple calls to this method when * servicing a resource request (i.e. {@link ResourceHandler#isResourceRequest(javax.faces.context.FacesContext)} * returns true). If we're not servicing a resource request, * an empty Map will be returned and the values added are effectively thrown * away. *

* * @see javax.faces.application.Resource#getResponseHeaders() */ @Override public Map getResponseHeaders() { if (isResourceRequest()) { if (responseHeaders == null) { responseHeaders = new HashMap<>(6, 1.0f); } long expiresTime; if (FacesContext.getCurrentInstance().isProjectStage(Development)) { expiresTime = new Date().getTime(); } else { expiresTime = new Date().getTime() + maxAge; } SimpleDateFormat format = new SimpleDateFormat(RFC1123_DATE_PATTERN, US); format.setTimeZone(GMT); responseHeaders.put("Expires", format.format(new Date(expiresTime))); URL url = getURL(); InputStream in = null; try { URLConnection conn = url.openConnection(); conn.setUseCaches(false); conn.connect(); in = conn.getInputStream(); long lastModified = getLastModified(url); long contentLength = conn.getContentLength(); if (lastModified == 0) { lastModified = initialTime; } responseHeaders.put("Last-Modified", format.format(new Date(lastModified))); if (lastModified != 0 && contentLength != -1) { responseHeaders.put("ETag", "W/\"" + contentLength + '-' + lastModified + '"'); } } catch (IOException ioe) { if (LOGGER.isLoggable(FINEST)) { LOGGER.log(FINEST, "Closing stream", ioe); } } finally { if (in != null) { try { in.close(); } catch (IOException ioe) { if (LOGGER.isLoggable(FINEST)) { LOGGER.log(FINEST, "Closing stream", ioe); } } } } return responseHeaders; } else { return emptyMap(); } } /** * @see javax.faces.application.Resource#getRequestPath() */ @Override public String getRequestPath() { FacesContext context = FacesContext.getCurrentInstance(); String facesServletMapping = getFacesMapping(context); String uri = null; // Check for exact mapping first if (isExactMapped(facesServletMapping)) { String resource = RESOURCE_IDENTIFIER + '/' + getResourceName(); // Check if the FacesServlet is exact mapped to the resource if (isResourceExactMappedToFacesServlet(context.getExternalContext(), resource)) { uri = facesServletMapping + resource; } else { // No exact mapping for the requested resource, see if Facelets servlet is mapped to // e.g. /faces/* or *.xhtml and take that mapping String mapping = getFirstWildCardMappingToFacesServlet(context.getExternalContext()); if (mapping == null) { // If there are only exact mappings and the resource is not exact mapped, // we can't serve this resource throw new IllegalStateException( "No suitable mapping for FacesServlet found. To serve resources " + "FacesServlet should have at least one prefix or suffix mapping." ); } facesServletMapping = mapping.replace("*", ""); } } if (uri == null) { // If it is extension mapped if (isPrefixMapped(facesServletMapping)) { uri = facesServletMapping + RESOURCE_IDENTIFIER + '/' + getResourceName(); } else { uri = RESOURCE_IDENTIFIER + '/' + getResourceName() + facesServletMapping; } } boolean queryStarted = false; if (getLibraryName() != null) { queryStarted = true; uri += "?ln=" + getLibraryName(); } String version = ""; initResourceInfo(); if (resourceInfo.getLibraryInfo() != null && resourceInfo.getLibraryInfo().getVersion() != null) { version += resourceInfo.getLibraryInfo().getVersion().toString(); } if (resourceInfo.getVersion() != null) { version += resourceInfo.getVersion().toString(); } if (version.length() > 0) { uri += ((queryStarted) ? "&v=" : "?v=") + version; queryStarted = true; } String localePrefix = resourceInfo.getLocalePrefix(); if (localePrefix != null) { uri += ((queryStarted) ? "&loc=" : "?loc=") + localePrefix; queryStarted = true; } String contract = resourceInfo.getContract(); if (contract != null) { uri += ((queryStarted) ? "&con=" : "?con=") + contract; queryStarted = true; } if (JSF_SCRIPT_RESOURCE_NAME.equals(getResourceName()) && JSF_SCRIPT_LIBRARY_NAME.equals(getLibraryName())) { ProjectStage stage = context.getApplication().getProjectStage(); switch (stage) { case Development: uri += ((queryStarted) ? "&stage=Development" : "?stage=Development" ); break; case SystemTest: uri += ((queryStarted) ? "&stage=SystemTest" : "?stage=SystemTest" ); break; case UnitTest: uri += ((queryStarted) ? "&stage=UnitTest" : "?stage=UnitTest" ); break; default: assert(stage.equals(Production)); } } uri = context.getApplication().getViewHandler() .getResourceURL(context, uri); return uri; } /** * @see javax.faces.application.Resource#userAgentNeedsUpdate(javax.faces.context.FacesContext) */ @Override public boolean userAgentNeedsUpdate(FacesContext context) { // PENDING(edburns): this is a sub-optimal implementation choice // done in the interest of prototyping. It's never a good idea // to do a switch statement based on the type of an object. if (resourceInfo instanceof FaceletResourceInfo) { return true; } // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html // 14.25 If-Modified-Since // if the requested variant has not been modified since the time // specified in this field, an entity will not be returned from the // server; instead, a 304 (not modified) response will be returned // without any message-body. // A date which is later than the server's current time is // invalid. Map requestHeaders = context.getExternalContext().getRequestHeaderMap(); if (requestHeaders.containsKey(IF_MODIFIED_SINCE)) { initResourceInfo(); /* * Make sure that we strip the milliseconds out of what comes back * from the getLastModified call for a resource as the * 'If-Modified-Since' header does not use milliseconds. */ long lastModifiedOfResource = (((ClientResourceInfo)resourceInfo).getLastModified(context) / 1000) * 1000; long lastModifiedHeader = getIfModifiedHeader(context.getExternalContext()); if (0 == lastModifiedOfResource) { long startupTime = ApplicationAssociate.getInstance(context.getExternalContext()).getTimeOfInstantiation(); return startupTime > lastModifiedHeader; } else { return lastModifiedOfResource > lastModifiedHeader; } } return true; } // --------------------------------------------------------- Private Methods /* * This method should only be called if the 'If-Modified-Since' header * is present in the request header map. */ private long getIfModifiedHeader(ExternalContext extcontext) { Object request = extcontext.getRequest(); if (request instanceof HttpServletRequest) { // try to use the container where we can. V3 for instance // has a FastHttpDateFormat format/parse implementation // which is more than likely more performant than SimpleDateFormat // (otherwise, why would it be there?). return ((HttpServletRequest) request).getDateHeader(IF_MODIFIED_SINCE); } else { SimpleDateFormat format = new SimpleDateFormat(RFC1123_DATE_PATTERN, Locale.US); try { Date ifModifiedSinceDate = format.parse(extcontext.getRequestHeaderMap().get(IF_MODIFIED_SINCE)); return ifModifiedSinceDate.getTime(); } catch (ParseException ex) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, "jsf.application.resource.invalid_if_modified_since_header", new Object[]{ extcontext.getRequestHeaderMap().get(IF_MODIFIED_SINCE) }); if (ex != null) { LOGGER.log(Level.WARNING, "", ex); } } return -1; } } } // --------------------------------------------- Methods from Externalizable @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(getResourceName()); out.writeObject(getLibraryName()); out.writeObject(getContentType()); out.writeLong(initialTime); out.writeLong(maxAge); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { setResourceName((String) in.readObject()); setLibraryName((String) in.readObject()); setContentType((String) in.readObject()); initialTime = in.readLong(); maxAge = in.readLong(); } private void initResourceInfo(){ if (resourceInfo != null) { return; } ResourceManager manager = ApplicationAssociate.getInstance(FacesContext.getCurrentInstance().getExternalContext()).getResourceManager(); resourceInfo = manager.findResource(getLibraryName(), getResourceName(), getContentType(), FacesContext.getCurrentInstance()); } // --------------------------------------------------------- Private Methods private boolean isResourceRequest() { FacesContext ctx = FacesContext.getCurrentInstance(); return ctx.getApplication().getResourceHandler().isResourceRequest(ctx); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy