/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.portletbridge.richfaces.application.resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.HashMap;
import java.util.Map;
import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.portlet.faces.BridgeUtil;
import javax.servlet.http.HttpServletResponse;
import org.jboss.portletbridge.application.resource.PortletResourceHandler;
import org.jboss.portletbridge.bridge.logger.BridgeLogger;
import org.jboss.portletbridge.bridge.logger.BridgeLogger.Level;
import org.jboss.portletbridge.bridge.logger.JULLoggerImpl;
/**
* @author <a href="http://community.jboss.org/people/kenfinni">Ken Finnigan</a>
*/
public class RichFacesPortletResourceHandler extends PortletResourceHandler {
private static final BridgeLogger logger = new JULLoggerImpl(RichFacesPortletResourceHandler.class.getName());
private final ResourceHandler wrapped;
public RichFacesPortletResourceHandler(ResourceHandler resourceHandler) {
super(resourceHandler);
wrapped = resourceHandler;
}
/**
* @see javax.faces.application.ResourceHandlerWrapper#getWrapped()
*/
@Override
public ResourceHandler getWrapped() {
return wrapped;
}
@Override
public void handleResourceRequest(FacesContext context) throws IOException {
if (BridgeUtil.isPortletRequest()) {
ExternalContext externalContext = context.getExternalContext();
String resourceName = externalContext.getRequestParameterMap().get(RESOURCE_IDENTIFIER.substring(1));
if (null != resourceName) {
String libraryId = externalContext.getRequestParameterMap().get(LIBRARY_ID);
ResourceHandler handler = context.getApplication().getResourceHandler();
Resource resource = handler.createResource(resourceName, libraryId);
if (null != resource) {
if (!isRichFacesPortletResource(resource)) {
resource = new RichFacesPortletResource(resource);
}
handleResourceRequest(context, resource);
} else {
send404(context, resourceName, libraryId);
}
} else {
send404(context, resourceName, null);
}
} else {
wrapped.handleResourceRequest(context);
}
}
protected void handleResourceRequest(FacesContext context, Resource resource) throws IOException {
if (resource.userAgentNeedsUpdate(context)) {
ExternalContext extContext = context.getExternalContext();
ReadableByteChannel resourceChannel = null;
WritableByteChannel out = null;
InputStream in = null;
int bufferSize = extContext.getResponseBufferSize();
if (0 == bufferSize) {
bufferSize = 2048;
}
ByteBuffer buf = ByteBuffer.allocate(bufferSize);
extContext.setResponseBufferSize(buf.capacity());
try {
in = resource.getInputStream();
if (in == null) {
send404(context, resource.getResourceName(), resource.getLibraryName());
return;
}
resourceChannel = Channels.newChannel(in);
String contentType = resource.getContentType();
if (contentType != null) {
extContext.setResponseContentType(resource.getContentType());
}
ByteArrayOutputStream byteArray = new ByteArrayOutputStream(bufferSize);
out = Channels.newChannel(byteArray);
extContext.setResponseBufferSize(buf.capacity());
handleHeaders(context, resource);
int size = 0;
for (int thisRead = resourceChannel.read(buf), totalWritten = 0; thisRead != -1; thisRead = resourceChannel
.read(buf)) {
buf.rewind();
buf.limit(thisRead);
do {
totalWritten += out.write(buf);
} while (totalWritten < size);
buf.clear();
size += thisRead;
}
// Fix RichFaces URLs in Resources
if (resource.getResourceName().indexOf(".css") > 0) {
String updatedCss = updateCssUrls(context, byteArray.toString());
size = updatedCss.length();
byteArray = new ByteArrayOutputStream(size);
byteArray.write(updatedCss.getBytes());
}
extContext.setResponseContentLength(size);
extContext.setResponseStatus(HttpServletResponse.SC_OK);
// Write out data
byteArray.writeTo(extContext.getResponseOutputStream());
byteArray.flush();
byteArray.close();
} finally {
if (null != in) {
in.close();
}
if (null != out) {
out.close();
}
if (null != resourceChannel) {
resourceChannel.close();
}
}
} else {
send304(context);
}
}
protected String updateCssUrls(FacesContext context, String cssContent) {
Map<String, String> urlCache = new HashMap<String, String>();
ResourceHandler handler = context.getApplication().getResourceHandler();
for (RichFacesUrlType rfUrlType : RichFacesUrlType.values()) {
int urlStart = cssContent.indexOf(rfUrlType.getPathPrefix());
while (urlStart > 0) {
int fileNamePosStart = urlStart + rfUrlType.getPathPrefix().length();
int period = cssContent.indexOf('.', fileNamePosStart);
if (period > 0) {
int extEnd = cssContent.indexOf(')', period + 1);
String relPath = cssContent.substring(urlStart, extEnd);
String imageUrl = urlCache.get(relPath);
if (null == imageUrl) {
String resourceName = cssContent.substring(fileNamePosStart, extEnd);
String libraryName = rfUrlType.getLibraryName();
Resource imageResource = handler.createResource(resourceName, libraryName);
if (!isRichFacesPortletResource(imageResource)) {
imageResource = new RichFacesPortletResource(imageResource);
}
if (null != imageResource) {
imageResource.setLibraryName(rfUrlType.getLibraryName());
imageUrl = imageResource.getRequestPath();
imageUrl = imageUrl.replaceAll(libraryName, rfUrlType.getToken());
urlCache.put(relPath, imageUrl);
} else {
// Shouldn't happen, but can when there are errors in resource mappings
imageUrl = relPath;
logger.log(Level.ERROR, "Unable to retrieve resource " + resourceName + " from library "
+ libraryName);
}
}
StringBuilder buf = new StringBuilder();
buf.append(cssContent.substring(0, urlStart));
buf.append(imageUrl);
buf.append(cssContent.substring(extEnd));
cssContent = buf.toString();
} else {
// Filename not found
logger.log(Level.WARNING, "Unable to retrieve resource referenced from css: " + cssContent.substring(fileNamePosStart, fileNamePosStart + 150));
}
urlStart = cssContent.indexOf(rfUrlType.getPathPrefix(), fileNamePosStart);
}
}
for (RichFacesUrlType rfUrlType : RichFacesUrlType.values()) {
cssContent = cssContent.replaceAll(rfUrlType.getToken(), rfUrlType.getLibraryName());
}
return cssContent;
}
protected boolean isRichFacesPortletResource(Resource res) {
if (null == res || res instanceof RichFacesPortletResource) {
return true;
} else {
return false;
}
}
protected enum RichFacesUrlType {
ONE("org.richfaces", "../../org.richfaces.images/", "rf-one"),
TWO("org.richfaces", "../../", "rf-two"),
THREE("org.richfaces.images", "../org.richfaces.images/", "rf-three"),
FOUR("org.richfaces.images", "org.richfaces.images/", "rf-four");
private String libraryName;
private String pathPrefix;
private String token;
private RichFacesUrlType(String libraryName, String pathPrefix, String token) {
this.libraryName = libraryName;
this.pathPrefix = pathPrefix;
this.token = token;
}
public String getLibraryName() {
return libraryName;
}
public String getPathPrefix() {
return pathPrefix;
}
public String getToken() {
return token;
}
}
}