Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.jawr.web.servlet.JawrBinaryResourceRequestHandler Maven / Gradle / Ivy
Go to download
Javascript/CSS bundling and compressing tool for java web apps.
By using jawr resources are automatically bundled together and optionally minified and gzipped.
Jawr provides tag libraries to reference a generated bundle either by id or by using the name of any of its members.
/**
* Copyright 2009-2016 Ibrahim Chaehoi
*
* 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 net.jawr.web.servlet;
import static net.jawr.web.JawrConstant.URL_SEPARATOR;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.jawr.web.JawrConstant;
import net.jawr.web.context.ThreadLocalJawrContext;
import net.jawr.web.exception.InvalidPathException;
import net.jawr.web.exception.ResourceNotFoundException;
import net.jawr.web.resource.BinaryResourcesHandler;
import net.jawr.web.resource.bundle.CheckSumUtils;
import net.jawr.web.resource.bundle.IOUtils;
import net.jawr.web.resource.bundle.factory.PropertiesBundleConstant;
import net.jawr.web.resource.bundle.factory.util.PathNormalizer;
import net.jawr.web.resource.bundle.factory.util.PropertiesConfigHelper;
import net.jawr.web.resource.bundle.generator.GeneratorRegistry;
import net.jawr.web.resource.bundle.handler.BundleHashcodeType;
import net.jawr.web.resource.handler.bundle.ResourceBundleHandler;
import net.jawr.web.servlet.util.ClientAbortExceptionResolver;
import net.jawr.web.servlet.util.MIMETypesSupport;
import net.jawr.web.util.StopWatch;
/**
* This class defines the request handler for binary web resources (images,
* font, ...). Jawr binary resource servlet delegates to this class to handle
* requests.
*
* @author Ibrahim Chaehoi
*/
public class JawrBinaryResourceRequestHandler extends JawrRequestHandler {
/** The serial version UID */
private static final long serialVersionUID = -8342090032443416738L;
/** The logger */
private static final Logger LOGGER = LoggerFactory.getLogger(JawrBinaryResourceRequestHandler.class);
/** The performance processing logger */
private static final Logger PERF_PROCESSING_LOGGER = LoggerFactory.getLogger(JawrConstant.PERF_PROCESSING_LOGGER);
/** The resource handler */
private ResourceBundleHandler rsBundleHandler;
/** The binary web resource handler */
private BinaryResourcesHandler binaryRsHandler;
/** The bundle mapping */
private Properties bundleMapping;
/**
* The binary resource MIME map, associating the resource extension to their
* MIME type
*/
protected Map binaryMimeTypeMap;
/**
* Reads the properties file and initializes all configuration using the
* ServletConfig object. If applicable, a ConfigChangeListenerThread will be
* started to listen to changes in the properties configuration.
*
* @param context
* the servlet context
* @param config
* the servlet config
* @throws ServletException
*/
public JawrBinaryResourceRequestHandler(ServletContext context, ServletConfig config) throws ServletException {
super(context, config);
this.binaryMimeTypeMap = MIMETypesSupport.getSupportedProperties(this);
resourceType = JawrConstant.BINARY_TYPE;
}
/**
* Alternate constructor that does not need a ServletConfig object.
* Parameters normally read rom it are read from the initParams Map, and the
* configProps are used instead of reading a .properties file.
*
* @param context
* the servlet context
* @param initParams
* the init parameters
* @param configProps
* the config properties
*
* @throws ServletException
* if a servlet exception occurs
*/
public JawrBinaryResourceRequestHandler(ServletContext context, Map initParams,
Properties configProps) throws ServletException {
super(context, initParams, configProps);
this.binaryMimeTypeMap = MIMETypesSupport.getSupportedProperties(this);
resourceType = JawrConstant.BINARY_TYPE;
}
/**
* Initialize the Jawr config
*
* @param props
* the properties
* @throws ServletException
* if an exception occurs
*/
@Override
protected void initializeJawrConfig(Properties props) throws ServletException {
StopWatch stopWatch = new StopWatch("Jawr Processing for '" + resourceType + "' resource");
ThreadLocalJawrContext.setStopWatch(stopWatch);
stopWatch.start("Initialize jawr config for binary resource handler");
if (this.binaryMimeTypeMap == null) {
this.binaryMimeTypeMap = MIMETypesSupport.getSupportedProperties(this);
}
// init registry
generatorRegistry = new GeneratorRegistry(resourceType);
// Initialize config
if (null != jawrConfig) {
jawrConfig.invalidate();
}
jawrConfig = createJawrConfig(props);
jawrConfig.setContext(servletContext);
jawrConfig.setGeneratorRegistry(generatorRegistry);
// Set mapping, to be used by the tag lib to define URLs that point to
// this servlet.
String mapping = (String) initParameters.get(JawrConstant.SERVLET_MAPPING_PROPERTY_NAME);
if (null != mapping) {
jawrConfig.setServletMapping(mapping);
}
// Initialize the IllegalBundleRequest handler
initIllegalBundleRequestHandler();
// Initialize the resource handler
rsReaderHandler = initResourceReaderHandler();
rsBundleHandler = initResourceBundleHandler();
// Initialize custom generators
PropertiesConfigHelper propertiesHelper = new PropertiesConfigHelper(props, resourceType);
Iterator generators = propertiesHelper
.getCommonPropertyAsSet(PropertiesBundleConstant.CUSTOM_GENERATORS).iterator();
while (generators.hasNext()) {
String generatorClass = (String) generators.next();
generatorRegistry.registerGenerator(generatorClass);
}
if (jawrConfig.getUseBundleMapping()) {
bundleMapping = rsBundleHandler.getJawrBundleMapping();
} else {
bundleMapping = new Properties();
}
stopWatch.stop();
stopWatch.start("initialize mapping for binary resource handler");
binaryRsHandler = new BinaryResourcesHandler(jawrConfig, rsReaderHandler, rsBundleHandler);
initMapping(binaryRsHandler);
servletContext.setAttribute(JawrConstant.BINARY_CONTEXT_ATTRIBUTE, binaryRsHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Configuration read. Current config:");
LOGGER.debug(jawrConfig.toString());
}
stopWatch.stop();
stopWatch.start("initialize mapping for binary resource handler");
if (PERF_PROCESSING_LOGGER.isDebugEnabled()) {
PERF_PROCESSING_LOGGER.debug(stopWatch.prettyPrint());
}
// Warn when in debug mode
if (jawrConfig.isDebugModeOn()) {
LOGGER.warn(
"Jawr initialized in DEVELOPMENT MODE. Do NOT use this mode in production or integration servers. ");
}
stopWatch.stop();
if (PERF_PROCESSING_LOGGER.isDebugEnabled()) {
PERF_PROCESSING_LOGGER.debug(stopWatch.prettyPrint());
}
}
/**
* Initialize the mapping of the binary web resources handler
*
* @param binaryRsHandler
* the binary web resources handler
*/
private void initMapping(BinaryResourcesHandler binaryRsHandler) {
if (jawrConfig.getUseBundleMapping() && rsBundleHandler.isExistingMappingFile()) {
// Initialize the binary web resource mapping
Iterator> mapIterator = bundleMapping.entrySet().iterator();
while (mapIterator.hasNext()) {
Entry entry = mapIterator.next();
binaryRsHandler.addMapping((String) entry.getKey(), entry.getValue().toString());
}
} else {
// Create a resource handler to read files from the WAR archive or
// exploded dir.
String binaryResourcesDefinition = jawrConfig.getBinaryResourcesDefinition();
if (binaryResourcesDefinition != null) {
StringTokenizer tokenizer = new StringTokenizer(binaryResourcesDefinition, ",");
while (tokenizer.hasMoreTokens()) {
String pathMapping = tokenizer.nextToken();
// path is a generated image and ends with an image
// extension
if (generatorRegistry.isGeneratedBinaryResource(pathMapping)
&& hasBinaryFileExtension(pathMapping)) {
addBinaryResourcePath(binaryRsHandler, pathMapping);
}
// path ends in /, the folder is included without subfolders
else if (pathMapping.endsWith("/")) {
addItemsFromDir(binaryRsHandler, pathMapping, false);
}
// path ends in /, the folder is included with all
// subfolders
else if (pathMapping.endsWith("/**")) {
addItemsFromDir(binaryRsHandler, pathMapping.substring(0, pathMapping.lastIndexOf("**")), true);
} else if (hasBinaryFileExtension(pathMapping)) {
addBinaryResourcePath(binaryRsHandler, pathMapping);
} else
LOGGER.warn(
"Wrong mapping [" + pathMapping + "] for image bundle. Please check configuration. ");
}
}
}
// Store the bundle mapping
if (jawrConfig.getUseBundleMapping() && !rsBundleHandler.isExistingMappingFile()) {
rsBundleHandler.storeJawrBundleMapping(bundleMapping);
}
if (LOGGER.isDebugEnabled())
LOGGER.debug("Finish creation of map for image bundle");
}
/**
* Add an binary resource path to the binary map
*
* @param binRsHandler
* the image resources handler
* @param resourcePath
* the image path
*/
private void addBinaryResourcePath(BinaryResourcesHandler binRsHandler, String resourcePath) {
try {
String resultPath = CheckSumUtils.getCacheBustedUrl(resourcePath, rsReaderHandler, jawrConfig);
binRsHandler.addMapping(resourcePath, resultPath);
bundleMapping.put(resourcePath, resultPath);
} catch (IOException e) {
LOGGER.error("An exception occurs while defining the mapping for the file : " + resourcePath, e);
} catch (ResourceNotFoundException e) {
LOGGER.error("Impossible to define the checksum for the resource '" + resourcePath
+ "'. Unable to retrieve the content of the file.");
}
}
/**
* Returns true of the path contains a binary file extension
*
* @param path
* the path
* @return the binary file extension
*/
private boolean hasBinaryFileExtension(String path) {
boolean result = false;
int extFileIdx = path.lastIndexOf(".");
if (extFileIdx != -1 && extFileIdx + 1 < path.length()) {
String extension = path.substring(extFileIdx + 1);
result = binaryMimeTypeMap.containsKey(extension);
}
return result;
}
/**
* Adds all the resources within a path to the image map.
*
* @param binRsHandler
* the binary resources handler
* @param dirName
* the directory name
* @param addSubDirs
* boolean If subfolders will be included. In such case, every
* folder below the path is included.
*/
private void addItemsFromDir(BinaryResourcesHandler binRsHandler, String dirName, boolean addSubDirs) {
Set resources = rsReaderHandler.getResourceNames(dirName);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Adding " + resources.size() + " resources from path [" + dirName + "] to binary bundle");
}
GeneratorRegistry binGeneratorRegistry = binRsHandler.getConfig().getGeneratorRegistry();
List folders = new ArrayList<>();
boolean generatedPath = binGeneratorRegistry.isPathGenerated(dirName);
for (String resourceName : resources) {
String resourcePath = PathNormalizer.joinPaths(dirName, resourceName, generatedPath);
if (hasBinaryFileExtension(resourceName)) {
addBinaryResourcePath(binRsHandler, resourcePath);
if (LOGGER.isDebugEnabled())
LOGGER.debug("Added to item path list:" + PathNormalizer.asPath(resourcePath));
} else if (addSubDirs) {
try {
if (rsReaderHandler.isDirectory(resourcePath)) {
folders.add(resourceName);
}
} catch (InvalidPathException e) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Enable to define if the following resource is a directory : "
+ PathNormalizer.asPath(resourcePath));
}
}
}
// Add subfolders if requested. Subfolders are added last unless
// specified in sorting file.
if (addSubDirs) {
for (String folderName : folders) {
addItemsFromDir(binRsHandler, PathNormalizer.joinPaths(dirName, folderName), true);
}
}
}
/**
* Process the request
*
* @param requestedPath
* the requested path
* @param request
* the request
* @param response
* the response
* @param bundleHashcodeType
* the bundle hashcode type
* @throws IOException
* if an IOException occurs
*/
@Override
protected void processRequest(String requestedPath, HttpServletRequest request, HttpServletResponse response,
BundleHashcodeType bundleHashcodeType) throws IOException {
boolean responseHeaderWritten = false;
boolean validBundle = true;
if (!jawrConfig.isDebugModeOn() && jawrConfig.isStrictMode()
&& bundleHashcodeType.equals(BundleHashcodeType.INVALID_HASHCODE)) {
validBundle = false;
}
// If debug mode is off, check for If-Modified-Since and
// If-none-match headers and set response caching headers.
if (!this.jawrConfig.isDebugModeOn()) {
// If a browser checks for changes, always respond 'no changes'.
if (validBundle && (null != request.getHeader(IF_MODIFIED_SINCE_HEADER)
|| null != request.getHeader(IF_NONE_MATCH_HEADER))) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
if (LOGGER.isDebugEnabled())
LOGGER.debug("Returning 'not modified' header. ");
return;
}
if (validBundle) {
// Add caching headers
setResponseHeaders(response);
} else {
responseHeaderWritten = illegalBundleRequestHandler.writeResponseHeader(requestedPath, request,
response);
if (!responseHeaderWritten) {
// Add caching headers
setResponseHeaders(response);
}
}
}
// Returns the real file path
String filePath = getRealFilePath(requestedPath, bundleHashcodeType);
try {
if (isValidRequestedPath(filePath)
&& (validBundle || illegalBundleRequestHandler.canWriteContent(requestedPath, request))) {
// Set the content type
response.setContentType(getContentType(requestedPath, request));
writeContent(filePath, request, response);
} else {
if (!responseHeaderWritten) {
LOGGER.error("Unable to load the image for the request URI : " + request.getRequestURI());
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
} catch (EOFException eofex) {
LOGGER.info("Browser cut off response", eofex);
} catch (ResourceNotFoundException e) {
LOGGER.info("Unable to write resource " + request.getRequestURI(), e);
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} catch (IOException ex) {
LOGGER.error("Unable to write resource " + request.getRequestURI(), ex);
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
if (LOGGER.isDebugEnabled())
LOGGER.debug("request succesfully attended");
}
/*
* (non-Javadoc)
*
* @see
* net.jawr.web.servlet.JawrRequestHandler#isValidBundle(java.lang.String)
*/
@Override
protected BundleHashcodeType isValidBundle(String requestedPath) {
BundleHashcodeType bundleHashcodeType = BundleHashcodeType.VALID_HASHCODE;
if (!jawrConfig.isDebugModeOn()) {
bundleHashcodeType = binaryRsHandler.getBundleHashcodeType(requestedPath);
}
return bundleHashcodeType;
}
/*
* (non-Javadoc)
*
* @see
* net.jawr.web.servlet.JawrRequestHandler#copyRequestedContentToResponse
* (java.lang.String, javax.servlet.http.HttpServletResponse,
* java.lang.String)
*/
@Override
protected boolean copyRequestedContentToResponse(String requestedPath, HttpServletResponse response,
String contentType) throws IOException {
boolean copyDone = false;
if (isValidRequestedPath(requestedPath)) {
try {
writeContent(requestedPath, null, response);
copyDone = true;
} catch (ResourceNotFoundException e) {
// Nothing to do here
}
}
return copyDone;
}
/*
* (non-Javadoc)
*
* @see
* net.jawr.web.servlet.JawrRequestHandler#handleSpecificRequest(java.lang
* .String, javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
@Override
protected boolean handleSpecificRequest(String requestedPath, HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
boolean processed = false;
// Retrieve the file path
if (requestedPath == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
processed = true;
} else {
// Check the content type
if (getContentType(requestedPath, request) == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
processed = true;
}
}
return processed;
}
/**
* Returns the content type for the image
*
* @param filePath
* the image file path
* @param request
* the request
* @return the content type of the image
*/
@Override
protected String getContentType(String filePath, HttpServletRequest request) {
String requestUri = request.getRequestURI();
// Retrieve the extension
String extension = getExtension(filePath);
if (extension == null) {
LOGGER.info("No extension found for the request URI : " + requestUri);
return null;
}
String binContentType = (String) binaryMimeTypeMap.get(extension);
if (binContentType == null) {
LOGGER.info(
"No binary extension match the extension '" + extension + "' for the request URI : " + requestUri);
return null;
}
return binContentType;
}
/**
* Checks if the path is valid and can be accessed.
*
* @param requestedPath
* the requested path
* @return true if the path is valid and can be accessed.
*/
@Override
protected boolean isValidRequestedPath(String requestedPath) {
boolean result = true;
if (requestedPath.startsWith(JawrConstant.WEB_INF_DIR_PREFIX)
|| requestedPath.startsWith(JawrConstant.META_INF_DIR_PREFIX)) {
result = false;
}
return result;
}
/*
* (non-Javadoc)
*
* @see
* net.jawr.web.servlet.JawrRequestHandler#writeContent(java.lang.String,
* javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
@Override
protected void writeContent(String requestedPath, HttpServletRequest request, HttpServletResponse response)
throws IOException, ResourceNotFoundException {
String resourceName = requestedPath;
if (!jawrConfig.getGeneratorRegistry().isGeneratedBinaryResource(resourceName)
&& !resourceName.startsWith(URL_SEPARATOR)) {
resourceName = URL_SEPARATOR + resourceName;
}
try (InputStream is = rsReaderHandler.getResourceAsStream(resourceName);
OutputStream os = response.getOutputStream()) {
IOUtils.copy(is, os);
} catch (EOFException eofex) {
LOGGER.debug("Browser cut off response", eofex);
} catch (IOException e) {
if (ClientAbortExceptionResolver.isClientAbortException(e)) {
LOGGER.debug("Browser cut off response", e);
} else {
throw e;
}
}
}
/**
* Removes the cache buster
*
* @param fileName
* the file name
* @return the file name without the cache buster.
*/
private String getRealFilePath(String fileName, BundleHashcodeType bundleHashcodeType) {
String realFilePath = fileName;
if (bundleHashcodeType.equals(BundleHashcodeType.INVALID_HASHCODE)) {
int idx = realFilePath.indexOf(JawrConstant.URL_SEPARATOR, 1);
if (idx != -1) {
realFilePath = realFilePath.substring(idx);
}
} else {
String[] resourceInfo = PathNormalizer.extractBinaryResourceInfo(realFilePath);
realFilePath = resourceInfo[0];
}
return realFilePath;
}
}