org.omnifaces.resourcehandler.UnmappedResourceHandler Maven / Gradle / Ivy
/*
* Copyright 2013 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
*
* 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.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.isPrefixMapping;
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
. For example, assuming that you've already a mapping on *.xhtml
:
*
* <servlet-mapping>
* <servlet-name>facesServlet</servlet-name>
* <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 unmappedRequestPath = unmapRequestPath(resource.getRequestPath());
return new RemappedResource(resource, unmappedRequestPath);
}
/**
* Returns true
if {@link ExternalContext#getRequestServletPath()} equals
* {@link ResourceHandler#RESOURCE_IDENTIFIER}.
*/
@Override
public boolean isResourceRequest(FacesContext context) {
return RESOURCE_IDENTIFIER.equals(context.getExternalContext().getRequestServletPath());
}
@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 String unmapRequestPath(String path) {
String mapping = getMapping();
if (isPrefixMapping(mapping)) {
return path.replaceFirst(mapping, "");
}
else if (path.contains("?")) {
return path.replace(mapping + "?", "?");
}
else {
return path.substring(0, path.length() - mapping.length());
}
}
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);
}
}