KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > enterprise > web > PEAccessLogValve


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 package com.sun.enterprise.web;
25
26
27 import java.io.File JavaDoc;
28 import java.io.FileWriter JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.FileOutputStream JavaDoc;
31
32 import java.net.InetAddress JavaDoc;
33
34 import java.nio.ByteBuffer JavaDoc;
35 import java.nio.CharBuffer JavaDoc;
36 import java.nio.BufferOverflowException JavaDoc;
37 import java.nio.channels.FileChannel JavaDoc;
38     
39 import java.text.SimpleDateFormat JavaDoc;
40 import java.text.DecimalFormat JavaDoc;
41
42 import java.util.Date JavaDoc;
43 import java.util.LinkedList JavaDoc;
44 import java.util.TimeZone JavaDoc;
45 import java.util.logging.Logger JavaDoc;
46 import java.util.logging.Level JavaDoc;
47
48 import javax.servlet.ServletException JavaDoc;
49 import javax.servlet.ServletRequest JavaDoc;
50 import javax.servlet.ServletResponse JavaDoc;
51 import javax.servlet.http.Cookie JavaDoc;
52 import javax.servlet.http.HttpServletRequest JavaDoc;
53 import javax.servlet.http.HttpServletResponse JavaDoc;
54 import javax.servlet.http.HttpSession JavaDoc;
55
56 import org.apache.catalina.Container;
57 import org.apache.catalina.HttpResponse;
58 import org.apache.catalina.Lifecycle;
59 import org.apache.catalina.LifecycleEvent;
60 import org.apache.catalina.LifecycleException;
61 import org.apache.catalina.LifecycleListener;
62 import org.apache.catalina.Request;
63 import org.apache.catalina.Response;
64 import org.apache.catalina.util.LifecycleSupport;
65 import org.apache.catalina.util.StringManager;
66 import org.apache.catalina.valves.ValveBase;
67
68 import com.sun.logging.LogDomains;
69
70 /**
71  * <p>Implementation of the <b>Valve</b> interface that generates a web server
72  * access log with the detailed line contents matching a configurable pattern.
73  * The syntax of the available patterns is similar to that supported by the
74  * Apache <code>mod_log_config</code> module. As an additional feature,
75  * automatic rollover of log files at a specified interval is also supported.
76  *
77  * </p>This class uses a direct <code>ByteBuffer</code> to store and write
78  * logs.
79  *
80  * @author Jean-Francois Arcand
81  * @author Charlie J. Hunt
82  */

83
84 public final class PEAccessLogValve
85     extends ValveBase
86     implements Lifecycle, Runnable JavaDoc {
87
88     private static final Logger JavaDoc _logger =
89         LogDomains.getLogger(LogDomains.WEB_LOGGER);
90
91     /**
92      * Name of the system property whose value specifies the max number of
93      * access log history files to keep.
94      * If this property has been specified without any value, a default value
95      * of 10 is used.
96      * Else, if it has been specified with a value of 0, no access log
97      * history files will be maintained, and the current access log file will
98      * be reset after each rotation.
99      * If undefined, all access log history files will be preserved.
100      */

101     private static final String JavaDoc LOGGING_MAX_HISTORY_FILES =
102         "com.sun.enterprise.server.logging.max_history_files";
103
104     /*
105      * HTTP header names
106      */

107     private static final String JavaDoc HTTP_HEADER_ACCEPT = "Accept";
108     private static final String JavaDoc HTTP_HEADER_AUTHORIZATION = "Authorization";
109     private static final String JavaDoc HTTP_HEADER_DATE = "Date";
110     private static final String JavaDoc HTTP_HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
111
112     /*
113      * Supported access log entry tokens
114      */

115     private static final String JavaDoc AUTH_USER_NAME = "auth-user-name";
116     private static final String JavaDoc CLIENT_DNS = "client.dns";
117     private static final String JavaDoc CLIENT_NAME = "client.name";
118     private static final String JavaDoc COOKIE_VALUE = "cookie.value";
119     private static final String JavaDoc DATE_TIME = "datetime";
120     private static final String JavaDoc HEADER_ACCEPT = "header.accept";
121     private static final String JavaDoc HEADER_ANY = "header.";
122     private static final int HEADER_ANY_LEN = HEADER_ANY.length();
123     private static final String JavaDoc HEADER_AUTH = "header.auth";
124     private static final String JavaDoc HEADER_DATE = "header.date";
125     private static final String JavaDoc HEADER_IF_MOD_SINCE = "header.if-mod-since";
126     private static final String JavaDoc HEADER_USER_AGENT = "header.user-agent";
127     private static final String JavaDoc HEADER_REFERER = "header.referer";
128     private static final String JavaDoc HTTP_METHOD = "http-method";
129     private static final String JavaDoc HTTP_URI = "http-uri";
130     private static final String JavaDoc HTTP_VERSION = "http-version";
131     private static final String JavaDoc QUERY_STR = "query-str";
132     private static final String JavaDoc REFERER = "referer";
133     private static final String JavaDoc REQUEST = "request";
134     private static final String JavaDoc RESPONSE_LENGTH = "response.length";
135     private static final String JavaDoc STATUS = "status";
136     private static final String JavaDoc USER_AGENT = "user.agent";
137     private static final String JavaDoc VS_ID = "vs.id";
138
139
140     // ----------------------------------------------------- Instance Variables
141

142
143     /**
144      * The directory in which log files are created.
145      */

146     private String JavaDoc directory = "logs";
147
148
149     /**
150      * The descriptive information about this implementation.
151      */

152     protected static final String JavaDoc info =
153         "com.sun.enterprise.web.PEAccessLogValve/1.0";
154
155
156     /**
157      * The lifecycle event support for this component.
158      */

159     protected LifecycleSupport lifecycle = new LifecycleSupport(this);
160
161
162     /**
163      * The set of month abbreviations for log messages.
164      */

165     protected static final String JavaDoc months[] =
166     { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
167       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
168
169
170     /**
171      * The pattern used to format our access log lines.
172      */

173     private String JavaDoc pattern = null;
174
175
176     /**
177      * The prefix that is added to log file filenames.
178      */

179     private String JavaDoc prefix = "";
180
181
182     /**
183      * Should we rotate our log file?
184      */

185     private boolean rotatable;
186
187
188     /**
189      * The string manager for this package.
190      */

191     private StringManager sm =
192         StringManager.getManager(Constants.Package);
193
194
195     /**
196      * Has this component been started yet?
197      */

198     private boolean started = false;
199
200
201     /**
202      * The suffix that is added to log file filenames.
203      */

204     private String JavaDoc suffix = "";
205
206
207     /**
208      * If prefix ends in '.', and suffix starts with '.', we must remove the
209      * leading '.' from suffix when prefix and suffix are concatenated.
210      */

211     private boolean removeLeadingDotFromSuffix = false;
212
213
214     /**
215      * Suffix from which leading '.' has been removed if
216      * removeLeadingDotFromSuffix is true
217      */

218     private String JavaDoc dotLessSuffix = null;
219
220
221     /**
222      * A date formatter to format a Date into a date in the format
223      * "yyyy-MM-dd".
224      */

225     private SimpleDateFormat JavaDoc dateFormatter = null;
226
227
228     /**
229      * A date formatter to format Dates into a day string in the format
230      * "dd".
231      */

232     private SimpleDateFormat JavaDoc dayFormatter = null;
233
234
235     /**
236      * A date formatter to format a Date into a month string in the format
237      * "MM".
238      */

239     private SimpleDateFormat JavaDoc monthFormatter = null;
240
241
242     /**
243      * A date formatter to format a Date into a year string in the format
244      * "yyyy".
245      */

246     private SimpleDateFormat JavaDoc yearFormatter = null;
247
248
249     /**
250      * A date formatter to format a Date into a time in the format
251      * "kk:mm:ss" (kk is a 24-hour representation of the hour).
252      */

253     private SimpleDateFormat JavaDoc timeFormatter = null;
254
255
256     /**
257      * The time zone relative to GMT.
258      */

259     private String JavaDoc timeZone = null;
260
261
262     /**
263      * The system time when we last updated the Date that this valve
264      * uses for log lines.
265      */

266     private Date JavaDoc currentDate = null;
267
268
269     /**
270      * When formatting log lines, we often use strings like this one (" ").
271      */

272     private String JavaDoc space = " ";
273
274
275     /**
276      * Resolve hosts.
277      */

278     private boolean resolveHosts = false;
279
280
281     /**
282      * Instant when the log daily rotation was last checked.
283      */

284     private long lastAccessLogCreationTime = 0L;
285
286
287     /**
288      * Are we doing conditional logging. default false.
289      */

290     private String JavaDoc condition = null;
291
292
293     /**
294      * Date format to place in log file name. Use at your own risk!
295      */

296     private String JavaDoc fileDateFormat = null;
297     
298     
299     /**
300      * The <code>FileChannel</code> used to write the access log.
301      */

302     protected FileChannel JavaDoc fileChannel;
303     
304     
305     /**
306      * The stream used to store the logs.
307      */

308     FileOutputStream JavaDoc fos;
309     
310     
311     /**
312      * The interval (in seconds) between writing the logs
313      */

314     private int writeInterval;
315
316    
317     /**
318      * The interval between rotating the logs
319      */

320     private int rotationInterval;
321     
322     
323     /**
324      * The background writerThread.
325      */

326     private Thread JavaDoc writerThread = null;
327     
328     
329     /**
330      * The background writerThread completion semaphore.
331      */

332     private boolean threadDone = false;
333
334     
335     /**
336      * The <code>CharBuffer</code> used to store the logs.
337      */

338     private CharBuffer JavaDoc charBuffer;
339
340
341     /**
342      * The size of the last bytes written to the <code>CharBuffer</code>. This
343      * is used to predict and avoid BufferOverflow when writting to the i
344      * <code>CharBuffer</code>
345      */

346     public int chunkSize = 0;
347    
348     
349     /**
350      * The <code>byteBuffer</code> used to store the log.
351      */

352     protected int bufferSize = 512 * 1024;
353     private int expandedBufferSize;
354     
355     
356     /**
357      * If the writer interval is equals to zero, then always flush the
358      * direct byte buffer after every request.
359      */

360     protected boolean flushRealTime = true;
361     
362
363     /**
364      * Are we supposed to add datestamp to first access log file we create,
365      * or only after first rotation?
366      *
367      * If set to false, the current access log file will never have any
368      * date stamp: It will be moved to a date-stamped file upon rotation
369      */

370     private boolean addDateStampToFirstAccessLogFile;
371
372
373     /**
374      * List of access log pattern components
375      */

376     private LinkedList JavaDoc<String JavaDoc> patternComponents;
377
378
379     /**
380      * The current access log file.
381      */

382     private File JavaDoc logFile;
383
384
385     /**
386      * The maximum number of access log history files to keep
387      */

388     private int maxHistoryFiles;
389
390
391     /**
392      * True if no access log history files are to be kept, false otherwise
393      */

394     private boolean deleteAllHistoryFiles;
395
396
397     /**
398      * List of most recent access log history files (the size of this list
399      * is not to exceed <code>maxHistoryFiles</code>)
400      */

401     private LinkedList JavaDoc<File JavaDoc> historyFiles;
402
403
404     /**
405      * Return writerThread interval (seconds)
406      */

407     public int getWriterInterval() {
408         return writeInterval;
409     }
410
411         
412     /**
413      * Set writerthread interval (seconds)
414      */

415     public void setWriterInterval(int t) {
416         if ( t > 0 ){
417             flushRealTime = false;
418         }
419         writeInterval = t;
420     }
421     
422     
423     /**
424      * Return rotation interval
425      */

426     public int geRotationInterval() {
427         return rotationInterval;
428     }
429
430         
431     /**
432      * Set rotation interval
433      */

434     public void setRotationInterval(int t) {
435         rotationInterval = t;
436     }
437
438     
439     /**
440      * Set the direct <code>ByteBuffer</code> size
441      */

442     public void setBufferSize(int size){
443         if ( size > 0 ){
444             flushRealTime = false;
445         }
446         bufferSize = size;
447     }
448     
449     /**
450      * Return the direct <code>ByteBuffer</code> size
451      */

452     public int getBufferSize(){
453         return bufferSize;
454     }
455
456     // ------------------------------------------------------------- Properties
457

458
459     /**
460      * Are we supposed to add datestamp to first access log file we create,
461      * or only starting with first rotation?
462      */

463     public void setAddDateStampToFirstAccessLogFile(boolean add) {
464         this.addDateStampToFirstAccessLogFile = add;
465     }
466
467
468     /**
469      * Return the directory in which we create log files.
470      */

471     public String JavaDoc getDirectory() {
472
473         return (directory);
474
475     }
476
477
478     /**
479      * Set the directory in which we create log files.
480      *
481      * @param directory The new log file directory
482      */

483     public void setDirectory(String JavaDoc directory) {
484
485         this.directory = directory;
486
487     }
488
489
490     /**
491      * Return descriptive information about this implementation.
492      */

493     public String JavaDoc getInfo() {
494
495         return (this.info);
496
497     }
498
499
500     /**
501      * Return the format pattern.
502      */

503     public String JavaDoc getPattern() {
504
505         return (this.pattern);
506
507     }
508
509
510     /**
511      * Set the format pattern, first translating any recognized alias.
512      *
513      * @param p The new pattern
514      */

515     public void setPattern(String JavaDoc p) {
516         this.pattern = p;
517         this.patternComponents = parsePattern();
518     }
519
520
521     /**
522      * Return the log file prefix.
523      */

524     public String JavaDoc getPrefix() {
525
526         return (prefix);
527
528     }
529
530
531     /**
532      * Set the log file prefix.
533      *
534      * @param prefix The new log file prefix
535      */

536     public void setPrefix(String JavaDoc p) {
537
538         prefix = p;
539
540         if (prefix != null && suffix != null && prefix.endsWith(".")
541                 && suffix.startsWith(".")) {
542             removeLeadingDotFromSuffix = true;
543             dotLessSuffix = suffix.substring(1);
544         } else {
545             removeLeadingDotFromSuffix = false;
546         }
547     }
548
549
550     /**
551      * Should we rotate the logs
552      */

553     public boolean isRotatable() {
554
555         return rotatable;
556
557     }
558
559
560     /**
561      * Set the value is we should we rotate the logs
562      *
563      * @param rotatable true is we should rotate.
564      */

565     public void setRotatable(boolean rotatable) {
566
567         this.rotatable = rotatable;
568
569     }
570
571
572     /**
573      * Return the log file suffix.
574      */

575     public String JavaDoc getSuffix() {
576
577         return (suffix);
578
579     }
580
581
582     /**
583      * Set the log file suffix.
584      *
585      * @param suffix The new log file suffix
586      */

587     public void setSuffix(String JavaDoc s) {
588
589         suffix = s;
590
591         if (prefix != null && suffix != null && prefix.endsWith(".")
592                 && suffix.startsWith(".")) {
593             removeLeadingDotFromSuffix = true;
594             dotLessSuffix = suffix.substring(1);
595         } else {
596             removeLeadingDotFromSuffix = false;
597         }
598     }
599
600
601     /**
602      * Set the resolve hosts flag.
603      *
604      * @param resolveHosts The new resolve hosts value
605      */

606     public void setResolveHosts(boolean resolveHosts) {
607
608         this.resolveHosts = resolveHosts;
609
610     }
611
612
613     /**
614      * Get the value of the resolve hosts flag.
615      */

616     public boolean isResolveHosts() {
617
618         return resolveHosts;
619
620     }
621
622
623     /**
624      * Return whether the attribute name to look for when
625      * performing conditional loggging. If null, every
626      * request is logged.
627      */

628     public String JavaDoc getCondition() {
629
630         return condition;
631
632     }
633
634
635     /**
636      * Set the ServletRequest.attribute to look for to perform
637      * conditional logging. Set to null to log everything.
638      *
639      * @param condition Set to null to log everything
640      */

641     public void setCondition(String JavaDoc condition) {
642
643         this.condition = condition;
644
645     }
646
647     /**
648      * Return the date format date based log rotation.
649      */

650     public String JavaDoc getFileDateFormat() {
651         return fileDateFormat;
652     }
653
654
655     /**
656      * Set the date format date based log rotation.
657      */

658     public void setFileDateFormat(String JavaDoc fileDateFormat) {
659         this.fileDateFormat = fileDateFormat;
660     }
661     
662     
663     // --------------------------------------------------------- Public Methods
664

665
666     /**
667      * Log a message summarizing the specified request and response, according
668      * to the format specified by the <code>pattern</code> property.
669      *
670      * @param request Request being processed
671      * @param response Response being processed
672      * @param context The valve context used to invoke the next valve
673      * in the current processing pipeline
674      *
675      * @exception IOException if an input/output error has occurred
676      * @exception ServletException if a servlet error has occurred
677      */

678     public int invoke(Request JavaDoc request, Response JavaDoc response)
679             throws IOException JavaDoc, ServletException JavaDoc {
680         return INVOKE_NEXT;
681     }
682
683    
684     public synchronized void postInvoke(Request JavaDoc request, Response JavaDoc response){
685  
686         if (condition!=null &&
687                 null!=request.getRequest().getAttribute(condition)) {
688              return;
689
690         }
691
692         int lastPosition = charBuffer.position();
693
694         // Reset properly the buffer in case of an unexpected
695
// exception.
696
if (charBuffer.position() == charBuffer.limit()){
697             charBuffer.limit(charBuffer.capacity());
698         }
699
700         boolean flushBuffer = !charBuffer.hasRemaining();
701
702         // Try to predict if the bufer has enough place.
703
if ( (chunkSize + charBuffer.position()) > charBuffer.limit()){
704             flushBuffer = true;
705         }
706
707         if ( !flushBuffer ){
708
709             try{
710                 createLogEntry(request, response, patternComponents);
711                 // Last chunck size used to predict if the buffer
712
// will soon be full.
713
chunkSize = charBuffer.position() - lastPosition;
714             } catch (BufferOverflowException JavaDoc ex ){
715                 // Will happen if the current chunk is larger than
716
// the previous one, or if the buffer size is too small to
717
// hold a single access log entry
718
flushBuffer = true;
719                 charBuffer.position(lastPosition);
720             }
721         }
722
723         /*
724          * Flush the buffer if it is full or we're logging in real time.
725          * We also flush if we had to expand the buffer (see below), and
726          * the buffer content is larger than the buffer size that was
727          * originally configured, so in essence we pretend that the original
728          * buffer size is still effective.
729          */

730         if ( flushBuffer || flushRealTime
731                 || (charBuffer.position() - lastPosition > bufferSize)) {
732             log();
733         }
734
735         if (flushBuffer) {
736             // We've flushed our buffer to make room for the current request.
737
// Now process the current request
738
boolean expand = true;
739             while (expand) {
740                 try {
741                     createLogEntry(request, response, patternComponents);
742                     expand = false;
743                     log();
744                  } catch (BufferOverflowException JavaDoc ex) {
745                     // Even after flushing, the current buffer size is too
746
// small to handle the single log entry corresponding to
747
// the current request. Double the buffer size
748
if (!flushRealTime) {
749                         _logger.log(
750                             Level.WARNING,
751                             "peaccesslogvalve.bufferSizeTooSmall",
752                             new Object JavaDoc[] {
753                                 Integer.toString(expandedBufferSize) });
754                     }
755                     expandedBufferSize *= 2;
756                     charBuffer = CharBuffer.allocate(expandedBufferSize);
757                 }
758             }
759         }
760
761     }
762
763
764     // -------------------------------------------------------- Private Methods
765

766
767     private void createLogEntry(Request JavaDoc request,
768                                 Response JavaDoc response,
769                                 LinkedList JavaDoc<String JavaDoc> patternComponents) {
770
771         ServletRequest JavaDoc req = request.getRequest();
772         HttpServletRequest JavaDoc hreq = (HttpServletRequest JavaDoc) req;
773
774         for (int i=0; i<patternComponents.size(); i++) {
775             String JavaDoc pc = patternComponents.get(i);
776             if (AUTH_USER_NAME.equals(pc)) {
777                 appendAuthUserName(charBuffer, hreq);
778             } else if (CLIENT_DNS.equals(pc)) {
779                 appendClientDNS(charBuffer, req);
780             } else if (CLIENT_NAME.equals(pc)) {
781                 appendClientName(charBuffer, req);
782             } else if (COOKIE_VALUE.equals(pc)) {
783                 appendCookieValue(charBuffer, hreq);
784             } else if (DATE_TIME.equals(pc)) {
785                 appendCurrentDate(charBuffer);
786             } else if (HEADER_ACCEPT.equals(pc)) {
787                 appendHeaderAccept(charBuffer, hreq);
788             } else if (HEADER_AUTH.equals(pc)) {
789                 appendHeaderAuth(charBuffer, hreq);
790             } else if (HEADER_DATE.equals(pc)) {
791                 appendHeaderDate(charBuffer, hreq);
792             } else if (HEADER_IF_MOD_SINCE.equals(pc)) {
793                 appendHeaderIfModSince(charBuffer, hreq);
794             } else if (HEADER_USER_AGENT.equals(pc)) {
795                 appendUserAgent(charBuffer, hreq);
796             } else if (HEADER_REFERER.equals(pc)) {
797                 appendReferer(charBuffer, hreq);
798             } else if (HTTP_METHOD.equals(pc)) {
799                 appendHTTPMethod(charBuffer, hreq);
800             } else if (HTTP_URI.equals(pc)) {
801                 appendHTTPUri(charBuffer, hreq);
802             } else if (HTTP_VERSION.equals(pc)) {
803                 appendHTTPVersion(charBuffer, hreq);
804             } else if (QUERY_STR.equals(pc)) {
805                 appendQueryString(charBuffer, hreq);
806             } else if (REFERER.equals(pc)) {
807                 appendReferer(charBuffer, hreq);
808             } else if (REQUEST.equals(pc)) {
809                 appendRequestInfo(charBuffer, hreq);
810             } else if (RESPONSE_LENGTH.equals(pc)) {
811                 appendResponseLength(charBuffer, response);
812             } else if (STATUS.equals(pc)) {
813                 appendResponseStatus(charBuffer, response);
814             } else if (USER_AGENT.equals(pc)) {
815                 appendUserAgent(charBuffer, hreq);
816             } else if (VS_ID.equals(pc)) {
817                 appendVirtualServerId(charBuffer);
818             } else if (pc.startsWith(HEADER_ANY)) {
819                 appendHeaderAny(charBuffer, pc.substring(HEADER_ANY_LEN),
820                                 hreq);
821             }
822
823             charBuffer.put(space);
824         }
825
826         charBuffer.put("\n");
827     }
828
829
830     /*
831      * Parses the access log pattern (that was specified via setPattern) into
832      * its individual components, and returns them as a list.
833      *
834      * @return List containing the access log pattern components
835      */

836     private LinkedList JavaDoc<String JavaDoc> parsePattern() {
837
838         LinkedList JavaDoc list = new LinkedList JavaDoc();
839
840         int from = 0;
841         int end = -1;
842         int index = -1;
843         while ((index = pattern.indexOf('%', from)) >= 0) {
844             end = pattern.indexOf('%', index+1);
845             if (end < 0) {
846                 _logger.log(
847                     Level.SEVERE,
848                     "peaccesslogvalve.missingAccessLogPatternEndDelimiter",
849                     pattern);
850             }
851             String JavaDoc component = pattern.substring(index+1, end);
852
853             if (!AUTH_USER_NAME.equals(component)
854                     && !CLIENT_DNS.equals(component)
855                     && !CLIENT_NAME.equals(component)
856                     && !COOKIE_VALUE.equals(component)
857                     && !DATE_TIME.equals(component)
858                     && !HEADER_ACCEPT.equals(component)
859                     && !HEADER_AUTH.equals(component)
860                     && !HEADER_DATE.equals(component)
861                     && !HEADER_IF_MOD_SINCE.equals(component)
862                     && !HEADER_USER_AGENT.equals(component)
863                     && !HEADER_REFERER.equals(component)
864                     && !HTTP_METHOD.equals(component)
865                     && !HTTP_URI.equals(component)
866                     && !HTTP_VERSION.equals(component)
867                     && !QUERY_STR.equals(component)
868                     && !REFERER.equals(component)
869                     && !REQUEST.equals(component)
870                     && !RESPONSE_LENGTH.equals(component)
871                     && !STATUS.equals(component)
872                     && !USER_AGENT.equals(component)
873                     && !VS_ID.equals(component)
874                     && !component.startsWith(HEADER_ANY)) {
875                 _logger.log(
876                     Level.SEVERE,
877                     "peaccesslogvalve.invalidAccessLogPatternComponent",
878                     new Object JavaDoc[] { component, pattern });
879             }
880
881             list.add(component);
882             from = end + 1;
883         }
884
885         return list;
886     }
887
888
889     /*
890      * Appends the client host name of the given request to the given char
891      * buffer.
892      */

893     private void appendClientName(CharBuffer JavaDoc cb, ServletRequest JavaDoc req) {
894         cb.put("\"");
895         String JavaDoc value = req.getRemoteHost();
896         if (value == null) {
897             value = "NULL-CLIENT-NAME";
898         }
899         cb.put(value);
900         cb.put("\"");
901     }
902
903
904     /*
905      * Appends the client DNS of the given request to the given char
906      * buffer.
907      */

908     private void appendClientDNS(CharBuffer JavaDoc cb, ServletRequest JavaDoc req) {
909         cb.put("\"");
910         String JavaDoc value = req.getRemoteAddr();
911         if (value == null) {
912             value = "NULL-CLIENT-DNS";
913         }
914         cb.put(value);
915         cb.put("\"");
916     }
917
918
919     /*
920      * Appends the authenticated user (if any) to the given char buffer.
921      */

922     private void appendAuthUserName(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
923         cb.put("\"");
924         String JavaDoc user = hreq.getRemoteUser();
925         if (user == null) {
926             user = "NULL-AUTH-USER";
927         }
928         cb.put(user);
929         cb.put("\"");
930     }
931
932
933     /*
934      * Appends the current date to the given char buffer.
935      */

936     private void appendCurrentDate(CharBuffer JavaDoc cb) {
937         cb.put("\"");
938         Date JavaDoc date = getDate();
939         cb.put(dayFormatter.format(date)); // Day
940
cb.put('/');
941         cb.put(lookup(monthFormatter.format(date))); // Month
942
cb.put('/');
943         cb.put(yearFormatter.format(date)); // Year
944
cb.put(':');
945         cb.put(timeFormatter.format(date)); // Time
946
cb.put(space);
947         cb.put(timeZone); // Time Zone
948
cb.put("\"");
949     }
950
951
952     /*
953      * Appends info about the given request to the given char buffer.
954      */

955     private void appendRequestInfo(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
956         cb.put("\"");
957         cb.put(hreq.getMethod());
958         cb.put(space);
959         cb.put(hreq.getRequestURI());
960         if (hreq.getQueryString() != null) {
961             cb.put('?');
962             cb.put(hreq.getQueryString());
963         }
964         cb.put(space);
965         cb.put(hreq.getProtocol());
966         cb.put("\"");
967     }
968
969
970     /*
971      * Appends the response status to the given char buffer.
972      */

973     private void appendResponseStatus(CharBuffer JavaDoc cb, Response JavaDoc response) {
974         cb.put(String.valueOf(((HttpResponse) response).getStatus()));
975     }
976
977
978     /*
979      * Appends the content length of the given response to the given char
980      * buffer.
981      */

982     private void appendResponseLength(CharBuffer JavaDoc cb, Response JavaDoc response) {
983         cb.put("" + response.getContentCount());
984     }
985
986
987     /*
988      * Appends the value of the 'user-agent' header of the given request to
989      * the given char buffer.
990      */

991     private void appendUserAgent(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
992         cb.put("\"");
993         String JavaDoc ua = hreq.getHeader("user-agent");
994         if (ua == null) {
995             ua = "NULL-USER-AGENT";
996         }
997         cb.put(ua);
998         cb.put("\"");
999     }
1000
1001
1002    /*
1003     * Appends the value of the 'referer' header of the given request to
1004     * the given char buffer.
1005     */

1006    private void appendReferer(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
1007        cb.put("\"");
1008        String JavaDoc referer = hreq.getHeader("referer");
1009        if (referer == null) {
1010            referer = "NULL-REFERER";
1011        }
1012        cb.put(referer);
1013        cb.put("\"");
1014    }
1015
1016
1017    /*
1018     * Appends the Accept header of the given request to the given char
1019     * buffer.
1020     */

1021    private void appendHeaderAccept(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
1022        cb.put("\"");
1023        String JavaDoc accept = hreq.getHeader(HTTP_HEADER_ACCEPT);
1024        if (accept == null) {
1025            accept = "NULL-HEADER-ACCEPT";
1026        }
1027        cb.put(accept);
1028        cb.put("\"");
1029    }
1030
1031
1032    /*
1033     * Appends the Authorization header of the given request to the given char
1034     * buffer.
1035     */

1036    private void appendHeaderAuth(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
1037        cb.put("\"");
1038        String JavaDoc auth = hreq.getHeader(HTTP_HEADER_AUTHORIZATION);
1039        if (auth == null) {
1040            auth = "NULL-HEADER-AUTHORIZATION";
1041        }
1042        cb.put(auth);
1043        cb.put("\"");
1044    }
1045
1046
1047    /*
1048     * Appends the Date header of the given request to the given char buffer.
1049     */

1050    private void appendHeaderDate(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
1051        cb.put("\"");
1052        String JavaDoc date = hreq.getHeader(HTTP_HEADER_DATE);
1053        if (date == null) {
1054            date = "NULL-HEADER-DATE";
1055        }
1056        cb.put(date);
1057        cb.put("\"");
1058    }
1059
1060
1061    /*
1062     * Appends the If-Modified-Since header of the given request to the
1063     * given char buffer.
1064     */

1065    private void appendHeaderIfModSince(CharBuffer JavaDoc cb,
1066                                        HttpServletRequest JavaDoc hreq) {
1067        cb.put("\"");
1068        String JavaDoc ifModSince = hreq.getHeader(HTTP_HEADER_IF_MODIFIED_SINCE);
1069        if (ifModSince == null) {
1070            ifModSince = "NULL-HEADER-IF-MODIFIED-SINCE";
1071        }
1072        cb.put(ifModSince);
1073        cb.put("\"");
1074    }
1075
1076
1077    /*
1078     * Appends the value of the specified header name of the given request
1079     * to the given char buffer.
1080     */

1081    private void appendHeaderAny(CharBuffer JavaDoc cb,
1082                                 String JavaDoc headerName,
1083                                 HttpServletRequest JavaDoc hreq) {
1084        cb.put("\"");
1085        String JavaDoc value = hreq.getHeader(headerName);
1086        if (value == null) {
1087            value = "NULL-HEADER-" + headerName.toUpperCase();
1088        }
1089        cb.put(value);
1090        cb.put("\"");
1091    }
1092
1093
1094    /*
1095     * Appends the value of the first cookie of the given request to the
1096     * given char buffer.
1097     */

1098    private void appendCookieValue(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
1099        cb.put("\"");
1100        String JavaDoc cookieValue = "NULL-COOKIE-VALUE";
1101        Cookie JavaDoc[] cookies = hreq.getCookies();
1102        if (cookies != null && cookies.length > 0) {
1103            cookieValue = cookies[0].getValue();
1104        }
1105        cb.put(cookieValue);
1106        cb.put("\"");
1107    }
1108
1109
1110    /*
1111     * Appends the HTTP method of the given request to the given char buffer.
1112     */

1113    private void appendHTTPMethod(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
1114        cb.put("\"");
1115        String JavaDoc method = hreq.getMethod();
1116        if (method == null) {
1117            method = "NULL-HTTP-METHOD";
1118        }
1119        cb.put(method);
1120        cb.put("\"");
1121    }
1122
1123
1124    /*
1125     * Appends the URI of the given request to the given char buffer.
1126     */

1127    private void appendHTTPUri(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
1128        cb.put("\"");
1129        String JavaDoc uri = hreq.getRequestURI();
1130        if (uri == null) {
1131            uri = "NULL-HTTP-URI";
1132        }
1133        cb.put(uri);
1134        cb.put("\"");
1135    }
1136
1137
1138    /*
1139     * Appends the HTTP protocol version of the given request to the given
1140     * char buffer.
1141     */

1142    private void appendHTTPVersion(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
1143        cb.put("\"");
1144        String JavaDoc protocol = hreq.getProtocol();
1145        if (protocol == null) {
1146            protocol = "NULL-HTTP-PROTOCOL";
1147        }
1148        cb.put(protocol);
1149        cb.put("\"");
1150    }
1151
1152
1153    /*
1154     * Appends the query string of the given request to the given char buffer.
1155     */

1156    private void appendQueryString(CharBuffer JavaDoc cb, HttpServletRequest JavaDoc hreq) {
1157        cb.put("\"");
1158        String JavaDoc query = hreq.getQueryString();
1159        if (query == null) {
1160            query = "NULL-QUERY";
1161        }
1162        cb.put(query);
1163        cb.put("\"");
1164    }
1165
1166
1167    /*
1168     * Appends the id of the virtual server with which this access log valve
1169     * has been associated to the given char buffer.
1170     */

1171    private void appendVirtualServerId(CharBuffer JavaDoc cb) {
1172        String JavaDoc vsId = "NULL-VIRTUAL-SERVER";
1173        Container cont = getContainer();
1174        if (cont != null) {
1175            vsId = cont.getName();
1176        }
1177        cb.put(vsId);
1178    }
1179
1180
1181    /**
1182     * Close the currently open log file (if any)
1183     */

1184    private synchronized void close() {
1185
1186        try{
1187            // Make sure the byteBuffer is clean
1188
log();
1189            fileChannel.close();
1190            fos.close();
1191        } catch (IOException JavaDoc ex){
1192            ;
1193        }
1194    }
1195
1196
1197    /**
1198     * Log the specified message to the log file, switching files if the date
1199     * has changed since the previous log call.
1200     *
1201     * @param message Message to be logged
1202     * @param date the current Date object (so this method doesn't need to
1203     * create a new one)
1204     */

1205    public void log() {
1206        
1207        if (rotatable){
1208
1209            long systime = System.currentTimeMillis();
1210            if ((systime-lastAccessLogCreationTime) > (rotationInterval*1000)) {
1211                synchronized (this) {
1212                    systime = System.currentTimeMillis();
1213                    if ((systime-lastAccessLogCreationTime) >
1214                                                    (rotationInterval*1000)) {
1215
1216                        // Rotate only if the formatted datestamps are
1217
// different
1218
String JavaDoc lastDateStamp = dateFormatter.format(
1219                            new Date JavaDoc(lastAccessLogCreationTime));
1220                        String JavaDoc newDateStamp = dateFormatter.format(
1221                            new Date JavaDoc(systime));
1222
1223                        lastAccessLogCreationTime = systime;
1224
1225                        if (!lastDateStamp.equals(newDateStamp)) {
1226                            close();
1227                            open(newDateStamp, false);
1228                        }
1229                    }
1230                }
1231            }
1232        }
1233        
1234        try{
1235            charBuffer.flip();
1236            ByteBuffer JavaDoc byteBuffer =
1237                ByteBuffer.wrap(charBuffer.toString().getBytes());
1238            while (byteBuffer.hasRemaining()){
1239                fileChannel.write(byteBuffer);
1240            }
1241            charBuffer.clear();
1242        } catch (IOException JavaDoc ex){
1243            ;
1244        }
1245
1246    }
1247
1248
1249    /**
1250     * Return the month abbreviation for the specified month, which must
1251     * be a two-digit String.
1252     *
1253     * @param month Month number ("01" .. "12").
1254     */

1255    private String JavaDoc lookup(String JavaDoc month) {
1256
1257        int index;
1258        try {
1259            index = Integer.parseInt(month) - 1;
1260        } catch (Throwable JavaDoc t) {
1261            index = 0; // Can not happen, in theory
1262
}
1263        return (months[index]);
1264
1265    }
1266
1267    
1268    /**
1269     * Open new access log file.
1270     *
1271     * @param dateStamp The date stamp of the new access log file (if log
1272     * rotation has been enabled)
1273     * @param firstAccessLogFile true if we are creating our first access log
1274     * file, and false if we have rotated
1275     */

1276    private synchronized void open(String JavaDoc dateStamp,
1277                                   boolean firstAccessLogFile) {
1278        
1279        // Create the directory if necessary
1280
File JavaDoc dir = new File JavaDoc(directory);
1281        if (!dir.isAbsolute())
1282            dir = new File JavaDoc(System.getProperty("catalina.base"), directory);
1283        dir.mkdirs();
1284
1285        // Open the current log file
1286
try {
1287            String JavaDoc pathname;
1288            // If no rotate - no need for dateStamp in fileName
1289
if (rotatable && addDateStampToFirstAccessLogFile) {
1290                pathname = dir.getAbsolutePath() + File.separator +
1291                            prefix + dateStamp + suffix;
1292            } else {
1293                if (removeLeadingDotFromSuffix) {
1294                    pathname = dir.getAbsolutePath() + File.separator +
1295                               prefix + dotLessSuffix;
1296                } else {
1297                    pathname = dir.getAbsolutePath() + File.separator +
1298                               prefix + suffix;
1299                }
1300            }
1301            
1302            if (rotatable
1303                    && !addDateStampToFirstAccessLogFile
1304                    && !firstAccessLogFile) {
1305                // Move current access log file, which has no date stamp,
1306
// to date-stamped file
1307
String JavaDoc dateStampedPathname = dir.getAbsolutePath()
1308                                        + File.separator
1309                                        + prefix + dateStamp + suffix;
1310                File JavaDoc renameToFile = new File JavaDoc(dateStampedPathname);
1311                if (!logFile.renameTo(renameToFile)) {
1312                    _logger.log(
1313                        Level.WARNING,
1314                        "peaccesslogvalve.unableToRenameLogFile",
1315                        new Object JavaDoc[] {
1316                            logFile.toString(), dateStampedPathname });
1317                }
1318                File JavaDoc removeFile = null;
1319                if (deleteAllHistoryFiles) {
1320                    removeFile = renameToFile;
1321                } else {
1322                    if (historyFiles != null) {
1323                        historyFiles.addLast(renameToFile);
1324                        if (historyFiles.size() > maxHistoryFiles) {
1325                            removeFile = historyFiles.removeFirst();
1326                        }
1327                    }
1328                }
1329                if (removeFile != null && !removeFile.delete()) {
1330                    _logger.log(Level.WARNING,
1331                                "peaccesslogvalve.unableToRemoveLogFile",
1332                                removeFile.toString());
1333                }
1334            }
1335
1336            // Open the file and then get a channel from the stream
1337
logFile = new File JavaDoc(pathname);
1338            fos = new FileOutputStream JavaDoc(logFile, true);
1339            fileChannel = fos.getChannel();
1340
1341        } catch (IOException JavaDoc e) {
1342            try{
1343                if ( fileChannel != null )
1344                    fileChannel.close();
1345            } catch (IOException JavaDoc ex){
1346                ;
1347            }
1348        }
1349
1350    }
1351
1352    
1353    /**
1354     * This method returns a Date object that is accurate to within one
1355     * second. If a writerThread calls this method to get a Date and it's been
1356     * less than 5 second since a new Date was created, this method
1357     * simply gives out the same Date again so that the system doesn't
1358     * spend time creating Date objects unnecessarily.
1359     */

1360    private synchronized Date JavaDoc getDate() {
1361
1362        // Only create a new Date once per second, max.
1363
long systime = System.currentTimeMillis();
1364        if ((systime - currentDate.getTime()) > 5000) {
1365            currentDate = new Date JavaDoc(systime);
1366        }
1367
1368        return currentDate;
1369
1370    }
1371
1372
1373    private String JavaDoc calculateTimeZoneOffset(long offset) {
1374        StringBuffer JavaDoc tz = new StringBuffer JavaDoc();
1375        if ((offset<0)) {
1376            tz.append("-");
1377            offset = -offset;
1378        } else {
1379            tz.append("+");
1380        }
1381
1382        long hourOffset = offset/(1000*60*60);
1383        long minuteOffset = (offset/(1000*60)) % 60;
1384
1385        if (hourOffset<10)
1386            tz.append("0");
1387        tz.append(hourOffset);
1388
1389        if (minuteOffset<10)
1390            tz.append("0");
1391        tz.append(minuteOffset);
1392
1393        return tz.toString();
1394    }
1395
1396
1397    // ------------------------------------------------------ Lifecycle Methods
1398

1399
1400    /**
1401     * Add a lifecycle event listener to this component.
1402     *
1403     * @param listener The listener to add
1404     */

1405    public void addLifecycleListener(LifecycleListener listener) {
1406
1407        lifecycle.addLifecycleListener(listener);
1408
1409    }
1410
1411
1412    /**
1413     * Get the lifecycle listeners associated with this lifecycle. If this
1414     * Lifecycle has no listeners registered, a zero-length array is returned.
1415     */

1416    public LifecycleListener[] findLifecycleListeners() {
1417
1418        return lifecycle.findLifecycleListeners();
1419
1420    }
1421
1422
1423    /**
1424     * Remove a lifecycle event listener from this component.
1425     *
1426     * @param listener The listener to add
1427     */

1428    public void removeLifecycleListener(LifecycleListener listener) {
1429
1430        lifecycle.removeLifecycleListener(listener);
1431
1432    }
1433
1434
1435    /**
1436     * Prepare for the beginning of active use of the public methods of this
1437     * component. This method should be called after <code>configure()</code>,
1438     * and before any of the public methods of the component are utilized.
1439     *
1440     * @exception LifecycleException if this component detects a fatal error
1441     * that prevents this component from being used
1442     */

1443    public void start() throws LifecycleException {
1444
1445        // Validate and update our current component state
1446
if (started) {
1447            throw new LifecycleException
1448                (sm.getString("accessLogValve.alreadyStarted"));
1449        }
1450
1451        lifecycle.fireLifecycleEvent(START_EVENT, null);
1452        started = true;
1453
1454        deleteAllHistoryFiles = false;
1455        historyFiles = null;
1456        String JavaDoc prop = System.getProperty(LOGGING_MAX_HISTORY_FILES);
1457        if (prop != null) {
1458            maxHistoryFiles = 10;
1459            if (!"".equals(prop)) {
1460                try {
1461                    maxHistoryFiles = Integer.parseInt(prop);
1462                } catch (NumberFormatException JavaDoc e) {};
1463            }
1464        if (maxHistoryFiles == 0) {
1465                deleteAllHistoryFiles = true;
1466            } else if (maxHistoryFiles > 0) {
1467                historyFiles = new LinkedList JavaDoc<File JavaDoc>();
1468            }
1469        }
1470
1471        expandedBufferSize = bufferSize;
1472        if (bufferSize <= 0) {
1473            // We need to expand (double) the buffer size as needed.
1474
// However, null times two is still null.
1475
expandedBufferSize = 1;
1476        }
1477
1478        charBuffer = CharBuffer.allocate(expandedBufferSize);
1479
1480        // Initialize the timeZone, Date formatters, and currentDate
1481
TimeZone JavaDoc tz = TimeZone.getDefault();
1482        timeZone = calculateTimeZoneOffset(tz.getRawOffset());
1483
1484        if (fileDateFormat==null || fileDateFormat.length()==0)
1485            fileDateFormat = "yyyy-MM-dd";
1486        dateFormatter = new SimpleDateFormat JavaDoc(fileDateFormat);
1487        dateFormatter.setTimeZone(tz);
1488        dayFormatter = new SimpleDateFormat JavaDoc("dd");
1489        dayFormatter.setTimeZone(tz);
1490        monthFormatter = new SimpleDateFormat JavaDoc("MM");
1491        monthFormatter.setTimeZone(tz);
1492        yearFormatter = new SimpleDateFormat JavaDoc("yyyy");
1493        yearFormatter.setTimeZone(tz);
1494        timeFormatter = new SimpleDateFormat JavaDoc("HH:mm:ss");
1495        timeFormatter.setTimeZone(tz);
1496
1497        long systime = System.currentTimeMillis();
1498        currentDate = new Date JavaDoc(systime);
1499        open(dateFormatter.format(currentDate), true);
1500        lastAccessLogCreationTime = systime;
1501
1502        if (!flushRealTime){
1503            // Start the background writer writerThread
1504
threadStart();
1505        }
1506   }
1507
1508
1509    /**
1510     * Gracefully terminate the active use of the public methods of this
1511     * component. This method should be the last one called on a given
1512     * instance of this component.
1513     *
1514     * @exception LifecycleException if this component detects a fatal error
1515     * that needs to be reported
1516     */

1517    public void stop() throws LifecycleException {
1518
1519        // Validate and update our current component state
1520
if (!started)
1521            throw new LifecycleException
1522                (sm.getString("accessLogValve.notStarted"));
1523        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
1524        started = false;
1525        
1526        if (!flushRealTime){
1527            // Stop the background writer thread
1528
threadStop();
1529        }
1530        
1531        close();
1532
1533    }
1534
1535    
1536   /**
1537     * The background writerThread that checks for write the log.
1538     */

1539    public void run() {
1540
1541        // Loop until the termination semaphore is set
1542
while (!threadDone) {
1543            threadSleep();
1544            log();
1545        }
1546
1547    }
1548    
1549    
1550    /**
1551     * Sleep for the duration specified by the <code>writeInterval</code>
1552     * property.
1553     */

1554    private void threadSleep() {
1555
1556        try {
1557            writerThread.sleep(writeInterval * 1000L);
1558        } catch (InterruptedException JavaDoc e) {
1559            ;
1560        }
1561
1562    }
1563
1564        
1565   /**
1566     * Start the background writerThread that will periodically write access log
1567     */

1568    private void threadStart() {
1569
1570        if (writerThread != null)
1571            return;
1572
1573        threadDone = false;
1574        String JavaDoc threadName = "AccessLogWriter";
1575        writerThread = new Thread JavaDoc(this, threadName);
1576        writerThread.setDaemon(true);
1577        writerThread.start();
1578
1579    }
1580
1581
1582    /**
1583     * Stop the background writerThread that is periodically write logs
1584     */

1585    private void threadStop() {
1586
1587        if (writerThread == null)
1588            return;
1589
1590        threadDone = true;
1591        writerThread.interrupt();
1592        try {
1593            writerThread.join();
1594        } catch (InterruptedException JavaDoc e) {
1595            ;
1596        }
1597
1598        writerThread = null;
1599
1600    }
1601}
1602
Popular Tags