markt 2005/04/23 03:22:37 Modified: catalina/src/conf web.xml catalina/src/share/org/apache/catalina/ssi ResponseIncludeWrapper.java SSICommand.java SSIConditional.java SSIConfig.java SSIEcho.java SSIExec.java SSIFlastmod.java SSIFsize.java SSIInclude.java SSIMediator.java SSIPrintenv.java SSIProcessor.java SSIServlet.java SSIServletExternalResolver.java SSISet.java webapps/docs changelog.xml ssi-howto.xml Log: Provide an ServletFilter implementation of Server Side Includes (SSI). This was submitted by David Becker under bug 33106. Revision Changes Path 1.57 +79 -0 jakarta-tomcat-catalina/catalina/src/conf/web.xml Index: web.xml =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/conf/web.xml,v retrieving revision 1.56 retrieving revision 1.57 diff -u -r1.56 -r1.57 --- web.xml 4 Apr 2005 20:57:02 -0000 1.56 +++ web.xml 23 Apr 2005 10:22:37 -0000 1.57 @@ -206,6 +206,9 @@ </servlet> + <!-- NOTE: An SSI Filter is also available as an alternative SSI --> + <!-- implementation. Use either the Servlet or the Filter but NOT both. --> + <!-- --> <!-- Server Side Includes processing servlet, which processes SSI --> <!-- directives in HTML pages consistent with similar support in web --> <!-- servers like Apache. Traditionally, this servlet is mapped to the --> @@ -362,6 +365,78 @@ --> + <!-- ================== Built In Filter Definitions ===================== --> + + <!-- NOTE: An SSI Servlet is also available as an alternative SSI --> + <!-- implementation. Use either the Servlet or the Filter but NOT both. --> + <!-- --> + <!-- Server Side Includes processing filter, which processes SSI --> + <!-- directives in HTML pages consistent with similar support in web --> + <!-- servers like Apache. Traditionally, this filter is mapped to the --> + <!-- URL pattern "*.shtml", though it can be mapped to "*" as it will --> + <!-- selectively enable/disable SSI processing based on mime types. The --> + <!-- contentType init param allows you to apply SSI processing to JSP --> + <!-- pages, javascript, or any other content you wish. This filter --> + <!-- supports the following initialization parameters (default values are --> + <!-- in square brackets): --> + <!-- --> + <!-- contentType A regex pattern that must be matched before --> + <!-- SSI processing is applied. --> + <!-- [text/x-server-parsed-html(;.*)?] --> + <!-- --> + <!-- debug Debugging detail level for messages logged --> + <!-- by this servlet. [0] --> + <!-- --> + <!-- expires The number of seconds before a page with SSI --> + <!-- directives will expire. [No default] --> + <!-- --> + <!-- isVirtualWebappRelative --> + <!-- Should "virtual" paths be interpreted as --> + <!-- relative to the context root, instead of --> + <!-- the server root? (0=false, 1=true) [0] --> + <!-- --> + <!-- --> + <!-- IMPORTANT: To use the SSI filter, you also need to rename the --> + <!-- $CATALINA_HOME/server/lib/servlets-ssi.renametojar file --> + <!-- to $CATALINA_HOME/server/lib/servlets-ssi.jar --> + +<!-- + <filter> + <filter-name>ssi</filter-name> + <filter-class> + org.apache.catalina.ssi.SSIFilter + </filter-class> + <init-param> + <param-name>contentType</param-name> + <param-value>text/x-server-parsed-html(;.*)?</param-value> + </init-param> + <init-param> + <param-name>debug</param-name> + <param-value>0</param-value> + </init-param> + <init-param> + <param-name>expires</param-name> + <param-value>666</param-value> + </init-param> + <init-param> + <param-name>isVirtualWebappRelative</param-name> + <param-value>0</param-value> + </init-param> + </filter> +--> + + + <!-- ==================== Built In Filter Mappings ====================== --> + + <!-- The mapping for the SSI Filter --> +<!-- + <filter-mapping> + <filter-name>ssi</filter-name> + <url-pattern>*.shtml</url-pattern> + </filter-mapping> +--> + + <!-- ==================== Default Session Configuration ================= --> <!-- You can set the default session timeout (in minutes) for all newly --> <!-- created sessions by modifying the value below. --> @@ -783,6 +858,10 @@ <mime-type>application/x-shar</mime-type> </mime-mapping> <mime-mapping> + <extension>shtml</extension> + <mime-type>text/x-server-parsed-html</mime-type> + </mime-mapping> + <mime-mapping> <extension>smf</extension> <mime-type>audio/x-midi</mime-type> </mime-mapping> 1.6 +135 -8 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/ResponseIncludeWrapper.java Index: ResponseIncludeWrapper.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/ResponseIncludeWrapper.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- ResponseIncludeWrapper.java 1 Sep 2004 18:33:33 -0000 1.5 +++ ResponseIncludeWrapper.java 23 Apr 2005 10:22:37 -0000 1.6 @@ -13,23 +13,40 @@ import java.io.IOException; import java.io.PrintWriter; + +import javax.servlet.ServletContext; import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; + +import org.apache.catalina.util.DateTool; /** * A HttpServletResponseWrapper, used from * <code>SSIServletExternalResolver</code> * * @author Bip Thelin + * @author David Becker * @version $Revision$, $Date$ */ public class ResponseIncludeWrapper extends HttpServletResponseWrapper { /** + * The names of some headers we want to capture. + */ + private static final String CONTENT_TYPE = "content-type"; + private static final String LAST_MODIFIED = "last-modified"; + protected long lastModified = -1; + private String contentType = null; + + /** * Our ServletOutputStream */ - protected ServletOutputStream originalServletOutputStream; + protected ServletOutputStream captureServletOutputStream; protected ServletOutputStream servletOutputStream; protected PrintWriter printWriter; + + private ServletContext context; + private HttpServletRequest request; /** @@ -41,10 +58,13 @@ * @param out * The ServletOutputStream' to use */ - public ResponseIncludeWrapper(HttpServletResponse res, - ServletOutputStream originalServletOutputStream) { - super(res); - this.originalServletOutputStream = originalServletOutputStream; + public ResponseIncludeWrapper(ServletContext context, + HttpServletRequest request, HttpServletResponse response, + ServletOutputStream captureServletOutputStream) { + super(response); + this.context = context; + this.request = request; + this.captureServletOutputStream = captureServletOutputStream; } @@ -74,7 +94,7 @@ public PrintWriter getWriter() throws java.io.IOException { if (servletOutputStream == null) { if (printWriter == null) { - printWriter = new PrintWriter(originalServletOutputStream); + printWriter = new PrintWriter(captureServletOutputStream); } return printWriter; } @@ -93,10 +113,117 @@ public ServletOutputStream getOutputStream() throws java.io.IOException { if (printWriter == null) { if (servletOutputStream == null) { - servletOutputStream = originalServletOutputStream; + servletOutputStream = captureServletOutputStream; } return servletOutputStream; } throw new IllegalStateException(); } + + + /** + * Returns the value of the <code>last-modified</code> header field. The + * result is the number of milliseconds since January 1, 1970 GMT. + * + * @return the date the resource referenced by this + * <code>ResponseIncludeWrapper</code> was last modified, or -1 if not + * known. + */ + public long getLastModified() { + if (lastModified == -1) { + // javadocs say to return -1 if date not known, if you want another + // default, put it here + return -1; + } + return lastModified; + } + + /** + * Sets the value of the <code>last-modified</code> header field. + * + * @param value The number of milliseconds since January 1, 1970 GMT. + */ + public void setLastModified(long lastModified) { + this.lastModified = lastModified; + ((HttpServletResponse) getResponse()).setDateHeader(LAST_MODIFIED, + lastModified); + } + + /** + * Returns the value of the <code>content-type</code> header field. + * + * @return the content type of the resource referenced by this + * <code>ResponseIncludeWrapper</code>, or <code>null</code> if not known. + */ + public String getContentType() { + if (contentType == null) { + String url = request.getRequestURI(); + String mime = context.getMimeType(url); + if (mime != null) + { + setContentType(mime); + } + else + { + // return a safe value + setContentType("application/x-octet-stream"); + } + } + return contentType; + } + + /** + * Sets the value of the <code>content-type</code> header field. + * + * @param mime a mime type + */ + public void setContentType(String mime) { + contentType = mime; + if (contentType != null) { + getResponse().setContentType(contentType); + } + } + + + public void addDateHeader(String name, long value) { + super.addDateHeader(name, value); + String lname = name.toLowerCase(); + if (lname.equals(LAST_MODIFIED)) { + lastModified = value; + } + } + + public void addHeader(String name, String value) { + super.addHeader(name, value); + String lname = name.toLowerCase(); + if (lname.equals(LAST_MODIFIED)) { + try { + lastModified = DateTool.rfc1123Format.parse(value).getTime(); + } catch (Throwable ignore) { } + } else if (lname.equals(CONTENT_TYPE)) { + contentType = value; + } + } + + public void setDateHeader(String name, long value) { + super.setDateHeader(name, value); + String lname = name.toLowerCase(); + if (lname.equals(LAST_MODIFIED)) { + lastModified = value; + } + } + + public void setHeader(String name, String value) { + super.setHeader(name, value); + String lname = name.toLowerCase(); + if (lname.equals(LAST_MODIFIED)) { + try { + lastModified = DateTool.rfc1123Format.parse(value).getTime(); + } catch (Throwable ignore) { } + } + else if (lname.equals(CONTENT_TYPE)) + { + contentType = value; + } + } } \ No newline at end of file 1.5 +4 -2 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSICommand.java Index: SSICommand.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSICommand.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- SSICommand.java 1 Sep 2004 18:33:33 -0000 1.4 +++ SSICommand.java 23 Apr 2005 10:22:37 -0000 1.5 @@ -18,6 +18,7 @@ * * @author Bip Thelin * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public interface SSICommand { @@ -34,10 +35,11 @@ * The parameter values * @param writer * the writer to output to + * @return the most current modified date resulting from any SSI commands * @throws SSIStopProcessingException * if SSI processing should be aborted */ - public void process(SSIMediator ssiMediator, String commandName, + public long process(SSIMediator ssiMediator, String commandName, String[] paramNames, String[] paramValues, PrintWriter writer) throws SSIStopProcessingException; } \ No newline at end of file 1.3 +11 -7 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIConditional.java Index: SSIConditional.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIConditional.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- SSIConditional.java 1 Sep 2004 18:33:33 -0000 1.2 +++ SSIConditional.java 23 Apr 2005 10:22:37 -0000 1.3 @@ -18,14 +18,17 @@ * * @version $Revision$ * @author Paul Speed + * @author David Becker */ public class SSIConditional implements SSICommand { /** * @see SSICommand */ - public void process(SSIMediator ssiMediator, String commandName, + public long process(SSIMediator ssiMediator, String commandName, String[] paramNames, String[] paramValues, PrintWriter writer) throws SSIStopProcessingException { + // Assume anything using conditionals was modified by it + long lastModified = System.currentTimeMillis(); // Retrieve the current state information SSIConditionalState state = ssiMediator.getConditionalState(); if ("if".equalsIgnoreCase(commandName)) { @@ -33,7 +36,7 @@ // except count it if (state.processConditionalCommandsOnly) { state.nestingCount++; - return; + return lastModified; } state.nestingCount = 0; // Evaluate the expression @@ -48,12 +51,12 @@ } else if ("elif".equalsIgnoreCase(commandName)) { // No need to even execute if we are nested in // a false branch - if (state.nestingCount > 0) return; + if (state.nestingCount > 0) return lastModified; // If a branch was already taken in this if block // then disable output and return if (state.branchTaken) { state.processConditionalCommandsOnly = true; - return; + return lastModified; } // Evaluate the expression if (evaluateArguments(paramNames, paramValues, ssiMediator)) { @@ -68,7 +71,7 @@ } else if ("else".equalsIgnoreCase(commandName)) { // No need to even execute if we are nested in // a false branch - if (state.nestingCount > 0) return; + if (state.nestingCount > 0) return lastModified; // If we've already taken another branch then // disable output otherwise enable it. state.processConditionalCommandsOnly = state.branchTaken; @@ -80,7 +83,7 @@ // one level on the nesting count if (state.nestingCount > 0) { state.nestingCount--; - return; + return lastModified; } // Turn output back on state.processConditionalCommandsOnly = false; @@ -93,6 +96,7 @@ //throw new SsiCommandException( "Not a conditional command:" + // cmdName ); } + return lastModified; } 1.5 +5 -2 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIConfig.java Index: SSIConfig.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIConfig.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- SSIConfig.java 1 Sep 2004 18:33:33 -0000 1.4 +++ SSIConfig.java 23 Apr 2005 10:22:37 -0000 1.5 @@ -18,13 +18,14 @@ * @author Bip Thelin * @author Paul Speed * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public final class SSIConfig implements SSICommand { /** * @see SSICommand */ - public void process(SSIMediator ssiMediator, String commandName, + public long process(SSIMediator ssiMediator, String commandName, String[] paramNames, String[] paramValues, PrintWriter writer) { for (int i = 0; i < paramNames.length; i++) { String paramName = paramNames[i]; @@ -46,5 +47,7 @@ writer.write(configErrMsg); } } + // Setting config options doesn't really change the page + return 0; } } \ No newline at end of file 1.4 +6 -2 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIEcho.java Index: SSIEcho.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIEcho.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- SSIEcho.java 1 Sep 2004 18:33:33 -0000 1.3 +++ SSIEcho.java 23 Apr 2005 10:22:37 -0000 1.4 @@ -18,6 +18,7 @@ * @author Bip Thelin * @author Paul Speed * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public class SSIEcho implements SSICommand { @@ -28,8 +29,9 @@ /** * @see SSICommand */ - public void process(SSIMediator ssiMediator, String commandName, + public long process(SSIMediator ssiMediator, String commandName, String[] paramNames, String[] paramValues, PrintWriter writer) { + long lastModified = 0; String encoding = DEFAULT_ENCODING; String errorMessage = ssiMediator.getConfigErrMsg(); for (int i = 0; i < paramNames.length; i++) { @@ -42,6 +44,7 @@ variableValue = MISSING_VARIABLE_VALUE; } writer.write(variableValue); + lastModified = System.currentTimeMillis(); } else if (paramName.equalsIgnoreCase("encoding")) { if (isValidEncoding(paramValue)) { encoding = paramValue; @@ -54,6 +57,7 @@ writer.write(errorMessage); } } + return lastModified; } 1.5 +9 -5 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIExec.java Index: SSIExec.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIExec.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- SSIExec.java 1 Sep 2004 18:33:33 -0000 1.4 +++ SSIExec.java 23 Apr 2005 10:22:37 -0000 1.5 @@ -23,6 +23,7 @@ * @author Amy Roh * @author Paul Speed * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public class SSIExec implements SSICommand { @@ -33,16 +34,17 @@ /** * @see SSICommand */ - public void process(SSIMediator ssiMediator, String commandName, + public long process(SSIMediator ssiMediator, String commandName, String[] paramNames, String[] paramValues, PrintWriter writer) { + long lastModified = 0; String configErrMsg = ssiMediator.getConfigErrMsg(); String paramName = paramNames[0]; String paramValue = paramValues[0]; String substitutedValue = ssiMediator.substituteVariables(paramValue); if (paramName.equalsIgnoreCase("cgi")) { - ssiInclude.process(ssiMediator, "include", - new String[]{"virtual"}, new String[]{substitutedValue}, - writer); + lastModified = ssiInclude.process(ssiMediator, "include", + new String[]{"virtual"}, new String[]{substitutedValue}, + writer); } else if (paramName.equalsIgnoreCase("cmd")) { boolean foundProgram = false; try { @@ -57,6 +59,7 @@ IOTools.flow(stdErrReader, writer, buf); IOTools.flow(stdOutReader, writer, buf); proc.waitFor(); + lastModified = System.currentTimeMillis(); } catch (InterruptedException e) { ssiMediator.log("Couldn't exec file: " + substitutedValue, e); writer.write(configErrMsg); @@ -68,5 +71,6 @@ ssiMediator.log("Couldn't exec file: " + substitutedValue, e); } } + return lastModified; } } \ No newline at end of file 1.5 +6 -3 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIFlastmod.java Index: SSIFlastmod.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIFlastmod.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- SSIFlastmod.java 1 Sep 2004 18:33:33 -0000 1.4 +++ SSIFlastmod.java 23 Apr 2005 10:22:37 -0000 1.5 @@ -22,14 +22,16 @@ * @author Bip Thelin * @author Paul Speed * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public final class SSIFlastmod implements SSICommand { /** * @see SSICommand */ - public void process(SSIMediator ssiMediator, String commandName, + public long process(SSIMediator ssiMediator, String commandName, String[] paramNames, String[] paramValues, PrintWriter writer) { + long lastModified = 0; String configErrMsg = ssiMediator.getConfigErrMsg(); StringBuffer buf = new StringBuffer(); for (int i = 0; i < paramNames.length; i++) { @@ -41,7 +43,7 @@ if (paramName.equalsIgnoreCase("file") || paramName.equalsIgnoreCase("virtual")) { boolean virtual = paramName.equalsIgnoreCase("virtual"); - long lastModified = ssiMediator.getFileLastModified( + lastModified = ssiMediator.getFileLastModified( substitutedValue, virtual); Date date = new Date(lastModified); String configTimeFmt = ssiMediator.getConfigTimeFmt(); @@ -58,6 +60,7 @@ writer.write(configErrMsg); } } + return lastModified; } 1.4 +7 -2 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIFsize.java Index: SSIFsize.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIFsize.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- SSIFsize.java 1 Sep 2004 18:33:33 -0000 1.3 +++ SSIFsize.java 23 Apr 2005 10:22:37 -0000 1.4 @@ -20,6 +20,7 @@ * @author Bip Thelin * @author Paul Speed * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public final class SSIFsize implements SSICommand { @@ -30,8 +31,9 @@ /** * @see SSICommand */ - public void process(SSIMediator ssiMediator, String commandName, + public long process(SSIMediator ssiMediator, String commandName, String[] paramNames, String[] paramValues, PrintWriter writer) { + long lastModified = 0; String configErrMsg = ssiMediator.getConfigErrMsg(); for (int i = 0; i < paramNames.length; i++) { String paramName = paramNames[i]; @@ -42,6 +44,8 @@ if (paramName.equalsIgnoreCase("file") || paramName.equalsIgnoreCase("virtual")) { boolean virtual = paramName.equalsIgnoreCase("virtual"); + lastModified = ssiMediator.getFileLastModified( + substitutedValue, virtual); long size = ssiMediator.getFileSize(substitutedValue, virtual); String configSizeFmt = ssiMediator.getConfigSizeFmt(); @@ -56,6 +60,7 @@ writer.write(configErrMsg); } } + return lastModified; } 1.5 +7 -2 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIInclude.java Index: SSIInclude.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIInclude.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- SSIInclude.java 1 Sep 2004 18:33:33 -0000 1.4 +++ SSIInclude.java 23 Apr 2005 10:22:37 -0000 1.5 @@ -19,14 +19,16 @@ * @author Bip Thelin * @author Paul Speed * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public final class SSIInclude implements SSICommand { /** * @see SSICommand */ - public void process(SSIMediator ssiMediator, String commandName, + public long process(SSIMediator ssiMediator, String commandName, String[] paramNames, String[] paramValues, PrintWriter writer) { + long lastModified = 0; String configErrMsg = ssiMediator.getConfigErrMsg(); for (int i = 0; i < paramNames.length; i++) { String paramName = paramNames[i]; @@ -37,6 +39,8 @@ if (paramName.equalsIgnoreCase("file") || paramName.equalsIgnoreCase("virtual")) { boolean virtual = paramName.equalsIgnoreCase("virtual"); + lastModified = ssiMediator.getFileLastModified( + substitutedValue, virtual); String text = ssiMediator.getFileText(substitutedValue, virtual); writer.write(text); @@ -51,5 +55,6 @@ writer.write(configErrMsg); } } + return lastModified; } } \ No newline at end of file 1.5 +5 -4 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIMediator.java Index: SSIMediator.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIMediator.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- SSIMediator.java 1 Sep 2004 18:33:33 -0000 1.4 +++ SSIMediator.java 23 Apr 2005 10:22:37 -0000 1.5 @@ -29,6 +29,7 @@ * @author Amy Roh * @author Paul Speed * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public class SSIMediator { @@ -41,7 +42,7 @@ protected String configSizeFmt = DEFAULT_CONFIG_SIZE_FMT; protected String className = getClass().getName(); protected SSIExternalResolver ssiExternalResolver; - protected Date lastModifiedDate; + protected long lastModifiedDate; protected int debug; protected Strftime strftime; protected SSIConditionalState conditionalState = new SSIConditionalState(); @@ -64,7 +65,7 @@ public SSIMediator(SSIExternalResolver ssiExternalResolver, - Date lastModifiedDate, int debug) { + long lastModifiedDate, int debug) { this.ssiExternalResolver = ssiExternalResolver; this.lastModifiedDate = lastModifiedDate; this.debug = debug; @@ -315,7 +316,7 @@ setVariableValue("DATE_LOCAL", null); ssiExternalResolver.setVariableValue(className + ".DATE_LOCAL", retVal); - retVal = formatDate(lastModifiedDate, null); + retVal = formatDate(new Date(lastModifiedDate), null); setVariableValue("LAST_MODIFIED", null); ssiExternalResolver.setVariableValue(className + ".LAST_MODIFIED", retVal); 1.4 +7 -3 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIPrintenv.java Index: SSIPrintenv.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIPrintenv.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- SSIPrintenv.java 1 Sep 2004 18:33:33 -0000 1.3 +++ SSIPrintenv.java 23 Apr 2005 10:22:37 -0000 1.4 @@ -18,14 +18,16 @@ * Implements the Server-side #printenv command * * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public class SSIPrintenv implements SSICommand { /** * @see SSICommand */ - public void process(SSIMediator ssiMediator, String commandName, + public long process(SSIMediator ssiMediator, String commandName, String[] paramNames, String[] paramValues, PrintWriter writer) { + long lastModified = 0; //any arguments should produce an error if (paramNames.length > 0) { String errorMessage = ssiMediator.getConfigErrMsg(); @@ -46,7 +48,9 @@ writer.write('='); writer.write(variableValue); writer.write('\n'); + lastModified = System.currentTimeMillis(); } } + return lastModified; } -} \ No newline at end of file +} 1.5 +10 -5 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIProcessor.java Index: SSIProcessor.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIProcessor.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- SSIProcessor.java 1 Sep 2004 18:33:33 -0000 1.4 +++ SSIProcessor.java 23 Apr 2005 10:22:37 -0000 1.5 @@ -15,7 +15,6 @@ import java.io.PrintWriter; import java.io.Reader; import java.io.StringWriter; -import java.util.Date; import java.util.HashMap; import java.util.StringTokenizer; import org.apache.catalina.util.IOTools; @@ -25,6 +24,7 @@ * necessary[ * * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public class SSIProcessor { @@ -76,11 +76,12 @@ * the reader to read the file containing SSIs from * @param writer * the writer to write the file with the SSIs processed. + * @return the most current modified date resulting from any SSI commands * @throws IOException * when things go horribly awry. Should be unlikely since the * SSICommand usually catches 'normal' IOExceptions. */ - public void process(Reader reader, Date lastModifiedDate, + public long process(Reader reader, long lastModifiedDate, PrintWriter writer) throws IOException { SSIMediator ssiMediator = new SSIMediator(ssiExternalResolver, lastModifiedDate, debug); @@ -142,8 +143,11 @@ // command is not conditional if (!ssiMediator.getConditionalState().processConditionalCommandsOnly || ssiCommand instanceof SSIConditional) { - ssiCommand.process(ssiMediator, strCmd, - paramNames, paramValues, writer); + long lmd = ssiCommand.process(ssiMediator, strCmd, + paramNames, paramValues, writer); + if (lmd > lastModifiedDate) { + lastModifiedDate = lmd; + } } } if (errorMessage != null) { @@ -160,6 +164,7 @@ //If we are here, then we have already stopped processing, so all // is good } + return lastModifiedDate; } 1.10 +10 -6 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIServlet.java Index: SSIServlet.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIServlet.java,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- SSIServlet.java 4 Apr 2005 20:57:02 -0000 1.9 +++ SSIServlet.java 23 Apr 2005 10:22:37 -0000 1.10 @@ -19,7 +19,6 @@ import java.io.StringWriter; import java.net.URL; import java.net.URLConnection; -import java.util.Date; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -33,6 +32,7 @@ * @author Bip Thelin * @author Amy Roh * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public class SSIServlet extends HttpServlet { @@ -190,8 +190,9 @@ protected void processSSI(HttpServletRequest req, HttpServletResponse res, URL resource) throws IOException { - SSIExternalResolver ssiExternalResolver = new SSIServletExternalResolver( - this, req, res, isVirtualWebappRelative, debug, inputEncoding); + SSIExternalResolver ssiExternalResolver = + new SSIServletExternalResolver(getServletContext(), req, res, + isVirtualWebappRelative, debug, inputEncoding); SSIProcessor ssiProcessor = new SSIProcessor(ssiExternalResolver, debug); PrintWriter printWriter = null; @@ -217,8 +218,11 @@ } BufferedReader bufferedReader = new BufferedReader(isr); - Date lastModifiedDate = new Date(resourceInfo.getLastModified()); - ssiProcessor.process(bufferedReader, lastModifiedDate, printWriter); + long lastModified = ssiProcessor.process(bufferedReader, + resourceInfo.getLastModified(), printWriter); + if (lastModified > 0) { + res.setDateHeader("last-modified", lastModified); + } if (buffered) { printWriter.flush(); String text = stringWriter.toString(); 1.6 +54 -25 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIServletExternalResolver.java Index: SSIServletExternalResolver.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSIServletExternalResolver.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- SSIServletExternalResolver.java 4 Apr 2005 20:57:02 -0000 1.5 +++ SSIServletExternalResolver.java 23 Apr 2005 10:22:37 -0000 1.6 @@ -12,6 +12,7 @@ import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLConnection; import java.net.URLDecoder; @@ -21,13 +22,15 @@ import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.catalina.connector.Request; + /** * An implementation of SSIExternalResolver that is used with servlets. * * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public class SSIServletExternalResolver implements SSIExternalResolver { @@ -37,17 +40,17 @@ "QUERY_STRING", "QUERY_STRING_UNESCAPED", "REMOTE_ADDR", "REMOTE_HOST", "REMOTE_USER", "REQUEST_METHOD", "SCRIPT_NAME", "SERVER_NAME", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE"}; - protected HttpServlet servlet; + protected ServletContext context; protected HttpServletRequest req; protected HttpServletResponse res; protected boolean isVirtualWebappRelative; protected int debug; protected String inputEncoding; - public SSIServletExternalResolver(HttpServlet servlet, + public SSIServletExternalResolver(ServletContext context, HttpServletRequest req, HttpServletResponse res, boolean isVirtualWebappRelative, int debug, String inputEncoding) { - this.servlet = servlet; + this.context = context; this.req = req; this.res = res; this.isVirtualWebappRelative = isVirtualWebappRelative; @@ -61,9 +64,9 @@ //is the same as Servlet.log( message ), since API //doesn't seem to say so. if (throwable != null) { - servlet.log(message, throwable); + context.log(message, throwable); } else { - servlet.log(message); + context.log(message); } } @@ -161,7 +164,35 @@ } else if (name.equalsIgnoreCase("QUERY_STRING_UNESCAPED")) { String queryString = req.getQueryString(); if (queryString != null) { - retVal = URLDecoder.decode(queryString); + // Use default as a last resort + String queryStringEncoding = + org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING; + + String uriEncoding = null; + boolean useBodyEncodingForURI = false; + + // Get encoding settings from request / connector if possible + String requestEncoding = req.getCharacterEncoding(); + if (req instanceof Request) { + uriEncoding = ((Request)req).getConnector().getURIEncoding(); + useBodyEncodingForURI = + ((Request)req).getConnector().getUseBodyEncodingForURI(); + } + + // If valid, apply settings from request / connector + if (uriEncoding != null) { + queryStringEncoding = uriEncoding; + } else if(useBodyEncodingForURI) { + if (requestEncoding != null) { + queryStringEncoding = requestEncoding; + } + } + + try { + retVal = URLDecoder.decode(queryString, queryStringEncoding); + } catch (UnsupportedEncodingException e) { + retVal = queryString; + } } } else if (name.equalsIgnoreCase("REMOTE_ADDR")) { retVal = req.getRemoteAddr(); @@ -180,8 +211,7 @@ } else if (name.equalsIgnoreCase("SERVER_PROTOCOL")) { retVal = req.getProtocol(); } else if (name.equalsIgnoreCase("SERVER_SOFTWARE")) { - ServletContext servletContext = servlet.getServletContext(); - retVal = servletContext.getServerInfo(); + retVal = context.getServerInfo(); } return retVal; } @@ -251,26 +281,24 @@ + nonVirtualPath); } String path = getAbsolutePath(nonVirtualPath); - ServletContext servletContext = servlet.getServletContext(); ServletContextAndPath csAndP = new ServletContextAndPath( - servletContext, path); + context, path); return csAndP; } protected ServletContextAndPath getServletContextAndPathFromVirtualPath( String virtualPath) throws IOException { - ServletContext servletContext = servlet.getServletContext(); String path = null; if (!virtualPath.startsWith("/") && !virtualPath.startsWith("\\")) { - path = getAbsolutePath(virtualPath); + return new ServletContextAndPath(context, getAbsolutePath(virtualPath)); } else { String normalized = SSIServletRequestUtil.normalize(virtualPath); if (isVirtualWebappRelative) { - path = normalized; + return new ServletContextAndPath(context, normalized); } else { - servletContext = servletContext.getContext(normalized); - if (servletContext == null) { + ServletContext normContext = context.getContext(normalized); + if (normContext == null) { throw new IOException("Couldn't get context for path: " + normalized); } @@ -278,19 +306,19 @@ // to remove, // ie: // '/file1.shtml' vs '/appName1/file1.shtml' - if (!isRootContext(servletContext)) { - path = getPathWithoutContext(normalized); - if (path == null) { + if (!isRootContext(normContext)) { + String noContext = getPathWithoutContext(normalized); + if (noContext == null) { throw new IOException( "Couldn't remove context from path: " + normalized); } + return new ServletContextAndPath(normContext, noContext); } else { - path = normalized; + return new ServletContextAndPath(normContext, normalized); } } } - return new ServletContextAndPath(servletContext, path); } @@ -370,9 +398,10 @@ throw new IOException( "Couldn't get request dispatcher for path: " + path); } - ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream(); - ResponseIncludeWrapper responseIncludeWrapper = new ResponseIncludeWrapper( - res, basos); + ByteArrayServletOutputStream basos = + new ByteArrayServletOutputStream(); + ResponseIncludeWrapper responseIncludeWrapper = + new ResponseIncludeWrapper(context, req, res, basos); rd.include(req, responseIncludeWrapper); //We can't assume the included servlet flushed its output responseIncludeWrapper.flushOutputStreamOrWriter(); 1.4 +6 -2 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSISet.java Index: SSISet.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/ssi/SSISet.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- SSISet.java 1 Sep 2004 18:33:33 -0000 1.3 +++ SSISet.java 23 Apr 2005 10:22:37 -0000 1.4 @@ -17,15 +17,17 @@ * * @author Paul Speed * @author Dan Sandberg + * @author David Becker * @version $Revision$, $Date$ */ public class SSISet implements SSICommand { /** * @see SSICommand */ - public void process(SSIMediator ssiMediator, String commandName, + public long process(SSIMediator ssiMediator, String commandName, String[] paramNames, String[] paramValues, PrintWriter writer) throws SSIStopProcessingException { + long lastModified = 0; String errorMessage = ssiMediator.getConfigErrMsg(); String variableName = null; for (int i = 0; i < paramNames.length; i++) { @@ -39,6 +41,7 @@ .substituteVariables(paramValue); ssiMediator.setVariableValue(variableName, substitutedValue); + lastModified = System.currentTimeMillis(); } else { ssiMediator.log("#set--no variable specified"); writer.write(errorMessage); @@ -50,5 +53,6 @@ throw new SSIStopProcessingException(); } } + return lastModified; } } \ No newline at end of file 1.292 +4 -0 jakarta-tomcat-catalina/webapps/docs/changelog.xml Index: changelog.xml =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/webapps/docs/changelog.xml,v retrieving revision 1.291 retrieving revision 1.292 diff -u -r1.291 -r1.292 --- changelog.xml 22 Apr 2005 20:38:38 -0000 1.291 +++ changelog.xml 23 Apr 2005 10:22:37 -0000 1.292 @@ -99,6 +99,10 @@ Remove CopyParentClassLoader rule, which doesn't seem to be doing anything useful anymore. (remm) </fix> + <add> + Provide an ServletFilter implementation of Server Side Includes (SSI). This was + submitted by David Becker under <bug>33106</bug>. (markt) + </add> </changelog> </subsection> 1.4 +41 -4 jakarta-tomcat-catalina/webapps/docs/ssi-howto.xml Index: ssi-howto.xml =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/webapps/docs/ssi-howto.xml,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- ssi-howto.xml 4 Apr 2005 20:57:02 -0000 1.3 +++ ssi-howto.xml 23 Apr 2005 10:22:37 -0000 1.4 @@ -29,10 +29,19 @@ <a href="http://httpd.apache.org/docs/howto/ssi.html#basicssidirectives"> Apache Introduction to SSI</a> for information on using SSI directives.</p> -<p>SSI support is implemented using the servlet class +<p>SSI support is available as a servlet and as a filter. You should use one +or the other to provide SSI support but not both.</p> + +<p>Servlet based SSI support is implemented using the class <code>org.apache.catalina.ssi.SSIServlet</code>. Traditionally, this servlet is mapped to the URL pattern "*.shtml".</p> +<p>Filter based SSI support is implemented using the class +<code>org.apache.catalina.ssi.SSIFilter</code>. Traditionally, this filter +is mapped to the URL pattern "*.shtml", though it can be mapped to "*" as +it will selectively enable/disable SSI processing based on mime types. The +contentType init param allows you to apply SSI processing to JSP pages, +javascript, or any other content you wish.</p> <p>By default SSI support is disabled in Tomcat.</p> </section> @@ -46,11 +55,17 @@ <p>Rename <code>$CATALINA_BASE/server/lib/servlets-ssi.renametojar</code> to <code>$CATALINA_BASE/server/lib/servlets-ssi.jar</code>.</p> -<p>Remove the XML comments from around the SSI servlet and servlet-mapping -configuration in <code>$CATALINA_BASE/conf/web.xml</code>.</p> +<p>To use the SSI servlet, remove the XML comments from around the SSI servlet +and servlet-mapping configuration in +<code>$CATALINA_BASE/conf/web.xml</code>.</p> + +<p>To use the SSI filter, remove the XML comments from around the SSI filter +and filter-mapping configuration in +<code>$CATALINA_BASE/conf/web.xml</code>.</p> + </section> -<section name="Configuration"> +<section name="Servlet Configuration"> <p>There are several servlet init parameters which can be used to configure the behaviour of the SSI servlet. @@ -75,6 +90,28 @@ </section> +<section name="Filter Configuration"> + +<p>There are several filter init parameters which can be used to +configure the behaviour of the SSI filter. +<ul> +<li><strong>contentType</strong> - A regex pattern that must be matched before +SSI processing is applied. When crafting your own pattern, don't forget that a +mime content type may be followed by an optional character set in the form +"mime/type; charset=set" that you must take into account. Default is +"text/x-server-parsed-html(;.*)?".</li> +<li><strong>debug</strong> - Debugging detail level for messages logged +by this servlet. Default 0.</li> +<li><strong>expires</strong> - The number of seconds before a page with SSI +directives will expire. Default behaviour is for all SSI directives to be +evaluated for every request.</li> +<li><strong>isVirtualWebappRelative</strong> - Should "virtual" SSI directive +paths be interpreted as relative to the context root, instead of the server +root? (0=false, 1=true) Default 0 (false).</li> +</ul> +</p> + +</section> </body> </document>
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]