FileDocCategorySizeDatePackage
SSIProcessor.javaAPI DocApache Tomcat 6.0.1413204Fri Jul 20 04:20:32 BST 2007org.apache.catalina.ssi

SSIProcessor

public class SSIProcessor extends Object
The entry point to SSI processing. This class does the actual parsing, delegating to the SSIMediator, SSICommand, and SSIExternalResolver as necessary[
author
Dan Sandberg
author
David Becker
version
$Revision: 531303 $, $Date: 2007-04-23 02:24:01 +0200 (lun., 23 avr. 2007) $

Fields Summary
protected static final String
COMMAND_START
The start pattern
protected static final String
COMMAND_END
The end pattern
protected static final int
BUFFER_SIZE
protected SSIExternalResolver
ssiExternalResolver
protected HashMap
commands
protected int
debug
Constructors Summary
public SSIProcessor(SSIExternalResolver ssiExternalResolver, int debug)



         
        this.ssiExternalResolver = ssiExternalResolver;
        this.debug = debug;
        addBuiltinCommands();
    
Methods Summary
protected voidaddBuiltinCommands()

        addCommand("config", new SSIConfig());
        addCommand("echo", new SSIEcho());
        addCommand("exec", new SSIExec());
        addCommand("include", new SSIInclude());
        addCommand("flastmod", new SSIFlastmod());
        addCommand("fsize", new SSIFsize());
        addCommand("printenv", new SSIPrintenv());
        addCommand("set", new SSISet());
        SSIConditional ssiConditional = new SSIConditional();
        addCommand("if", ssiConditional);
        addCommand("elif", ssiConditional);
        addCommand("endif", ssiConditional);
        addCommand("else", ssiConditional);
    
public voidaddCommand(java.lang.String name, SSICommand command)

        commands.put(name, command);
    
protected booleancharCmp(java.lang.String buf, int index, java.lang.String command)

        return buf.regionMatches(index, command, 0, command.length());
    
protected booleanisQuote(char c)

        return c == '\'" || c == '\"" || c == '`";
    
protected booleanisSpace(char c)

        return c == ' " || c == '\n" || c == '\t" || c == '\r";
    
private java.lang.StringparseCmd(java.lang.StringBuffer cmd)
Parse a StringBuffer and take out the command token. Called from requestHandler

param
cmd a value of type 'StringBuffer'
return
a value of type 'String', or null if there is none

        int firstLetter = -1;
        int lastLetter = -1;
        for (int i = 0; i < cmd.length(); i++) {
            char c = cmd.charAt(i);
            if (Character.isLetter(c)) {
                if (firstLetter == -1) {
                    firstLetter = i;
                }
                lastLetter = i;
            } else if (isSpace(c)) {
                if (lastLetter > -1) {
                    break;
                }
            } else {
                break;
            }
        }
        String command = null;
        if (firstLetter != -1) {
            command = cmd.substring(firstLetter, lastLetter + 1);
        }
        return command;
    
protected java.lang.String[]parseParamNames(java.lang.StringBuffer cmd, int start)
Parse a StringBuffer and take out the param type token. Called from requestHandler

param
cmd a value of type 'StringBuffer'
return
a value of type 'String[]'

        int bIdx = start;
        int i = 0;
        int quotes = 0;
        boolean inside = false;
        StringBuffer retBuf = new StringBuffer();
        while (bIdx < cmd.length()) {
            if (!inside) {
                while (bIdx < cmd.length() && isSpace(cmd.charAt(bIdx)))
                    bIdx++;
                if (bIdx >= cmd.length()) break;
                inside = !inside;
            } else {
                while (bIdx < cmd.length() && cmd.charAt(bIdx) != '=") {
                    retBuf.append(cmd.charAt(bIdx));
                    bIdx++;
                }
                retBuf.append('=");
                inside = !inside;
                quotes = 0;
                boolean escaped = false;
                for (; bIdx < cmd.length() && quotes != 2; bIdx++) {
                    char c = cmd.charAt(bIdx);
                    // Need to skip escaped characters
                    if (c == '\\" && !escaped) {
                        escaped = true;
                        bIdx++;
                        continue;
                    }
                    escaped = false;
                    if (c == '"") quotes++;
                }
            }
        }
        StringTokenizer str = new StringTokenizer(retBuf.toString(), "=");
        String[] retString = new String[str.countTokens()];
        while (str.hasMoreTokens()) {
            retString[i++] = str.nextToken().trim();
        }
        return retString;
    
protected java.lang.String[]parseParamValues(java.lang.StringBuffer cmd, int start, int count)
Parse a StringBuffer and take out the param token. Called from requestHandler

param
cmd a value of type 'StringBuffer'
return
a value of type 'String[]'

        int valIndex = 0;
        boolean inside = false;
        String[] vals = new String[count];
        StringBuffer sb = new StringBuffer();
        char endQuote = 0;
        for (int bIdx = start; bIdx < cmd.length(); bIdx++) {
            if (!inside) {
                while (bIdx < cmd.length() && !isQuote(cmd.charAt(bIdx)))
                    bIdx++;
                if (bIdx >= cmd.length()) break;
                inside = !inside;
                endQuote = cmd.charAt(bIdx);
            } else {
                boolean escaped = false;
                for (; bIdx < cmd.length(); bIdx++) {
                    char c = cmd.charAt(bIdx);
                    // Check for escapes
                    if (c == '\\" && !escaped) {
                        escaped = true;
                        continue;
                    }
                    // If we reach the other " then stop
                    if (c == endQuote && !escaped) break;
                    // Since parsing of attributes and var
                    // substitution is done in separate places,
                    // we need to leave escape in the string
                    if (c == '$" && escaped) sb.append('\\");
                    escaped = false;
                    sb.append(c);
                }
                // If we hit the end without seeing a quote
                // the signal an error
                if (bIdx == cmd.length()) return null;
                vals[valIndex++] = sb.toString();
                sb.delete(0, sb.length()); // clear the buffer
                inside = !inside;
            }
        }
        return vals;
    
public longprocess(java.io.Reader reader, long lastModifiedDate, java.io.PrintWriter writer)
Process a file with server-side commands, reading from reader and writing the processed version to writer. NOTE: We really should be doing this in a streaming way rather than converting it to an array first.

param
reader 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.

        SSIMediator ssiMediator = new SSIMediator(ssiExternalResolver,
                lastModifiedDate, debug);
        StringWriter stringWriter = new StringWriter();
        IOTools.flow(reader, stringWriter);
        String fileContents = stringWriter.toString();
        stringWriter = null;
        int index = 0;
        boolean inside = false;
        StringBuffer command = new StringBuffer();
        try {
            while (index < fileContents.length()) {
                char c = fileContents.charAt(index);
                if (!inside) {
                    if (c == COMMAND_START.charAt(0)
                            && charCmp(fileContents, index, COMMAND_START)) {
                        inside = true;
                        index += COMMAND_START.length();
                        command.setLength(0); //clear the command string
                    } else {
                        if (!ssiMediator.getConditionalState().processConditionalCommandsOnly) {
                            writer.write(c);
                        }
                        index++;
                    }
                } else {
                    if (c == COMMAND_END.charAt(0)
                            && charCmp(fileContents, index, COMMAND_END)) {
                        inside = false;
                        index += COMMAND_END.length();
                        String strCmd = parseCmd(command);
                        if (debug > 0) {
                            ssiExternalResolver.log(
                                    "SSIProcessor.process -- processing command: "
                                            + strCmd, null);
                        }
                        String[] paramNames = parseParamNames(command, strCmd
                                .length());
                        String[] paramValues = parseParamValues(command,
                                strCmd.length(), paramNames.length);
                        //We need to fetch this value each time, since it may
                        // change
                        // during the loop
                        String configErrMsg = ssiMediator.getConfigErrMsg();
                        SSICommand ssiCommand = (SSICommand)commands
                                .get(strCmd.toLowerCase());
                        String errorMessage = null;
                        if (ssiCommand == null) {
                            errorMessage = "Unknown command: " + strCmd;
                        } else if (paramValues == null) {
                            errorMessage = "Error parsing directive parameters.";
                        } else if (paramNames.length != paramValues.length) {
                            errorMessage = "Parameter names count does not match parameter values count on command: "
                                    + strCmd;
                        } else {
                            // don't process the command if we are processing
                            // conditional
                            // commands only and the
                            // command is not conditional
                            if (!ssiMediator.getConditionalState().processConditionalCommandsOnly
                                    || ssiCommand instanceof SSIConditional) {
                                long lmd = ssiCommand.process(ssiMediator, strCmd,
                                               paramNames, paramValues, writer);
                                if (lmd > lastModifiedDate) {
                                    lastModifiedDate = lmd;
                                }                                    
                            }
                        }
                        if (errorMessage != null) {
                            ssiExternalResolver.log(errorMessage, null);
                            writer.write(configErrMsg);
                        }
                    } else {
                        command.append(c);
                        index++;
                    }
                }
            }
        } catch (SSIStopProcessingException e) {
            //If we are here, then we have already stopped processing, so all
            // is good
        }
        return lastModifiedDate;