/* vim: set ts=2 et sw=2 cindent fo=qroca: */
package com.globant.katari.core.web;
import java.io.IOException;
import java.io.InputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Map;
import java.util.HashMap;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** A servlet that serves static content (gif, png, css, etc) from the
* classpath.
*
* It only serves content that matches one of the specified mime types.
*
* In addition to the parameters for the base class, this servlet accepts the
* following configuration parameters:<br>
*
* staticContentPath: the base classpath fragment that is prepended to the
* static content name. It must be specified or the servlet throws an
* exception. This parameter must not start with '/'.<br>
*
* requestCacheContent: whether to send the cache headers to the client with an
* expiration date one month in the future, or the headers that state that the
* content should not be cached (true / false). It is false by default.<br>
*
* mimeType_[extension]: the mime type for the corresponding extension, for
* example mymeType_gif = "image/gif".<br>
*
* debug: whether to enable debug mode or not. In debug mode, the servlet
* attempts to load the requested content directly from the file system. This
* makes it possible to edit the resources directly from disk and see the
* results immediately without a redeploy. It is false by default.<br>
*
* debugPrefix: in debug mode, a prefix that is prepended to the
* staticContentPath to search for the resource as a File. A typical value
* would be something like ../katari-style/src/main/resources. This is useful
* in a project with the standard maven layout where the webapp is a sibling of
* the module that contains the static resources.<br>
*/
public class StaticContentServlet extends BaseStaticContentServlet {
/** The serialization version number.
*
* This number must change every time a new serialization incompatible change
* is introduced in the class.
*/
private static final long serialVersionUID = 1;
/** The class logger.
*/
private static Logger log = LoggerFactory.getLogger(
StaticContentServlet.class);
/** Store the path prefix to use with static resources.
*/
private String pathPrefix = "";
/** The map of resource extensions to the corresponding mime type.
*
* It contains entries of the form "gif", "image/gif", This is never null.
*/
private Map<String, String> mimeTypes = new HashMap<String, String>();
/** A prefix to use to find the resources in the disk as a file.
*
* It is initialized from debugPrefix servlet parameter. It is never null.
*/
private String debugPrefix = ".";
/** Initializes the servlet.
*
* It sets the default packages for static resources.
*
* @throws ServletException in case of error.
*
* @param config The servlet configuration
*/
public void init(final ServletConfig config) throws ServletException {
log.trace("Entering init");
super.init(config);
String pathPrefixValue = config.getInitParameter("staticContentPath");
if (pathPrefixValue == null) {
throw new ServletException("You must specify staticContentPath");
}
pathPrefix = pathPrefixValue.trim();
if (pathPrefix.startsWith("/")) {
throw new ServletException(pathPrefixValue + " should not start with /");
}
String debugPrefixValue = config.getInitParameter("debugPrefix");
if (debugPrefixValue != null) {
debugPrefix = debugPrefixValue.trim();
}
initMimeTypes(config);
log.trace("Leaving init");
}
/** Initializes the mime types collection from the servlet init parameters.
*
* This method obtains the parameter names that start with mimeType_ and adds
* the corresponding mime type to the mime types collection.
*
* @param config The servlet config that contains the configuration
* parameters. It cannot be null.
*/
private void initMimeTypes(final ServletConfig config) {
log.trace("Entering initMimeTypes");
Validate.notNull(config, "The servlet config cannot be null");
Enumeration<?> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
String name = (String) parameterNames.nextElement();
log.debug("Parameter: {}", name);
if (name.startsWith("mimeType_")) {
String extension = name.substring("mimeType_".length());
log.debug("Mapped extension {} to mime {}", extension,
config.getInitParameter(name));
mimeTypes.put(extension, config.getInitParameter(name));
}
}
log.trace("Leaving initMimeTypes");
}
/** {@inheritDoc}
*/
@Override
protected String getContentType(final String name) {
log.trace("Entering getContentType");
int dotPosition = name.lastIndexOf('.');
if (dotPosition != -1) {
String contentType = mimeTypes.get(name.substring(dotPosition + 1));
log.trace("Leaving getContentType with {}", contentType);
return contentType;
} else {
log.trace("Leaving getContentType with null");
return null;
}
}
/** {@inheritDoc}
*/
@Override
protected InputStream findInputStream(final String name) throws IOException {
log.trace("Entering findInputStream('{}')", name);
Validate.notNull(name, "The resource name cannot be null");
String resourcePath = buildPath(pathPrefix, name);
if (isInDebugMode()) {
String filePath = buildPath(debugPrefix, resourcePath);
log.debug("In debug mode, looking for file {}", filePath);
File file = new File(filePath);
if (file.exists()) {
log.trace("Leaving findInputStream with a file resource");
return new FileInputStream(file);
}
}
log.debug("Looking for resource {}", resourcePath);
InputStream result = getResourceAsStream(resourcePath);
log.trace("Leaving findInputStream");
return result;
}
}