import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.DocumentException; import org.dom4j.io.SAXReader; import org.dom4j.io.SAXWriter; import org.springframework.core.io.Resource; import org.xml.sax.SAXException; /** * Filter implementation which preprocesses JSP files in XML using an XSL. * This is how you should set it up in web.xml: *
* <filter-mapping> * <filter-name>jspPreprocessFilter</filter-name> * <url-pattern>/*</url-pattern> * <dispatcher>INCLUDE</dispatcher> * <dispatcher>REQUEST</dispatcher> * <dispatcher>FORWARD</dispatcher> * <dispatcher>ERROR</dispatcher> * </filter-mapping> ** * @author Niklas Therning */ public class JspPreprocessFilter implements Filter { private static final Log log = LogFactory.getLog(JspPreprocessFilter.class); private final Resource stylesheetResource; private final SAXTransformerFactory transformerFactory; private String sourceFileExtension = ".jsp"; private String backupFileExtension = ".jsp.bak"; /** * Creates a new instance. * * @param transformerFactory the factory which will be used to create the * {@link TransformerHandler} which will receive the SAX events * from the parsing of the data from the response.. * @param stylesheetResource the XSL stylesheet file. */ public JspPreprocessFilter(SAXTransformerFactory transformerFactory, Resource stylesheetResource) { this.transformerFactory = org.apache.xalan.xsltc.trax.SmartTransformerFactoryImpl(); this.stylesheetResource = stylesheetResource; } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; ServletContext context = request.getSession().getServletContext(); String srcPath = (String) request.getAttribute("javax.servlet.include.servlet_path"); if (srcPath == null) { srcPath = request.getServletPath(); } if (!srcPath.endsWith(sourceFileExtension)) { chain.doFilter(req, res); return; } String realSrcPath = context.getRealPath(srcPath); if (realSrcPath == null) { throw new FileNotFoundException("Could not get real path for " + "source JSP file '" + srcPath + "'"); } File src = new File(realSrcPath); if (!src.exists()) { chain.doFilter(req, res); return; } String backupPath = StringUtils.substringBeforeLast(srcPath, sourceFileExtension) + backupFileExtension; String realBackupPath = context.getRealPath(backupPath); if (realBackupPath == null) { throw new FileNotFoundException("Could not get real path for " + "backup JSP file '" + backupPath + "'"); } File backup = new File(realBackupPath); synchronized (this) { if (!backup.exists() || src.lastModified() > backup.lastModified()) { src.renameTo(backup); File dest = new File(realSrcPath);; src = backup; InputStream in = null; OutputStream out = null; try { if (log.isDebugEnabled()) { log.debug("Preprocessing JSP file '" + realSrcPath + "'. Saving result to '" + realBackupPath + '"'); } in = new BufferedInputStream(new FileInputStream(src)); out = new BufferedOutputStream(new FileOutputStream(dest)); preprocess(in, out); } catch (TransformerConfigurationException tce) { throw new ServletException(tce); } catch (DocumentException de) { throw new ServletException(de); } catch (SAXException de) { throw new ServletException(de); } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(out); } /* * Make sure backup's lastModifiedTime is greater than the * preprocessed JSP's. */ backup.setLastModified(System.currentTimeMillis()); } else { if (log.isDebugEnabled()) { if (log.isDebugEnabled()) { log.debug("JSP file '" + realSrcPath + "' hasn't been modified " + "since last check. Skipping preprocessing."); } } } } chain.doFilter(request, res); } private void preprocess(InputStream in, OutputStream out) throws DocumentException, TransformerConfigurationException, IOException, SAXException { SAXWriter writer = new SAXWriter(); TransformerHandler handler = transformerFactory.newTransformerHandler(getTemplates()); handler.setResult(new StreamResult(out)); writer.setContentHandler(handler); writer.write(new SAXReader().read(in)); } private Templates getTemplates() { InputStream is = null; try { is = stylesheetResource.getInputStream(); StreamSource src = new StreamSource(is); return transformerFactory.newTemplates(src); } catch (IOException ie) { throw new RuntimeException(ie); } catch (TransformerConfigurationException tce) { throw new RuntimeException(tce); } finally { IOUtils.closeQuietly(is); } } public void init(FilterConfig filterConfig) throws ServletException {} public void destroy() {} }