You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2007/02/19 13:19:09 UTC

svn commit: r509179 - in /tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves: AccessLogValve.java ExtendedAccessLogValve.java

Author: remm
Date: Mon Feb 19 04:19:08 2007
New Revision: 509179

URL: http://svn.apache.org/viewvc?view=rev&rev=509179
Log:
- Refactoring of ExtendedAccessLogValve, which now extends AccessLogValve.
- Submitted by Takayuki Kaneko.

Modified:
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves/AccessLogValve.java
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java

Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves/AccessLogValve.java
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves/AccessLogValve.java?view=diff&rev=509179&r1=509178&r2=509179
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves/AccessLogValve.java (original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves/AccessLogValve.java Mon Feb 19 04:19:08 2007
@@ -43,6 +43,8 @@
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.util.LifecycleSupport;
 import org.apache.catalina.util.StringManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
 
 
 /**
@@ -116,17 +118,7 @@
     extends ValveBase
     implements Lifecycle {
 
-
-    // ----------------------------------------------------------- Constructors
-
-
-    /**
-     * Construct a new instance of this class with default property values.
-     */
-    public AccessLogValve() {
-        setPattern("common");
-    }
-
+    private static Log log = LogFactory.getLog(AccessLogValve.class);
 
     // ----------------------------------------------------- Instance Variables
 
@@ -168,19 +160,19 @@
     /**
      * The pattern used to format our access log lines.
      */
-    private String pattern = null;
+    protected String pattern = null;
 
 
     /**
      * The prefix that is added to log file filenames.
      */
-    private String prefix = "access_log.";
+    protected String prefix = "access_log.";
 
 
     /**
      * Should we rotate our log file? Default is true (like old behavior)
      */
-    private boolean rotatable = true;
+    protected boolean rotatable = true;
 
 
     /**
@@ -192,33 +184,33 @@
     /**
      * The string manager for this package.
      */
-    private StringManager sm =
+    protected StringManager sm =
         StringManager.getManager(Constants.Package);
 
 
     /**
      * Has this component been started yet?
      */
-    private boolean started = false;
+    protected boolean started = false;
 
 
     /**
      * The suffix that is added to log file filenames.
      */
-    private String suffix = "";
+    protected String suffix = "";
 
 
     /**
      * The PrintWriter to which we are currently logging, if any.
      */
-    private PrintWriter writer = null;
+    protected PrintWriter writer = null;
 
 
     /**
      * A date formatter to format a Date into a date in the format
      * "yyyy-MM-dd".
      */
-    private SimpleDateFormat dateFormatter = null;
+    protected SimpleDateFormat fileDateFormatter = null;
 
 
     /**
@@ -270,6 +262,12 @@
     
     
     /**
+     * The current log file we are writing to. Helpful when checkExists
+     * is true.
+     */
+    protected File currentLogFile = null;
+    
+    /**
      * The system time when we last updated the Date that this valve
      * uses for log lines.
      */
@@ -289,22 +287,28 @@
      */
     private long rotationLastChecked = 0L;
 
-
+    /**
+     * Do we check for log file existence? Helpful if an external
+     * agent renames the log file so we can automagically recreate it.
+     */
+    private boolean checkExists = false;
+    
+    
     /**
      * Are we doing conditional logging. default false.
      */
-    private String condition = null;
+    protected String condition = null;
 
 
     /**
      * Date format to place in log file name. Use at your own risk!
      */
-    private String fileDateFormat = null;
+    protected String fileDateFormat = null;
     
     /**
      * Array of AccessLogElement, they will be used to make log message.
      */
-    private AccessLogElement[] logElements = null;
+    protected AccessLogElement[] logElements = null;
 
     // ------------------------------------------------------------- Properties
 
@@ -361,6 +365,28 @@
 
 
     /**
+     * Check for file existence before logging.
+     */
+    public boolean isCheckExists() {
+
+        return checkExists;
+
+    }
+
+
+    /**
+     * Set whether to check for log file existence before logging.
+     *
+     * @param checkExists true meaning to check for file existence.
+     */
+    public void setCheckExists(boolean checkExists) {
+
+        this.checkExists = checkExists;
+
+    }
+    
+    
+    /**
      * Return the log file prefix.
      */
     public String getPrefix() {
@@ -519,7 +545,7 @@
         long t2 = System.currentTimeMillis();
         long time = t2 - t1;
 
-        if (condition != null
+        if (logElements == null || condition != null
                 && null != request.getRequest().getAttribute(condition)) {
             return;
         }
@@ -534,6 +560,38 @@
         log(result.toString());
     }
 
+    
+    /**
+     * Rename the existing log file to something else. Then open the
+     * old log file name up once again. Intended to be called by a JMX
+     * agent.
+     *
+     *
+     * @param newFileName The file name to move the log file entry to
+     * @return true if a file was rotated with no error
+     */
+    public synchronized boolean rotate(String newFileName) {
+
+        if (currentLogFile != null) {
+            File holder = currentLogFile;
+            close();
+            try {
+                holder.renameTo(new File(newFileName));
+            } catch (Throwable e) {
+                log.error("rotate failed", e);
+            }
+
+            /* Make sure date is correct */
+            currentDate = new Date(System.currentTimeMillis());
+            dateStamp = fileDateFormatter.format(currentDate);
+
+            open();
+            return true;
+        } else {
+            return false;
+        }
+
+    }
 
     // -------------------------------------------------------- Private Methods
 
@@ -549,6 +607,7 @@
         writer.close();
         writer = null;
         dateStamp = "";
+        currentLogFile = null;
     }
 
 
@@ -569,7 +628,7 @@
                 rotationLastChecked = systime;
 
                 // Check for a change of date
-                String tsDate = dateFormatter.format(currentDate);
+                String tsDate = fileDateFormatter.format(currentDate);
 
                 // If the date has changed, switch log files
                 if (!dateStamp.equals(tsDate)) {
@@ -583,6 +642,25 @@
                 }
             }
         }
+        
+        /* In case something external rotated the file instead */
+        if (checkExists) {
+            synchronized (this) {
+                if (currentLogFile != null && !currentLogFile.exists()) {
+                    try {
+                        close();
+                    } catch (Throwable e) {
+                        log.info("at least this wasn't swallowed", e);
+                    }
+
+                    /* Make sure date is correct */
+                    currentDate = new Date(System.currentTimeMillis());
+                    dateStamp = fileDateFormatter.format(currentDate);
+
+                    open();
+                }
+            }
+        }
 
         // Log this message
         if (writer != null) {
@@ -615,7 +693,7 @@
     /**
      * Open the new log file for the date specified by <code>dateStamp</code>.
      */
-    private synchronized void open() {
+    protected synchronized void open() {
         // Create the directory if necessary
         File dir = new File(directory);
         if (!dir.isAbsolute())
@@ -635,8 +713,11 @@
             }
             writer = new PrintWriter(new BufferedWriter(new FileWriter(
                     pathname, true), 128000), false);
+            
+            currentLogFile = new File(pathname);
         } catch (IOException e) {
             writer = null;
+            currentLogFile = null;
         }
     }
  
@@ -755,8 +836,8 @@
 
         if (fileDateFormat == null || fileDateFormat.length() == 0)
             fileDateFormat = "yyyy-MM-dd";
-        dateFormatter = new SimpleDateFormat(fileDateFormat);
-        dateFormatter.setTimeZone(timezone);
+        fileDateFormatter = new SimpleDateFormat(fileDateFormat);
+        fileDateFormatter.setTimeZone(timezone);
         dayFormatter = new SimpleDateFormat("dd");
         dayFormatter.setTimeZone(timezone);
         monthFormatter = new SimpleDateFormat("MM");
@@ -766,7 +847,7 @@
         timeFormatter = new SimpleDateFormat("HH:mm:ss");
         timeFormatter.setTimeZone(timezone);
         currentDate = new Date();
-        dateStamp = dateFormatter.format(currentDate);
+        dateStamp = fileDateFormatter.format(currentDate);
         open();
     }
 
@@ -793,7 +874,7 @@
     /**
      * AccessLogElement writes the partial message into the buffer.
      */
-    private interface AccessLogElement {
+    protected interface AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time);
 
@@ -802,14 +883,20 @@
     /**
      * write local IP address - %A
      */
-    private class LocalAddrElement implements AccessLogElement {
+    protected class LocalAddrElement implements AccessLogElement {
+        
+        private String value = null;
+        
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
-            String value;
-            try {
-                value = InetAddress.getLocalHost().getHostAddress();
-            } catch (Throwable e) {
-                value = "127.0.0.1";
+            if (value == null) {
+                synchronized (this) {
+                    try {
+                        value = InetAddress.getLocalHost().getHostAddress();
+                    } catch (Throwable e) {
+                        value = "127.0.0.1";
+                    }
+                }
             }
             buf.append(value);
         }
@@ -818,7 +905,7 @@
     /**
      * write remote IP address - %a
      */
-    private class RemoteAddrElement implements AccessLogElement {
+    protected class RemoteAddrElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             buf.append(request.getRemoteAddr());
@@ -828,7 +915,7 @@
     /**
      * write remote host name - %h
      */
-    private class HostElement implements AccessLogElement {
+    protected class HostElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             buf.append(request.getRemoteHost());
@@ -838,7 +925,7 @@
     /**
      * write remote logical username from identd (always returns '-') - %l
      */
-    private class LogicalUserNameElement implements AccessLogElement {
+    protected class LogicalUserNameElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             buf.append('-');
@@ -848,7 +935,7 @@
     /**
      * write request protocol - %H
      */
-    private class ProtocolElement implements AccessLogElement {
+    protected class ProtocolElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             buf.append(request.getProtocol());
@@ -858,7 +945,7 @@
     /**
      * write remote user that was authenticated (if any), else '-' - %u
      */
-    private class UserElement implements AccessLogElement {
+    protected class UserElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             if (request != null) {
@@ -877,7 +964,7 @@
     /**
      * write date and time, in Common Log Format - %t
      */
-    private class DateAndTimeElement implements AccessLogElement {
+    protected class DateAndTimeElement implements AccessLogElement {
         private Date currentDate = new Date(0);
 
         private String currentDateString = null;
@@ -911,7 +998,7 @@
     /**
      * write first line of the request (method and request URI) - %r
      */
-    private class RequestElement implements AccessLogElement {
+    protected class RequestElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             if (request != null) {
@@ -934,7 +1021,7 @@
     /**
      * write HTTP status code of the response - %s
      */
-    private class HttpStatusCodeElement implements AccessLogElement {
+    protected class HttpStatusCodeElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             if (response != null) {
@@ -948,7 +1035,7 @@
     /**
      * write local port on which this request was received - %p
      */
-    private class LocalPortElement implements AccessLogElement {
+    protected class LocalPortElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             buf.append(request.getServerPort());
@@ -958,7 +1045,7 @@
     /**
      * write bytes sent, excluding HTTP headers - %b, %B
      */
-    private class ByteSentElement implements AccessLogElement {
+    protected class ByteSentElement implements AccessLogElement {
         private boolean conversion;
 
         /**
@@ -982,7 +1069,7 @@
     /**
      * write request method (GET, POST, etc.) - %m
      */
-    private class MethodElement implements AccessLogElement {
+    protected class MethodElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             if (request != null) {
@@ -994,7 +1081,7 @@
     /**
      * write time taken to process the request - %D, %T
      */
-    private class ElapsedTimeElement implements AccessLogElement {
+    protected class ElapsedTimeElement implements AccessLogElement {
         private boolean millis;
 
         /**
@@ -1025,7 +1112,7 @@
     /**
      * write Query string (prepended with a '?' if it exists) - %q
      */
-    private class QueryElement implements AccessLogElement {
+    protected class QueryElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             String query = null;
@@ -1041,7 +1128,7 @@
     /**
      * write user session ID - %S
      */
-    private class SessionIdElement implements AccessLogElement {
+    protected class SessionIdElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             if (request != null) {
@@ -1060,7 +1147,7 @@
     /**
      * write requested URL path - %U
      */
-    private class RequestURIElement implements AccessLogElement {
+    protected class RequestURIElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             if (request != null) {
@@ -1074,7 +1161,7 @@
     /**
      * write local server name - %v
      */
-    private class LocalServerNameElement implements AccessLogElement {
+    protected class LocalServerNameElement implements AccessLogElement {
         public void addElement(StringBuffer buf, Date date, Request request,
                 Response response, long time) {
             buf.append(request.getServerName());
@@ -1084,7 +1171,7 @@
     /**
      * write any string
      */
-    private class StringElement implements AccessLogElement {
+    protected class StringElement implements AccessLogElement {
         private String str;
 
         public StringElement(String str) {
@@ -1100,7 +1187,7 @@
     /**
      * write incoming headers - %{xxx}i
      */
-    private class HeaderElement implements AccessLogElement {
+    protected class HeaderElement implements AccessLogElement {
         private String header;
 
         public HeaderElement(String header) {
@@ -1116,7 +1203,7 @@
     /**
      * write a specific cookie - %{xxx}c
      */
-    private class CookieElement implements AccessLogElement {
+    protected class CookieElement implements AccessLogElement {
         private String header;
 
         public CookieElement(String header) {
@@ -1142,7 +1229,7 @@
     /**
      * write an attribute in the ServletRequest - %{xxx}r
      */
-    private class RequestAttributeElement implements AccessLogElement {
+    protected class RequestAttributeElement implements AccessLogElement {
         private String header;
 
         public RequestAttributeElement(String header) {
@@ -1172,7 +1259,7 @@
     /**
      * write an attribute in the HttpSession - %{xxx}s
      */
-    private class SessionAttributeElement implements AccessLogElement {
+    protected class SessionAttributeElement implements AccessLogElement {
         private String header;
 
         public SessionAttributeElement(String header) {
@@ -1207,8 +1294,8 @@
     /**
      * parse pattern string and create the array of AccessLogElement
      */
-    private AccessLogElement[] createLogElements() {
-        List list = new ArrayList();
+    protected AccessLogElement[] createLogElements() {
+        List<AccessLogElement> list = new ArrayList<AccessLogElement>();
         boolean replace = false;
         StringBuffer buf = new StringBuffer();
         for (int i = 0; i < pattern.length(); i++) {

Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java?view=diff&rev=509179&r1=509178&r2=509179
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java (original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/valves/ExtendedAccessLogValve.java Mon Feb 19 04:19:08 2007
@@ -19,31 +19,23 @@
 package org.apache.catalina.valves;
 
 
-import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
-import java.io.PrintWriter;
+import java.io.StringReader;
 import java.net.InetAddress;
 import java.net.URLEncoder;
-import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedList;
+import java.util.List;
 import java.util.TimeZone;
 
-import javax.servlet.ServletException;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpSession;
 
 import org.apache.catalina.Lifecycle;
-import org.apache.catalina.LifecycleException;
-import org.apache.catalina.LifecycleListener;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
-import org.apache.catalina.util.LifecycleSupport;
 import org.apache.catalina.util.ServerInfo;
-import org.apache.catalina.util.StringManager;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 
@@ -103,7 +95,7 @@
  * </p>
  *
  * <p>
- * For UNIX users, another field called <code>checkExists</code>is also
+ * For UvNIX users, another field called <code>checkExists</code>is also
  * available. If set to true, the log file's existence will be checked before
  * each logging. This way an external log rotator can move the file
  * somewhere and tomcat will start with a new file.
@@ -134,650 +126,37 @@
  */
 
 public class ExtendedAccessLogValve
-    extends ValveBase
+    extends AccessLogValve
     implements Lifecycle {
 
-
-    // ----------------------------------------------------------- Constructors
-
-
-    /**
-     * Construct a new instance of this class with default property values.
-     */
-    public ExtendedAccessLogValve() {
-
-        super();
-
-
-    }
-
+    private static Log log = LogFactory.getLog(ExtendedAccessLogValve.class);
 
     // ----------------------------------------------------- Instance Variables
-    private static Log log = LogFactory.getLog(ExtendedAccessLogValve.class);
 
 
     /**
      * The descriptive information about this implementation.
      */
-    protected static final String info =
+    protected static final String extendedAccessLogInfo =
         "org.apache.catalina.valves.ExtendedAccessLogValve/1.0";
 
 
-    /**
-     * The lifecycle event support for this component.
-     */
-    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
-
-
-
-    /**
-     * The string manager for this package.
-     */
-    private StringManager sm =
-        StringManager.getManager(Constants.Package);
-
-
-    /**
-     * Has this component been started yet?
-     */
-    private boolean started = false;
-
-
-    /**
-     * The as-of date for the currently open log file, or a zero-length
-     * string if there is no open log file.
-     */
-    private String dateStamp = "";
-
-
-    /**
-     * The PrintWriter to which we are currently logging, if any.
-     */
-    private PrintWriter writer = null;
-
-
-    /**
-     * The formatter for the date contained in the file name.
-     */
-    private SimpleDateFormat fileDateFormatter = null;
-
-
-    /**
-     * A date formatter to format a Date into a date in the format
-     * "yyyy-MM-dd".
-     */
-    private SimpleDateFormat dateFormatter = null;
-
-
-    /**
-     * A date formatter to format a Date into a time in the format
-     * "kk:mm:ss" (kk is a 24-hour representation of the hour).
-     */
-    private SimpleDateFormat timeFormatter = null;
-
-
-    /**
-     * Time taken formatter for 3 decimal places.
-     */
-     private DecimalFormat timeTakenFormatter = null;
-
-
-    /**
-     * My ip address. Look it up once and remember it. Dump this if we can
-     * determine another reliable way to get server ip address since this
-     * server may have many ip's.
-     */
-    private String myIpAddress = null;
-
-
-    /**
-     * My dns name. Look it up once and remember it. Dump this if we can
-     * determine another reliable way to get server name address since this
-     * server may have many ip's.
-     */
-    private String myDNSName = null;
-
-
-    /**
-     * Holder for all of the fields to log after the pattern is decoded.
-     */
-    private FieldInfo[] fieldInfos;
-
-
-    /**
-     * The current log file we are writing to. Helpful when checkExists
-     * is true.
-     */
-    private File currentLogFile = null;
-
-
-
-    /**
-     * The system time when we last updated the Date that this valve
-     * uses for log lines.
-     */
-    private Date currentDate = null;
-
-
-    /**
-     * Instant when the log daily rotation was last checked.
-     */
-    private long rotationLastChecked = 0L;
-
-
-    /**
-     * The directory in which log files are created.
-     */
-    private String directory = "logs";
-
-
-    /**
-     * The pattern used to format our access log lines.
-     */
-    private String pattern = null;
-
-
-    /**
-     * The prefix that is added to log file filenames.
-     */
-    private String prefix = "access_log.";
-
-
-    /**
-     * Should we rotate our log file? Default is true (like old behavior)
-     */
-    private boolean rotatable = true;
-
-
-    /**
-     * The suffix that is added to log file filenames.
-     */
-    private String suffix = "";
-
-
-    /**
-     * Are we doing conditional logging. default false.
-     */
-    private String condition = null;
-
-
-    /**
-     * Do we check for log file existence? Helpful if an external
-     * agent renames the log file so we can automagically recreate it.
-     */
-    private boolean checkExists = false;
-
-
-    /**
-     * Date format to place in log file name. Use at your own risk!
-     */
-    private String fileDateFormat = null;
-
-
     // ------------------------------------------------------------- Properties
 
 
     /**
-     * Return the directory in which we create log files.
-     */
-    public String getDirectory() {
-
-        return (directory);
-
-    }
-
-
-    /**
-     * Set the directory in which we create log files.
-     *
-     * @param directory The new log file directory
-     */
-    public void setDirectory(String directory) {
-
-        this.directory = directory;
-
-    }
-
-
-    /**
      * Return descriptive information about this implementation.
      */
     public String getInfo() {
-
-        return (info);
-
-    }
-
-
-    /**
-     * Return the format pattern.
-     */
-    public String getPattern() {
-
-        return (this.pattern);
-
-    }
-
-
-    /**
-     * Set the format pattern, first translating any recognized alias.
-     *
-     * @param pattern The new pattern pattern
-     */
-    public void setPattern(String pattern) {
-
-        FieldInfo[] f= decodePattern(pattern);
-        if (f!=null) {
-            this.pattern = pattern;
-            this.fieldInfos = f;
-        }
-    }
-
-
-    /**
-     * Return the log file prefix.
-     */
-    public String getPrefix() {
-
-        return (prefix);
-
-    }
-
-
-    /**
-     * Set the log file prefix.
-     *
-     * @param prefix The new log file prefix
-     */
-    public void setPrefix(String prefix) {
-
-        this.prefix = prefix;
-
-    }
-
-
-    /**
-     * Return true if logs are automatically rotated.
-     */
-    public boolean isRotatable() {
-
-        return rotatable;
-
-    }
-
-
-    /**
-     * Set the value is we should we rotate the logs
-     *
-     * @param rotatable true is we should rotate.
-     */
-    public void setRotatable(boolean rotatable) {
-
-        this.rotatable = rotatable;
-
-    }
-
-
-    /**
-     * Return the log file suffix.
-     */
-    public String getSuffix() {
-
-        return (suffix);
-
-    }
-
-
-    /**
-     * Set the log file suffix.
-     *
-     * @param suffix The new log file suffix
-     */
-    public void setSuffix(String suffix) {
-
-        this.suffix = suffix;
-
-    }
-
-
-
-    /**
-     * Return whether the attribute name to look for when
-     * performing conditional loggging. If null, every
-     * request is logged.
-     */
-    public String getCondition() {
-
-        return condition;
-
-    }
-
-
-    /**
-     * Set the ServletRequest.attribute to look for to perform
-     * conditional logging. Set to null to log everything.
-     *
-     * @param condition Set to null to log everything
-     */
-    public void setCondition(String condition) {
-
-        this.condition = condition;
-
-    }
-
-
-
-    /**
-     * Check for file existence before logging.
-     */
-    public boolean isCheckExists() {
-
-        return checkExists;
-
-    }
-
-
-    /**
-     * Set whether to check for log file existence before logging.
-     *
-     * @param checkExists true meaning to check for file existence.
-     */
-    public void setCheckExists(boolean checkExists) {
-
-        this.checkExists = checkExists;
-
-    }
-
-
-    /**
-     *  Return the date format date based log rotation.
-     */
-    public String getFileDateFormat() {
-        return fileDateFormat;
-    }
-
-
-    /**
-     *  Set the date format date based log rotation.
-     */
-    public void setFileDateFormat(String fileDateFormat) {
-        this.fileDateFormat =  fileDateFormat;
+        return (extendedAccessLogInfo);
     }
 
 
     // --------------------------------------------------------- Public Methods
 
 
-    /**
-     * Log a message summarizing the specified request and response, according
-     * to the format specified by the <code>pattern</code> property.
-     *
-     * @param request Request being processed
-     * @param response Response being processed
-     *
-     * @exception IOException if an input/output error has occurred
-     * @exception ServletException if a servlet error has occurred
-     */
-    public void invoke(Request request, Response response)
-        throws IOException, ServletException {
-
-        // Pass this request on to the next valve in our pipeline
-        long endTime;
-        long runTime;
-        long startTime=System.currentTimeMillis();
-
-        getNext().invoke(request, response);
-
-        endTime = System.currentTimeMillis();
-        runTime = endTime-startTime;
-
-        if (fieldInfos==null || condition!=null &&
-              null!=request.getRequest().getAttribute(condition)) {
-            return;
-        }
-
-
-        Date date = getDate(endTime);
-        StringBuffer result = new StringBuffer();
-
-        for (int i=0; fieldInfos!=null && i<fieldInfos.length; i++) {
-            switch(fieldInfos[i].type) {
-                case FieldInfo.DATA_CLIENT:
-                    if (FieldInfo.FIELD_IP==fieldInfos[i].location)
-                        result.append(request.getRequest().getRemoteAddr());
-                    else if (FieldInfo.FIELD_DNS==fieldInfos[i].location)
-                        result.append(request.getRequest().getRemoteHost());
-                    else
-                        result.append("?WTF?"); /* This should never happen! */
-                    break;
-                case FieldInfo.DATA_SERVER:
-                    if (FieldInfo.FIELD_IP==fieldInfos[i].location)
-                        result.append(myIpAddress);
-                    else if (FieldInfo.FIELD_DNS==fieldInfos[i].location)
-                        result.append(myDNSName);
-                    else
-                        result.append("?WTF?"); /* This should never happen! */
-                    break;
-                case FieldInfo.DATA_REMOTE:
-                    result.append('?'); /* I don't know how to handle these! */
-                    break;
-                case FieldInfo.DATA_CLIENT_TO_SERVER:
-                    result.append(getClientToServer(fieldInfos[i], request));
-                    break;
-                case FieldInfo.DATA_SERVER_TO_CLIENT:
-                    result.append(getServerToClient(fieldInfos[i], response));
-                    break;
-                case FieldInfo.DATA_SERVER_TO_RSERVER:
-                    result.append('-');
-                    break;
-                case FieldInfo.DATA_RSERVER_TO_SERVER:
-                    result.append('-');
-                    break;
-                case FieldInfo.DATA_APP_SPECIFIC:
-                    result.append(getAppSpecific(fieldInfos[i], request));
-                    break;
-                case FieldInfo.DATA_SPECIAL:
-                    if (FieldInfo.SPECIAL_DATE==fieldInfos[i].location)
-                        result.append(dateFormatter.format(date));
-                    else if (FieldInfo.SPECIAL_TIME_TAKEN==fieldInfos[i].location)
-                        result.append(timeTakenFormatter.format(runTime/1000d));
-                    else if (FieldInfo.SPECIAL_TIME==fieldInfos[i].location)
-                        result.append(timeFormatter.format(date));
-                    else if (FieldInfo.SPECIAL_BYTES==fieldInfos[i].location) {
-                        int length = response.getContentCount();
-                        if (length > 0)
-                            result.append(length);
-                        else
-                            result.append("-");
-                    } else if (FieldInfo.SPECIAL_CACHED==fieldInfos[i].location)
-                        result.append('-'); /* I don't know how to evaluate this! */
-                    else
-                        result.append("?WTF?"); /* This should never happen! */
-                    break;
-                default:
-                    result.append("?WTF?"); /* This should never happen! */
-            }
-
-            if (fieldInfos[i].postWhiteSpace!=null) {
-                result.append(fieldInfos[i].postWhiteSpace);
-            }
-        }
-        log(result.toString(), date);
-
-    }
-
-
-    /**
-     * Rename the existing log file to something else. Then open the
-     * old log file name up once again. Intended to be called by a JMX
-     * agent.
-     *
-     *
-     * @param newFileName The file name to move the log file entry to
-     * @return true if a file was rotated with no error
-     */
-    public synchronized boolean rotate(String newFileName) {
-
-        if (currentLogFile!=null) {
-            File holder = currentLogFile;
-            close();
-            try {
-                holder.renameTo(new File(newFileName));
-            } catch(Throwable e){
-                log.error("rotate failed", e);
-            }
-
-            /* Make sure date is correct */
-            currentDate = new Date(System.currentTimeMillis());
-            dateStamp = fileDateFormatter.format(currentDate);
-
-            open();
-            return true;
-        } else {
-            return false;
-        }
-
-    }
-
     // -------------------------------------------------------- Private Methods
 
-
-    /**
-     *  Return the client to server data.
-     *  @param fieldInfo The field to decode.
-     *  @param request The object we pull data from.
-     *  @return The appropriate value.
-     */
-     private String getClientToServer(FieldInfo fieldInfo, Request request) {
-
-        switch(fieldInfo.location) {
-            case FieldInfo.FIELD_METHOD:
-                return request.getMethod();
-            case FieldInfo.FIELD_URI:
-                if (null==request.getQueryString())
-                    return request.getRequestURI();
-                else
-                    return request.getRequestURI() + "?" + request.getQueryString();
-            case FieldInfo.FIELD_URI_STEM:
-                return request.getRequestURI();
-            case FieldInfo.FIELD_URI_QUERY:
-                if (null==request.getQueryString())
-                    return "-";
-                return request.getQueryString();
-            case FieldInfo.FIELD_HEADER:
-                return wrap(request.getHeader(fieldInfo.value));
-            default:
-                ;
-        }
-
-        return "-";
-
-    }
-
-
-    /**
-     *  Return the server to client data.
-     *  @param fieldInfo The field to decode.
-     *  @param response The object we pull data from.
-     *  @return The appropriate value.
-     */
-    private String getServerToClient(FieldInfo fieldInfo, Response response) {
-        switch(fieldInfo.location) {
-            case FieldInfo.FIELD_STATUS:
-                return "" + response.getStatus();
-            case FieldInfo.FIELD_COMMENT:
-                return "?"; /* Not coded yet*/
-            case FieldInfo.FIELD_HEADER:
-                return wrap(response.getHeader(fieldInfo.value));
-            default:
-                ;
-        }
-
-        return "-";
-
-    }
-
-
-    /**
-     * Get app specific data.
-     * @param fieldInfo The field to decode
-     * @param request Where we will pull the data from.
-     * @return The appropriate value
-     */
-    private String getAppSpecific(FieldInfo fieldInfo, Request request) {
-
-        switch(fieldInfo.xType) {
-            case FieldInfo.X_PARAMETER:
-                return wrap(urlEncode(request.getParameter(fieldInfo.value)));
-            case FieldInfo.X_REQUEST:
-                return wrap(request.getAttribute(fieldInfo.value));
-            case FieldInfo.X_SESSION:
-                HttpSession session = null;
-                if (request!=null){
-                    session = request.getSession(false);
-                    if (session!=null)
-                        return wrap(session.getAttribute(fieldInfo.value));
-                }
-                break;
-            case FieldInfo.X_COOKIE:
-                Cookie[] c = request.getCookies();
-                for (int i=0; c != null && i < c.length; i++){
-                    if (fieldInfo.value.equals(c[i].getName())){
-                        return wrap(c[i].getValue());
-                    }
-                 }
-            case FieldInfo.X_APP:
-                return wrap(request.getContext().getServletContext()
-                                .getAttribute(fieldInfo.value));
-            case FieldInfo.X_SERVLET_REQUEST:
-                if (fieldInfo.location==FieldInfo.X_LOC_AUTHTYPE) {
-                    return wrap(request.getAuthType());
-                } else if (fieldInfo.location==FieldInfo.X_LOC_REMOTEUSER) {
-                    return wrap(request.getRemoteUser());
-                } else if (fieldInfo.location==
-                            FieldInfo.X_LOC_REQUESTEDSESSIONID) {
-                    return wrap(request.getRequestedSessionId());
-                } else if (fieldInfo.location==
-                            FieldInfo.X_LOC_REQUESTEDSESSIONIDFROMCOOKIE) {
-                    return wrap(""+request.isRequestedSessionIdFromCookie());
-                } else if (fieldInfo.location==
-                            FieldInfo.X_LOC_REQUESTEDSESSIONIDVALID) {
-                    return wrap(""+request.isRequestedSessionIdValid());
-                } else if (fieldInfo.location==FieldInfo.X_LOC_CONTENTLENGTH) {
-                    return wrap(""+request.getContentLength());
-                } else if (fieldInfo.location==
-                            FieldInfo.X_LOC_CHARACTERENCODING) {
-                    return wrap(request.getCharacterEncoding());
-                } else if (fieldInfo.location==FieldInfo.X_LOC_LOCALE) {
-                    return wrap(request.getLocale());
-                } else if (fieldInfo.location==FieldInfo.X_LOC_PROTOCOL) {
-                    return wrap(request.getProtocol());
-                } else if (fieldInfo.location==FieldInfo.X_LOC_SCHEME) {
-                    return wrap(request.getScheme());
-                } else if (fieldInfo.location==FieldInfo.X_LOC_SECURE) {
-                    return wrap(""+request.isSecure());
-                }
-                break;
-            default:
-                ;
-        }
-
-        return "-";
-
-    }
-
-
-    /**
-     *  urlEncode the given string. If null or empty, return null.
-     */
-    private String urlEncode(String value) {
-        if (null==value || value.length()==0) {
-            return null;
-        }
-        return URLEncoder.encode(value);
-    }
-
-
     /**
      *  Wrap the incoming value into quotes and escape any inner
      *  quotes with double quotes.
@@ -789,642 +168,640 @@
      *     sets of quotes.
      */
     private String wrap(Object value) {
-
         String svalue;
         // Does the value contain a " ? If so must encode it
-        if (value==null || "-".equals(value))
+        if (value == null || "-".equals(value))
             return "-";
 
-
         try {
             svalue = value.toString();
             if ("".equals(svalue))
                 return "-";
-        } catch(Throwable e){
+        } catch (Throwable e) {
             /* Log error */
             return "-";
         }
 
         /* Wrap all quotes in double quotes. */
-        StringBuffer buffer = new StringBuffer(svalue.length()+2);
-        buffer.append('"');
-        int i=0;
-        while (i<svalue.length()) {
-            int j = svalue.indexOf('"', i);
-            if (j==-1) {
+        StringBuffer buffer = new StringBuffer(svalue.length() + 2);
+        buffer.append('\'');
+        int i = 0;
+        while (i < svalue.length()) {
+            int j = svalue.indexOf('\'', i);
+            if (j == -1) {
                 buffer.append(svalue.substring(i));
-                i=svalue.length();
+                i = svalue.length();
             } else {
-                buffer.append(svalue.substring(i, j+1));
+                buffer.append(svalue.substring(i, j + 1));
                 buffer.append('"');
-                i=j+2;
+                i = j + 2;
             }
         }
 
-        buffer.append('"');
+        buffer.append('\'');
         return buffer.toString();
-
     }
 
-
     /**
-     * Close the currently open log file (if any)
+     * Open the new log file for the date specified by <code>dateStamp</code>.
      */
-    private synchronized void close() {
-
-        if (writer == null)
-            return;
-        writer.flush();
-        writer.close();
-        writer = null;
-        currentLogFile = null;
-
+    protected synchronized void open() {
+        super.open();
+        if (currentLogFile.length()==0) {
+            writer.println("#Fields: " + pattern);
+            writer.println("#Version: 1.0");
+            writer.println("#Software: " + ServerInfo.getServerInfo());
+        }
     }
 
 
-    /**
-     * Log the specified message to the log file, switching files if the date
-     * has changed since the previous log call.
-     *
-     * @param message Message to be logged
-     * @param date the current Date object (so this method doesn't need to
-     *        create a new one)
-     */
-    private void log(String message, Date date) {
+    // ------------------------------------------------------ Lifecycle Methods
 
-        if (rotatable){
-            // Only do a logfile switch check once a second, max.
-            long systime = System.currentTimeMillis();
-            if ((systime - rotationLastChecked) > 1000) {
-
-                // We need a new currentDate
-                currentDate = new Date(systime);
-                rotationLastChecked = systime;
-
-                // Check for a change of date
-                String tsDate = fileDateFormatter.format(currentDate);
-
-                // If the date has changed, switch log files
-                if (!dateStamp.equals(tsDate)) {
-                    synchronized (this) {
-                        if (!dateStamp.equals(tsDate)) {
-                            close();
-                            dateStamp = tsDate;
-                            open();
-                        }
+
+    protected class DateElement implements AccessLogElement {
+        private Date currentDate = new Date(0);
+        
+        private String currentDateString = null;
+        
+        /**
+         * A date formatter to format a Date into a date in the format
+         * "yyyy-MM-dd".
+         */
+        private SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
+        
+        public DateElement() {
+            dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
+        }
+        
+        public void addElement(StringBuffer buf, Date date, Request request,
+                Response response, long time) {
+            if (currentDate != date) {
+                synchronized (this) {
+                    if (currentDate != date) {
+                        currentDateString = dateFormatter.format(date);
+                        currentDate = date;
                     }
                 }
             }
+            buf.append(currentDateString);            
         }
-
-        /* In case something external rotated the file instead */
-        if (checkExists){
-            synchronized (this) {
-                if (currentLogFile!=null && !currentLogFile.exists()) {
-                    try {
-                        close();
-                    } catch (Throwable e){
-                        log.info("at least this wasn't swallowed", e);
+    }
+    
+    protected class TimeElement implements AccessLogElement {
+        private Date currentDate = new Date(0);
+        
+        private String currentTimeString = null;
+        
+        /**
+         * A date formatter to format a Date into a time in the format
+         * "kk:mm:ss" (kk is a 24-hour representation of the hour).
+         */
+        private SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss");
+
+        public TimeElement() {
+            timeFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
+        }
+        
+        public void addElement(StringBuffer buf, Date date, Request request,
+                Response response, long time) {
+            if (currentDate != date) {
+                synchronized (this) {
+                    if (currentDate != date) {
+                        currentTimeString = timeFormatter.format(date);
+                        currentDate = date;
                     }
-
-                    /* Make sure date is correct */
-                    currentDate = new Date(System.currentTimeMillis());
-                    dateStamp = fileDateFormatter.format(currentDate);
-
-                    open();
                 }
             }
+            buf.append(currentTimeString);            
         }
-
-        // Log this message
-        if (writer != null) {
-            writer.println(message);
+    }
+    
+    protected class RequestHeaderElement implements AccessLogElement {
+        private String header;
+        
+        public RequestHeaderElement(String header) {
+            this.header = header;
+        }
+        public void addElement(StringBuffer buf, Date date, Request request,
+                Response response, long time) {
+            buf.append(wrap(request.getHeader(header)));
+        }
+    }
+    
+    protected class ResponseHeaderElement implements AccessLogElement {
+        private String header;
+        
+        public ResponseHeaderElement(String header) {
+            this.header = header;
+        }
+        
+        public void addElement(StringBuffer buf, Date date, Request request,
+                Response response, long time) {
+            buf.append(wrap(response.getHeader(header)));
+        }
+    }
+    
+    protected class ServletContextElement implements AccessLogElement {
+        private String attribute;
+        
+        public ServletContextElement(String attribute) {
+            this.attribute = attribute;
+        }
+        public void addElement(StringBuffer buf, Date date, Request request,
+                Response response, long time) {
+            buf.append(wrap(request.getContext().getServletContext()
+                    .getAttribute(attribute)));
+        }
+    }
+    
+    protected class CookieElement implements AccessLogElement {
+        private String name;
+        
+        public CookieElement(String name) {
+            this.name = name;
+        }
+        public void addElement(StringBuffer buf, Date date, Request request,
+                Response response, long time) {
+            Cookie[] c = request.getCookies();
+            for (int i = 0; c != null && i < c.length; i++) {
+                if (name.equals(c[i].getName())) {
+                    buf.append(wrap(c[i].getValue()));
+                }
+            }
         }
-
     }
-
-
-    /**
-     * Open the new log file for the date specified by <code>dateStamp</code>.
-     */
-    private synchronized void open() {
-
-        // Create the directory if necessary
-        File dir = new File(directory);
-        if (!dir.isAbsolute())
-            dir = new File(System.getProperty("catalina.base"), directory);
-        dir.mkdirs();
-
-        // Open the current log file
-        try {
-            String pathname;
-
-            // If no rotate - no need for dateStamp in fileName
-            if (rotatable){
-                pathname = dir.getAbsolutePath() + File.separator +
-                            prefix + dateStamp + suffix;
+    
+    protected class RequestAttributeElement implements AccessLogElement { 
+        private String attribute;
+        
+        public RequestAttributeElement(String attribute) {
+            this.attribute = attribute;
+        }
+        
+        public void addElement(StringBuffer buf, Date date, Request request,
+                Response response, long time) {
+            buf.append(wrap(request.getAttribute(attribute)));
+        }        
+    }
+    
+    protected class SessionAttributeElement implements AccessLogElement {
+        private String attribute;
+        
+        public SessionAttributeElement(String attribute) {
+            this.attribute = attribute;
+        }
+        public void addElement(StringBuffer buf, Date date, Request request,
+                Response response, long time) {
+            HttpSession session = null;
+            if (request != null) {
+                session = request.getSession(false);
+                if (session != null)
+                    buf.append(wrap(session.getAttribute(attribute)));
+            }
+        }
+    }
+    
+    protected class RequestParameterElement implements AccessLogElement {
+        private String parameter;
+        
+        public RequestParameterElement(String parameter) {
+            this.parameter = parameter;
+        }
+        /**
+         *  urlEncode the given string. If null or empty, return null.
+         */
+        private String urlEncode(String value) {
+            if (null==value || value.length()==0) {
+                return null;
+            }
+            return URLEncoder.encode(value);
+        }   
+        
+        public void addElement(StringBuffer buf, Date date, Request request,
+                Response response, long time) {
+            buf.append(wrap(urlEncode(request.getParameter(parameter))));
+        }
+    }
+    
+    protected class PatternTokenizer {
+        private StringReader sr = null;
+        private StringBuffer buf = new StringBuffer();
+        private boolean ended = false;
+        private boolean subToken;
+        private boolean parameter;
+        
+        public PatternTokenizer(String str) {
+            sr = new StringReader(str);
+        }
+        
+        public boolean hasSubToken() {
+            return subToken;
+        }
+        
+        public boolean hasParameter() {
+            return parameter;
+        }
+        
+        public String getToken() throws IOException {
+            String result = null;
+            subToken = false;
+            parameter = false;
+            
+            int c = sr.read();
+            while (c != -1) {
+                switch (c) {
+                case ' ':
+                    result = buf.toString();
+                    buf = new StringBuffer();
+                    buf.append((char) c);
+                    return result;
+                case '-':
+                    result = buf.toString();
+                    buf = new StringBuffer();
+                    subToken = true;
+                    return result;
+                case '(':
+                    result = buf.toString();
+                    buf = new StringBuffer();
+                    parameter = true;
+                    return result;
+                case ')':
+                    result = buf.toString();
+                    buf = new StringBuffer();
+                    break;
+                default:
+                    buf.append((char) c);
+                }
+                c = sr.read();
+            }
+            ended = true;
+            if (buf.length() != 0) {
+                return buf.toString();
             } else {
-                pathname = dir.getAbsolutePath() + File.separator +
-                            prefix + suffix;
+                return null;
             }
-
-            currentLogFile = new File(pathname);
-            writer = new PrintWriter(new FileWriter(pathname, true), true);
-            if (currentLogFile.length()==0) {
-                writer.println("#Fields: " + pattern);
-                writer.println("#Version: 1.0");
-                writer.println("#Software: " + ServerInfo.getServerInfo());
+        }
+        
+        public String getParameter()throws IOException {
+            String result;
+            if (!parameter) {
+                return null;
             }
-
-
-        } catch (IOException e) {
-            writer = null;
-            currentLogFile = null;
+            parameter = false;
+            int c = sr.read();
+            while (c != -1) {
+                if (c == ')') {
+                    result = buf.toString();
+                    buf = new StringBuffer();
+                    return result;
+                }
+                buf.append((char) c);
+                c = sr.read();
+            }
+            return null;
         }
-
-    }
-
-
-    /**
-     * This method returns a Date object that is accurate to within one
-     * second.  If a thread calls this method to get a Date and it's been
-     * less than 1 second since a new Date was created, this method
-     * simply gives out the same Date again so that the system doesn't
-     * spend time creating Date objects unnecessarily.
-     */
-    private Date getDate(long systime) {
-        /* Avoid extra call to System.currentTimeMillis(); */
-        if (0==systime) {
-            systime = System.currentTimeMillis();
+        
+        public String getWhiteSpaces() throws IOException {
+            StringBuffer whiteSpaces = new StringBuffer();
+            if (buf.length() > 0) {
+                whiteSpaces.append(buf);
+                buf = new StringBuffer();
+            }
+            int c = sr.read();
+            while (Character.isWhitespace((char) c)) {
+                whiteSpaces.append((char) c);
+                c = sr.read();
+            }
+            if (c == -1) {
+                ended = true;
+            } else {
+                buf.append((char) c);
+            }
+            return whiteSpaces.toString();
         }
-
-        // Only create a new Date once per second, max.
-        if ((systime - currentDate.getTime()) > 1000) {
-            currentDate.setTime(systime);
+        
+        public boolean isEnded() {
+            return ended;
         }
-
-        return currentDate;
-
-    }
-
-
-    // ------------------------------------------------------ Lifecycle Methods
-
-
-    /**
-     * Add a lifecycle event listener to this component.
-     *
-     * @param listener The listener to add
-     */
-    public void addLifecycleListener(LifecycleListener listener) {
-
-        lifecycle.addLifecycleListener(listener);
-
-    }
-
-
-    /**
-     * Get the lifecycle listeners associated with this lifecycle. If this
-     * Lifecycle has no listeners registered, a zero-length array is returned.
-     */
-    public LifecycleListener[] findLifecycleListeners() {
-
-        return lifecycle.findLifecycleListeners();
-
-    }
-
-
-    /**
-     * Remove a lifecycle event listener from this component.
-     *
-     * @param listener The listener to add
-     */
-    public void removeLifecycleListener(LifecycleListener listener) {
-
-        lifecycle.removeLifecycleListener(listener);
-
-    }
-
-
-    /**
-     * Prepare for the beginning of active use of the public methods of this
-     * component.  This method should be called after <code>configure()</code>,
-     * and before any of the public methods of the component are utilized.
-     *
-     * @exception LifecycleException if this component detects a fatal error
-     *  that prevents this component from being used
-     */
-    public void start() throws LifecycleException {
-
-        // Validate and update our current component state
-        if (started)
-            throw new LifecycleException
-                (sm.getString("extendedAccessLogValve.alreadyStarted"));
-        lifecycle.fireLifecycleEvent(START_EVENT, null);
-        started = true;
-
-        // Initialize the timeZone, Date formatters, and currentDate
-        TimeZone tz = TimeZone.getTimeZone("GMT");
-        dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
-        dateFormatter.setTimeZone(tz);
-        timeFormatter = new SimpleDateFormat("HH:mm:ss");
-        timeFormatter.setTimeZone(tz);
-        currentDate = new Date(System.currentTimeMillis());
-        if (fileDateFormat==null || fileDateFormat.length()==0)
-            fileDateFormat = "yyyy-MM-dd";
-        fileDateFormatter = new SimpleDateFormat(fileDateFormat);
-        dateStamp = fileDateFormatter.format(currentDate);
-        timeTakenFormatter = new DecimalFormat("0.000");
-
-        /* Everybody say ick ... ick */
-        try {
-            InetAddress inetAddress = InetAddress.getLocalHost();
-            myIpAddress = inetAddress.getHostAddress();
-            myDNSName = inetAddress.getHostName();
-        } catch(Throwable e){
-            myIpAddress="127.0.0.1";
-            myDNSName="localhost";
+        
+        public String getRemains() throws IOException {
+            StringBuffer remains = new StringBuffer();
+            for(int c = sr.read(); c != -1; c = sr.read()) {
+                remains.append((char) c);
+            }
+            return remains.toString();
         }
-
-        open();
-
+        
     }
+    
+    protected AccessLogElement[] createLogElements() {
+        if (log.isDebugEnabled()) {
+            log.debug("decodePattern, pattern =" + pattern);
+        }
+        List<AccessLogElement> list = new ArrayList<AccessLogElement>();
 
+        PatternTokenizer tokenizer = new PatternTokenizer(pattern);
+        try {
 
-    /**
-     * Gracefully terminate the active use of the public methods of this
-     * component.  This method should be the last one called on a given
-     * instance of this component.
-     *
-     * @exception LifecycleException if this component detects a fatal error
-     *  that needs to be reported
-     */
-    public void stop() throws LifecycleException {
-
-        // Validate and update our current component state
-        if (!started)
-            throw new LifecycleException
-                (sm.getString("extendedAccessLogValve.notStarted"));
-        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
-        started = false;
-
-        close();
-
-    }
-
-
-    /**
-     * Decode the given pattern. Is public so a pattern may
-     * allows to be validated.
-     * @param fields The pattern to decode
-     * @return null on error.  Otherwise array of decoded fields
-     */
-    public FieldInfo[] decodePattern(String fields) {
-
-        if (log.isDebugEnabled())
-            log.debug("decodePattern, fields=" + fields);
-
-        LinkedList list = new LinkedList();
+            // Ignore leading whitespace.
+            tokenizer.getWhiteSpaces();
 
-        //Ignore leading whitespace.
-        int i=0;
-        for (;i<fields.length() && Character.isWhitespace(fields.charAt(i));i++);
+            if (tokenizer.isEnded()) {
+                log.info("pattern was just empty or whitespace");
+                return null;
+            }
 
-        if (i>=fields.length()) {
-            log.info("fields was just empty or whitespace");
+            String token = tokenizer.getToken();
+            while (token != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("token = " + token);
+                }
+                AccessLogElement element = getLogElement(token, tokenizer);
+                if (element == null) {
+                    break;
+                }
+                list.add(element);
+                String whiteSpaces = tokenizer.getWhiteSpaces();
+                if (whiteSpaces.length() > 0) {
+                    list.add(new StringElement(whiteSpaces));
+                }
+                if (tokenizer.isEnded()) {
+                    break;
+                }
+                token = tokenizer.getToken();
+            }
+            if (log.isDebugEnabled()) {
+                log.debug("finished decoding with element size of: " + list.size());
+            }
+            return (AccessLogElement[]) list.toArray(new AccessLogElement[0]);
+        } catch (IOException e) {
+            log.error("parse error", e);
             return null;
         }
-
-        int j;
-        while(i<fields.length()) {
-            if (log.isDebugEnabled())
-                log.debug("fields.substring(i)=" + fields.substring(i));
-
-            FieldInfo currentFieldInfo = new FieldInfo();
-
-
-            if (fields.startsWith("date",i)) {
-                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
-                currentFieldInfo.location = FieldInfo.SPECIAL_DATE;
-                i+="date".length();
-            } else if (fields.startsWith("time-taken",i)) {
-                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
-                currentFieldInfo.location = FieldInfo.SPECIAL_TIME_TAKEN;
-                i+="time-taken".length();
-            } else if (fields.startsWith("time",i)) {
-                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
-                currentFieldInfo.location = FieldInfo.SPECIAL_TIME;
-                i+="time".length();
-            } else if (fields.startsWith("bytes",i)) {
-                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
-                currentFieldInfo.location = FieldInfo.SPECIAL_BYTES;
-                i+="bytes".length();
-            } else if (fields.startsWith("cached",i)) {
-                currentFieldInfo.type = FieldInfo.DATA_SPECIAL;
-                currentFieldInfo.location = FieldInfo.SPECIAL_CACHED;
-                i+="cached".length();
-            } else if (fields.startsWith("c-ip",i)) {
-                currentFieldInfo.type = FieldInfo.DATA_CLIENT;
-                currentFieldInfo.location = FieldInfo.FIELD_IP;
-                i+="c-ip".length();
-            } else if (fields.startsWith("c-dns",i)) {
-                currentFieldInfo.type = FieldInfo.DATA_CLIENT;
-                currentFieldInfo.location = FieldInfo.FIELD_DNS;
-                i+="c-dns".length();
-            } else if (fields.startsWith("s-ip",i)) {
-                currentFieldInfo.type = FieldInfo.DATA_SERVER;
-                currentFieldInfo.location = FieldInfo.FIELD_IP;
-                i+="s-ip".length();
-            } else if (fields.startsWith("s-dns",i)) {
-                currentFieldInfo.type = FieldInfo.DATA_SERVER;
-                currentFieldInfo.location = FieldInfo.FIELD_DNS;
-                i+="s-dns".length();
-            } else if (fields.startsWith("cs",i)) {
-                i = decode(fields, i+2, currentFieldInfo,
-                            FieldInfo.DATA_CLIENT_TO_SERVER);
-                if (i<0)
-                    return null;
-            } else if (fields.startsWith("sc",i)) {
-                i = decode(fields, i+2, currentFieldInfo,
-                            FieldInfo.DATA_SERVER_TO_CLIENT);
-                if (i<0)
-                    return null;
-            } else if (fields.startsWith("sr",i)) {
-                i = decode(fields, i+2, currentFieldInfo,
-                            FieldInfo.DATA_SERVER_TO_RSERVER);
-                if (i<0)
-                    return null;
-            } else if (fields.startsWith("rs",i)) {
-                i = decode(fields, i+2, currentFieldInfo,
-                            FieldInfo.DATA_RSERVER_TO_SERVER);
-                if (i<0)
-                    return null;
-            } else if (fields.startsWith("x",i)) {
-                i = decodeAppSpecific(fields, i, currentFieldInfo);
+    }
+    
+    protected AccessLogElement getLogElement(String token, PatternTokenizer tokenizer) throws IOException {
+        if ("date".equals(token)) {
+            return new DateElement();
+        } else if ("time".equals(token)) {
+            if (tokenizer.hasSubToken()) {
+                String nextToken = tokenizer.getToken();
+                if ("taken".equals(nextToken)) {
+                    return new ElapsedTimeElement(false);                
+                }
             } else {
-                // Unable to decode ...
-                log.error("unable to decode with rest of chars being: " +
-                            fields.substring(i));
-                return null;
+                return new TimeElement();
             }
-
-            // By this point we should have the field, get the whitespace
-            j=i;
-            for (;j<fields.length() && Character.isWhitespace(fields.charAt(j));j++);
-
-            if (j>=fields.length()) {
-                if (j==i) {
-                    // Special case - end of string
-                    currentFieldInfo.postWhiteSpace = "";
+        } else if ("bytes".equals(token)) {
+            return new ByteSentElement(true);
+        } else if ("cached".equals(token)) {
+            /* I don't know how to evaluate this! */
+            return new StringElement("-");
+        } else if ("c".equals(token)) {
+            String nextToken = tokenizer.getToken();
+            if ("ip".equals(nextToken)) {
+                return new RemoteAddrElement();
+            } else if ("dns".equals(nextToken)) {
+                return new HostElement();
+            }
+        } else if ("s".equals(token)) {
+            String nextToken = tokenizer.getToken();
+            if ("ip".equals(nextToken)) {
+                return new LocalAddrElement();
+            } else if ("dns".equals(nextToken)) {
+                return new AccessLogElement() {
+                    public void addElement(StringBuffer buf, Date date,
+                            Request request, Response response, long time) {
+                        String value;
+                        try {
+                            value = InetAddress.getLocalHost().getHostName();
+                        } catch (Throwable e) {
+                            value = "localhost";
+                        }
+                        buf.append(value);
+                    }
+                };
+            }
+        } else if ("cs".equals(token)) {
+            return getClientToServerElement(tokenizer);
+        } else if ("sc".equals(token)) {
+            return getServerToClientElement(tokenizer);
+        } else if ("sr".equals(token) || "rs".equals(token)) {
+            return getProxyElement(tokenizer);
+        } else if ("x".equals(token)) {
+            return getXParameterElement(tokenizer);
+        }
+        log.error("unable to decode with rest of chars starting: " + token);
+        return null;
+    }
+    
+    protected AccessLogElement getClientToServerElement(
+            PatternTokenizer tokenizer) throws IOException {
+        if (tokenizer.hasSubToken()) {
+            String token = tokenizer.getToken();
+            if ("method".equals(token)) {
+                return new MethodElement();
+            } else if ("uri".equals(token)) {
+                if (tokenizer.hasSubToken()) {
+                    token = tokenizer.getToken();
+                    if ("stem".equals(token)) {
+                        return new RequestURIElement();
+                    } else if ("query".equals(token)) {
+                        return new AccessLogElement() {
+                            public void addElement(StringBuffer buf, Date date,
+                                    Request request, Response response,
+                                    long time) {
+                                String query = request.getQueryString();
+                                if (query != null) {
+                                    buf.append(query);
+                                } else {
+                                    buf.append('-');
+                                }
+                            }
+                        };
+                    }
                 } else {
-                    currentFieldInfo.postWhiteSpace = fields.substring(i);
-                    i=j;
+                    return new AccessLogElement() {
+                        public void addElement(StringBuffer buf, Date date,
+                                Request request, Response response, long time) {
+                            String query = request.getQueryString();
+                            if (query != null) {
+                                buf.append(request.getRequestURI());
+                            } else {
+                                buf.append(request.getRequestURI());
+                                buf.append('?');
+                                buf.append(request.getQueryString());
+                            }
+                        }
+                    };
                 }
-            } else {
-                currentFieldInfo.postWhiteSpace = fields.substring(i,j);
-                i=j;
             }
-
-            list.add(currentFieldInfo);
+        } else if (tokenizer.hasParameter()) {
+            String parameter = tokenizer.getParameter();
+            if (parameter == null) {
+                log.error("No closing ) found for in decode");
+                return null;
+            }
+            return new RequestHeaderElement(parameter);
         }
-
-        i=0;
-        FieldInfo[] f = new FieldInfo[list.size()];
-        for (Iterator k = list.iterator(); k.hasNext();)
-             f[i++] = (FieldInfo)k.next();
-
-        if (log.isDebugEnabled())
-            log.debug("finished decoding with length of: " + i);
-
-        return f;
-    }
-
-    /**
-     * Decode the cs or sc fields.
-     * Returns negative on error.
-     *
-     * @param fields The pattern to decode
-     * @param i The string index where we are decoding.
-     * @param fieldInfo Where to store the results
-     * @param type The type we are decoding.
-     * @return -1 on error. Otherwise the new String index.
-     */
-    private int decode(String fields, int i, FieldInfo fieldInfo, short type) {
-
-        if (fields.startsWith("-status",i)) {
-            fieldInfo.location = FieldInfo.FIELD_STATUS;
-            i+="-status".length();
-        } else if (fields.startsWith("-comment",i)) {
-            fieldInfo.location = FieldInfo.FIELD_COMMENT;
-            i+="-comment".length();
-        } else if (fields.startsWith("-uri-query",i)) {
-            fieldInfo.location = FieldInfo.FIELD_URI_QUERY;
-            i+="-uri-query".length();
-        } else if (fields.startsWith("-uri-stem",i)) {
-            fieldInfo.location = FieldInfo.FIELD_URI_STEM;
-            i+="-uri-stem".length();
-        } else if (fields.startsWith("-uri",i)) {
-            fieldInfo.location = FieldInfo.FIELD_URI;
-            i+="-uri".length();
-        } else if (fields.startsWith("-method",i)) {
-            fieldInfo.location = FieldInfo.FIELD_METHOD;
-            i+="-method".length();
-        } else if (fields.startsWith("(",i)) {
-            fieldInfo.location = FieldInfo.FIELD_HEADER;
-            i++;                                  /* Move past the ( */
-            int j = fields.indexOf(')', i);
-            if (j==-1) {                          /* Not found */
+        log.error("The next characters couldn't be decoded: "
+                + tokenizer.getRemains());
+        return null;
+    }
+    
+    protected AccessLogElement getServerToClientElement(
+            PatternTokenizer tokenizer) throws IOException {
+        if (tokenizer.hasSubToken()) {
+            String token = tokenizer.getToken();
+            if ("status".equals(token)) {
+                return new HttpStatusCodeElement();
+            } else if ("comment".equals(token)) {
+                return new StringElement("?");
+            }
+        } else if (tokenizer.hasParameter()) {
+            String parameter = tokenizer.getParameter();
+            if (parameter == null) {
                 log.error("No closing ) found for in decode");
-                return -1;
+                return null;
             }
-            fieldInfo.value = fields.substring(i,j);
-            i=j+1;                                // Move pointer past ) */
-        } else {
-            log.error("The next characters couldn't be decoded: " + fields.substring(i));
-            return -1;
+            return new ResponseHeaderElement(parameter);
         }
-
-        fieldInfo.type = type;
-        return i;
-
-    }
-
-
-    /**
-      * Decode app specific log entry.
-      *
-      * Special fields are of the form:
-      * x-C(...) - For cookie
-      * x-A(...) - Value in servletContext
-      * x-S(...) - Value in session
-      * x-R(...) - Value in servletRequest
-      * @param fields The pattern to decode
-      * @param i The string index where we are decoding.
-      * @param fieldInfo Where to store the results
-      * @return -1 on error. Otherwise the new String index.
-      */
-    private int decodeAppSpecific(String fields, int i, FieldInfo fieldInfo) {
-
-        fieldInfo.type = FieldInfo.DATA_APP_SPECIFIC;
-        /* Move past 'x-' */
-        i+=2;
-
-        if (i>=fields.length()) {
-            log.error("End of line reached before decoding x- param");
-            return -1;
-        }
-
-        switch(fields.charAt(i)) {
-            case 'A':
-                fieldInfo.xType = FieldInfo.X_APP;
-                break;
-            case 'C':
-                fieldInfo.xType = FieldInfo.X_COOKIE;
-                break;
-            case 'R':
-                fieldInfo.xType = FieldInfo.X_REQUEST;
-                break;
-            case 'S':
-                fieldInfo.xType = FieldInfo.X_SESSION;
-                break;
-            case 'H':
-                fieldInfo.xType = FieldInfo.X_SERVLET_REQUEST;
-                break;
-            case 'P':
-                fieldInfo.xType = FieldInfo.X_PARAMETER;
-                break;
-            default:
-                return -1;
+        log.error("The next characters couldn't be decoded: "
+                + tokenizer.getRemains());
+        return null;
+    }
+    
+    protected AccessLogElement getProxyElement(PatternTokenizer tokenizer)
+        throws IOException {
+        String token = null;
+        if (tokenizer.hasSubToken()) {
+            token = tokenizer.getToken();
+            return new StringElement("-");
+        } else if (tokenizer.hasParameter()) {
+            tokenizer.getParameter();
+            return new StringElement("-");
+        }
+        log.error("The next characters couldn't be decoded: " + token);
+        return null;
+    }
+    
+    protected AccessLogElement getXParameterElement(PatternTokenizer tokenizer)
+            throws IOException {
+        if (!tokenizer.hasSubToken()) {
+            log.error("x param in wrong format. Needs to be 'x-#(...)' read the docs!");
+            return null;
         }
-
-        /* test that next char is a ( */
-        if (i+1!=fields.indexOf('(',i)) {
+        String token = tokenizer.getToken();
+        if (!tokenizer.hasParameter()) {
             log.error("x param in wrong format. Needs to be 'x-#(...)' read the docs!");
-            return -1;
+            return null;
         }
-        i+=2; /* Move inside of the () */
-
-        /* Look for ending ) and return error if not found. */
-        int j = fields.indexOf(')',i);
-        if (j==-1) {
-            log.error("x param in wrong format. No closing ')'!");
-            return -1;
-        }
-
-        fieldInfo.value = fields.substring(i,j);
-
-        if (fieldInfo.xType == FieldInfo.X_SERVLET_REQUEST) {
-            if ("authType".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_AUTHTYPE;
-            } else if ("remoteUser".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_REMOTEUSER;
-            } else if ("requestedSessionId".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONID;
-            } else if ("requestedSessionIdFromCookie".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONIDFROMCOOKIE;
-            } else if ("requestedSessionIdValid".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONIDVALID;
-            } else if ("contentLength".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_CONTENTLENGTH;
-            } else if ("characterEncoding".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_CHARACTERENCODING;
-            } else if ("locale".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_LOCALE;
-            } else if ("protocol".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_PROTOCOL;
-            } else if ("scheme".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_SCHEME;
-            } else if ("secure".equals(fieldInfo.value)){
-                fieldInfo.location = FieldInfo.X_LOC_SECURE;
-            } else {
-                log.error("x param for servlet request, couldn't decode value: " +
-                            fieldInfo.location);
-                return -1;
-            }
+        String parameter = tokenizer.getParameter();
+        if (parameter == null) {
+            log.error("No closing ) found for in decode");
+            return null;
         }
-
-        return j+1;
-
+        if ("A".equals(token)) {
+            return new ServletContextElement(parameter);
+        } else if ("C".equals(token)) {
+            return new CookieElement(parameter);
+        } else if ("R".equals(token)) {
+            return new RequestAttributeElement(parameter);
+        } else if ("S".equals(token)) {
+            return new SessionAttributeElement(parameter);
+        } else if ("H".equals(token)) {
+            return getServletRequestElement(parameter);
+        } else if ("P".equals(token)) {
+            return new RequestParameterElement(parameter);
+        }
+        log.error("x param for servlet request, couldn't decode value: "
+                + token);
+        return null;
+    }
+    
+    protected AccessLogElement getServletRequestElement(String parameter) {
+        if ("authType".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(wrap(request.getAuthType()));
+                }
+            };
+        } else if ("remoteUser".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(wrap(request.getRemoteUser()));
+                }
+            };
+        } else if ("requestedSessionId".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(wrap(request.getRequestedSessionId()));
+                }
+            };
+        } else if ("requestedSessionIdFromCookie".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(wrap(""
+                            + request.isRequestedSessionIdFromCookie()));
+                }
+            };
+        } else if ("requestedSessionIdValid".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(wrap("" + request.isRequestedSessionIdValid()));
+                }
+            };
+        } else if ("contentLength".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(wrap("" + request.getContentLength()));
+                }
+            };
+        } else if ("characterEncoding".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(wrap(request.getCharacterEncoding()));
+                }
+            };
+        } else if ("locale".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(wrap(request.getLocale()));
+                }
+            };
+        } else if ("protocol".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(wrap(request.getProtocol()));
+                }
+            };
+        } else if ("scheme".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(request.getScheme());
+                }
+            };
+        } else if ("secure".equals(parameter)) {
+            return new AccessLogElement() {
+                public void addElement(StringBuffer buf, Date date,
+                        Request request, Response response, long time) {
+                    buf.append(wrap("" + request.isSecure()));
+                }
+            };
+        }
+        log.error("x param for servlet request, couldn't decode value: "
+                + parameter);
+        return null;
     }
-
-
-}
-
-/**
- * A simple helper for decoding the pattern.
- */
-class FieldInfo {
-    /*
-       The goal of the constants listed below is to make the construction of the log
-       entry as quick as possible via numerci decodings of the methods to call instead
-       of performing many String comparisons on each logging request.
-    */
-
-    /* Where the data is located. */
-    static final short DATA_CLIENT = 0;
-    static final short DATA_SERVER = 1;
-    static final short DATA_REMOTE = 2;
-    static final short DATA_CLIENT_TO_SERVER = 3;
-    static final short DATA_SERVER_TO_CLIENT = 4;
-    static final short DATA_SERVER_TO_RSERVER = 5; /* Here to honor the spec. */
-    static final short DATA_RSERVER_TO_SERVER = 6; /* Here to honor the spec. */
-    static final short DATA_APP_SPECIFIC = 7;
-    static final short DATA_SPECIAL = 8;
-
-    /* The type of special fields. */
-    static final short SPECIAL_DATE         = 1;
-    static final short SPECIAL_TIME_TAKEN   = 2;
-    static final short SPECIAL_TIME         = 3;
-    static final short SPECIAL_BYTES        = 4;
-    static final short SPECIAL_CACHED       = 5;
-
-    /* Where to pull the data for prefixed values */
-    static final short FIELD_IP            = 1;
-    static final short FIELD_DNS           = 2;
-    static final short FIELD_STATUS        = 3;
-    static final short FIELD_COMMENT       = 4;
-    static final short FIELD_METHOD        = 5;
-    static final short FIELD_URI           = 6;
-    static final short FIELD_URI_STEM      = 7;
-    static final short FIELD_URI_QUERY     = 8;
-    static final short FIELD_HEADER        = 9;
-
-
-    /* Application Specific parameters */
-    static final short X_REQUEST = 1; /* For x app specific */
-    static final short X_SESSION = 2; /* For x app specific */
-    static final short X_COOKIE  = 3; /* For x app specific */
-    static final short X_APP     = 4; /* For x app specific */
-    static final short X_SERVLET_REQUEST = 5; /* For x app specific */
-    static final short X_PARAMETER = 6; /* For x app specific */
-
-    static final short X_LOC_AUTHTYPE                       = 1;
-    static final short X_LOC_REMOTEUSER                     = 2;
-    static final short X_LOC_REQUESTEDSESSIONID             = 3;
-    static final short X_LOC_REQUESTEDSESSIONIDFROMCOOKIE   = 4;
-    static final short X_LOC_REQUESTEDSESSIONIDVALID        = 5;
-    static final short X_LOC_CONTENTLENGTH                  = 6;
-    static final short X_LOC_CHARACTERENCODING              = 7;
-    static final short X_LOC_LOCALE                         = 8;
-    static final short X_LOC_PROTOCOL                       = 9;
-    static final short X_LOC_SCHEME                         = 10;
-    static final short X_LOC_SECURE                         = 11;
-
-
-
-    /** The field type */
-    short type;
-
-    /** Where to pull the data from? Icky variable name. */
-    short location;
-
-    /** The x- specific place to pull the data from. */
-    short xType;
-
-    /** The field value if needed. Needed for headers and app specific. */
-    String value;
-
-    /** Any white space after this field? Put it here. */
-    String postWhiteSpace = null;
-
+        
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org