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

org.omnifaces.resourcehandler.UnmappedResourceHandler Maven / Gradle / Ivy

There is a newer version: 4.4.1
Show newest version
/*
 * Copyright OmniFaces
 *
 * 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
 *
 *     https://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.omnifaces.resourcehandler;

import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
import static org.omnifaces.util.Faces.getMapping;
import static org.omnifaces.util.Faces.getRequestContextPath;
import static org.omnifaces.util.Faces.isPrefixMapping;
import static org.omnifaces.util.FacesLocal.getRequestURI;
import static org.omnifaces.util.Utils.stream;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map.Entry;

import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.webapp.FacesServlet;

import org.omnifaces.util.Hacks;

/**
 * 

* This {@link ResourceHandler} implementation allows the developer to map JSF resources on an URL pattern of * /javax.faces.resource/* (basically, the value of {@link ResourceHandler#RESOURCE_IDENTIFIER}) without * the need for an additional {@link FacesServlet} prefix or suffix URL pattern in the default produced resource URLs, * such as /javax.faces.resource/faces/css/style.css or * /javax.faces.resource/css/style.css.xhtml. This resource handler will produce unmapped URLs like * /javax.faces.resource/css/style.css. This has the major advantage that the developer don't need the * #{resource} EL expression anymore in order to properly reference relative URLs to images in CSS files. *

* So, given the following folder structure, *

 * WebContent
 *  `-- resources
 *       `-- css
 *            |-- images
 *            |    `-- background.png
 *            `-- style.css
 * 
*

And the following CSS file reference (note: the library is not supported by the * UnmappedResourceHandler! this is a technical limitation, just exclusively use name): *

 * <h:outputStylesheet name="css/style.css" />
 * 
*

you can in css/style.css just use: *

 * body {
 *     background: url("images/background.png");
 * }
 * 
*

instead of *

 * body {
 *     background: url("#{resource['css/images/background.png']}");
 * }
 * 
*

* This has in turn the advantage that you don't need to modify the background image or font face URLs in CSS files from * 3rd party libraries such as Twitter Bootstrap, FontAwesome, etcetera. * *

Installation

*

* To get it to run, this handler needs be registered as follows in faces-config.xml: *

 * <application>
 *     <resource-handler>org.omnifaces.resourcehandler.UnmappedResourceHandler</resource-handler>
 * </application>
 * 
*

* And the {@link FacesServlet} needs to have an additional mapping /javax.faces.resource/* in * web.xml. You can just add it as a new <url-pattern> entry to the existing mapping * of the {@link FacesServlet}. For example, assuming that you've already a mapping on *.xhtml: *

 * <servlet-mapping>
 *     ...
 *     <url-pattern>*.xhtml</url-pattern>
 *     <url-pattern>/javax.faces.resource/*</url-pattern>
 * </servlet-mapping>
 * 
* *

CombinedResourceHandler

*

* If you're also using the {@link CombinedResourceHandler} or any other custom resource handler, then you need to * ensure that this is in faces-config.xml declared before the * UnmappedResourceHandler. Thus, like so: *

 * <application>
 *     <resource-handler>org.omnifaces.resourcehandler.CombinedResourceHandler</resource-handler>
 *     <resource-handler>org.omnifaces.resourcehandler.UnmappedResourceHandler</resource-handler>
 * </application>
 * 
*

* Otherwise the combined resource handler will still produce mapped URLs. In essence, the one which is later * registered wraps the previously registered one. * * @author Bauke Scholtz * @since 1.4 * @see RemappedResource * @see DefaultResourceHandler */ public class UnmappedResourceHandler extends DefaultResourceHandler { // Constructors --------------------------------------------------------------------------------------------------- /** * Creates a new instance of this unmapped resource handler which wraps the given resource handler. * @param wrapped The resource handler to be wrapped. */ public UnmappedResourceHandler(ResourceHandler wrapped) { super(wrapped); } // Actions -------------------------------------------------------------------------------------------------------- /** * If the given resource is not null, then decorate it as an unmapped resource. */ @Override public Resource decorateResource(Resource resource) { if (resource == null) { return resource; } String path = resource.getRequestPath(); return isResourceRequest(path) ? new RemappedResource(resource, unmapRequestPath(path)) : resource; } @Override public boolean isResourceRequest(FacesContext context) { return isResourceRequest(getRequestURI(context)) || super.isResourceRequest(context); } @Override public void handleResourceRequest(FacesContext context) throws IOException { Resource resource = createResource(context); if (resource == null) { super.handleResourceRequest(context); return; } ExternalContext externalContext = context.getExternalContext(); if (!resource.userAgentNeedsUpdate(context)) { externalContext.setResponseStatus(SC_NOT_MODIFIED); return; } InputStream inputStream = resource.getInputStream(); if (inputStream == null) { externalContext.setResponseStatus(SC_NOT_FOUND); return; } externalContext.setResponseContentType(resource.getContentType()); for (Entry header : resource.getResponseHeaders().entrySet()) { externalContext.setResponseHeader(header.getKey(), header.getValue()); } stream(inputStream, externalContext.getResponseOutputStream()); } // Helpers -------------------------------------------------------------------------------------------------------- private static boolean isResourceRequest(String path) { return path.startsWith(getRequestContextPath() + RESOURCE_IDENTIFIER); } private static String unmapRequestPath(String path) { String mapping = getMapping(); if (isPrefixMapping(mapping)) { return path.replaceFirst(mapping, ""); } else if (path.contains("?")) { return path.replace(mapping + "?", "?"); } else if (path.endsWith(mapping)) { return path.substring(0, path.length() - mapping.length()); } else { return path; } } private static Resource createResource(FacesContext context) { if (Hacks.isPrimeFacesDynamicResourceRequest(context)) { return null; } String pathInfo = context.getExternalContext().getRequestPathInfo(); String resourceName = (pathInfo != null) ? pathInfo.substring(1) : ""; if (resourceName.isEmpty()) { return null; } String libraryName = context.getExternalContext().getRequestParameterMap().get("ln"); return context.getApplication().getResourceHandler().createResource(resourceName, libraryName); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy