/*
* Copyright 2004-2012 the original author or authors.
*
* 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.springframework.faces.webflow.context.portlet;
import java.io.IOException;
import java.util.Map;
import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.context.FacesContext;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.PortletRequest;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;
import org.springframework.core.Ordered;
import org.springframework.faces.webflow.FacesContextHelper;
import org.springframework.http.HttpStatus;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.portlet.HandlerAdapter;
import org.springframework.web.portlet.HandlerExecutionChain;
import org.springframework.web.portlet.HandlerMapping;
import org.springframework.web.portlet.ModelAndView;
import org.springframework.web.portlet.handler.PortletContentGenerator;
/**
* Handles a request by delegating to the JSF ResourceHandler, which serves web application and classpath resources such
* as images, CSS and JavaScript files from well-known locations.
*
* @since 2.4.0
* @author Phillip Webb
* @see ResourceHandler
*/
public class JsfResourceRequestHandler extends PortletContentGenerator implements HandlerAdapter, HandlerMapping, Ordered {
private static final String FACES_RESOURCE = "javax.faces.resource";
private static final String RESOURCE_EXCLUDES_DEFAULT = ".class .jsp .jspx .properties .xhtml .groovy";
private static final String RESOURCE_EXCLUDES_PARAM_NAME = "javax.faces.RESOURCE_EXCLUDES";
private int order = Ordered.HIGHEST_PRECEDENCE;
public HandlerExecutionChain getHandler(PortletRequest request) throws Exception {
if (request instanceof ResourceRequest && request.getParameter(FACES_RESOURCE) != null) {
return new HandlerExecutionChain(new JsfResourceRequest());
}
return null;
}
public boolean supports(Object handler) {
return handler instanceof JsfResourceRequest;
}
public ModelAndView handleResource(ResourceRequest request, ResourceResponse response, Object handler) throws IOException {
FacesContextHelper helper = new FacesContextHelper();
try {
FacesContext facesContext = helper.getFacesContext(getPortletContext(), request, response);
handleResourceRequest(facesContext, request, response);
} finally {
helper.releaseIfNecessary();
}
return null;
}
private void handleResourceRequest(FacesContext facesContext, ResourceRequest request, ResourceResponse response) throws IOException {
ResourceHandler resourceHandler = facesContext.getApplication().getResourceHandler();
String resourceName = request.getParameter(FACES_RESOURCE);
String libraryName = request.getParameter("ln");
int statusCodeNotFound = HttpStatus.NOT_FOUND.value();
if (isResourceExcluded(facesContext, resourceName)) {
response.setProperty(ResourceResponse.HTTP_STATUS_CODE, String.valueOf(statusCodeNotFound));
PortletResponseUtils.setStatusCodeForPluto(response, statusCodeNotFound);
return;
}
Resource resource = createResource(resourceHandler, resourceName, libraryName);
if (resource == null) {
response.setProperty(ResourceResponse.HTTP_STATUS_CODE, String.valueOf(statusCodeNotFound));
PortletResponseUtils.setStatusCodeForPluto(response, statusCodeNotFound);
return;
}
for (Map.Entry<String, String> entry : resource.getResponseHeaders().entrySet()) {
response.setProperty(entry.getKey(), entry.getValue());
}
response.setContentType(resource.getContentType());
FileCopyUtils.copy(resource.getInputStream(), response.getPortletOutputStream());
}
private boolean isResourceExcluded(FacesContext context, String resourceName) {
for (String resourceExclude : getResourceExcludes(context)) {
if (StringUtils.endsWithIgnoreCase(resourceName, resourceExclude)) {
return true;
}
}
return false;
}
private String[] getResourceExcludes(FacesContext context) {
String resourceExcludes = context.getExternalContext().getInitParameter(RESOURCE_EXCLUDES_PARAM_NAME);
if (resourceExcludes == null) {
resourceExcludes = RESOURCE_EXCLUDES_DEFAULT;
}
return StringUtils.tokenizeToStringArray(resourceExcludes, " ");
}
private Resource createResource(ResourceHandler resourceHandler, String resourceName, String libraryName) {
if (libraryName != null) {
return resourceHandler.createResource(resourceName, libraryName);
}
return resourceHandler.createResource(resourceName);
}
public void handleAction(ActionRequest request, ActionResponse response, Object handler) throws Exception {
}
public ModelAndView handleRender(RenderRequest request, RenderResponse response, Object handler) throws Exception {
return null;
}
public void handleEvent(EventRequest request, EventResponse response, Object handler) throws Exception {
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
private static class JsfResourceRequest {
}
}