/*
* Carrot2 project.
*
* Copyright (C) 2002-2016, Dawid Weiss, Stanisław Osiński.
* All rights reserved.
*
* Refer to the full license file "carrot2.LICENSE"
* in the root folder of the repository checkout or at:
* http://www.carrot2.org/carrot2.LICENSE
*/
package org.carrot2.util.xsltfilter;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.carrot2.util.xslt.TemplatesPool;
/**
* A servlet filter applying XSLT stylesheets to the result of a request. The filter is
* activated when the content has a MIME type equal to <code>text/xml</code> AND:
* <ol>
* <li>the content has a correct <code>ext-stylesheet</code> directive (custom extension) OR,</li>
* <li>the content has a correct <code>xml-stylesheet</code> directive,</li>
* <li>processing has not been suppressed by setting
* {@link XSLTFilterConstants#NO_XSLT_PROCESSING} in the request context.</li>
* </ol>
* Filter configuration is given through the web application descriptor file (<code>web.xml</code>).
*
* <p>Example configuration using <code>ext-stylesheet</code>:
* <pre>
* <?ext-stylesheet resource="WEB-INF/stylesheets/stylesheet.xsl" ?>
* </pre>
*
* <p>Example configuration using <code>xml-stylesheet</code> (note the URL here is servlet-container
* relative, not application-context relative):
* <pre>
* <?xml-stylesheet type="text/xsl" href="/stylesheets/stylesheet.xsl" ?>
* </pre>
*/
public final class XSLTFilter implements Filter
{
private final static Logger logger = org.slf4j.LoggerFactory.getLogger(XSLTFilter.class);
/**
* Init parameter for the filter: if <code>true</code>, the parsed XSLT stylesheets
* will be cached and reused. This is useful to speedup your application, but annoying
* during development, so you can set this to <code>false</code> when you want the
* stylesheet reloaded for each request. Default value: <code>true</code>
*/
private final static String PARAM_TEMPLATE_CACHING = "template.caching";
/**
* A pool of cached stylesheets.
*/
private TemplatesPool pool;
/**
* Servlet context of this filter.
*/
private ServletContext context;
/**
* Place this filter into service.
*
* @param filterConfig The filter configuration object
*/
public void init(FilterConfig filterConfig) throws ServletException
{
if (filterConfig == null)
{
throw new IllegalArgumentException("FilterConfig must not be null.");
}
this.context = filterConfig.getServletContext();
final boolean templateCaching = getBooleanInit(filterConfig, PARAM_TEMPLATE_CACHING, true);
try
{
pool = new TemplatesPool(templateCaching);
}
catch (Exception e)
{
final String message = "Could not initialize XSLT transformers pool.";
logger.error(message, e);
throw new ServletException(message, e);
}
}
/**
* Returns init parameter or the default value.
*/
private boolean getBooleanInit(FilterConfig config, String name, boolean defaultValue)
{
if (config.getInitParameter(name) != null)
{
return Boolean.valueOf(config.getInitParameter(name)).booleanValue();
}
else return defaultValue;
}
/**
* Take this filter out of service.
*/
public void destroy()
{
this.pool = null;
}
/**
* Apply the XSLT stylesheet to the response and pass the result to the next filter.
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
final HttpServletRequest httpRequest = (HttpServletRequest) request;
final HttpServletResponse httpResponse = (HttpServletResponse) response;
// Generate the stream and process it with the stylesheet.
final XSLTFilterServletResponse wrappedResponse = new XSLTFilterServletResponse(
httpResponse, httpRequest, context, pool);
try
{
chain.doFilter(httpRequest, wrappedResponse);
}
catch (IOException t)
{
logger.info("I/O exception (doFilter): " + t.toString());
}
catch (Throwable t)
{
wrappedResponse.filterError("An unhandled exception occurred.", t);
}
finally
{
try
{
wrappedResponse.finishResponse();
}
catch (IOException e)
{
logger.info("I/O exception (finishResponse): " + e.toString());
}
}
}
}