KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > tomcat5 > util > LogViewer


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.tomcat5.util;
21
22 import java.io.BufferedReader JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileReader JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.text.DateFormat JavaDoc;
27 import java.text.SimpleDateFormat JavaDoc;
28 import java.util.Collections JavaDoc;
29 import java.util.Date JavaDoc;
30 import java.util.EventListener JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.LinkedList JavaDoc;
33 import java.util.List JavaDoc;
34 import org.netbeans.api.java.classpath.GlobalPathRegistry;
35 import org.netbeans.modules.tomcat5.TomcatManager;
36 import org.netbeans.modules.tomcat5.util.LogSupport.LineInfo;
37 import org.openide.ErrorManager;
38 import org.openide.windows.IOProvider;
39 import org.openide.windows.InputOutput;
40 import org.openide.windows.OutputWriter;
41
42 /**
43  * Thread which displays Tomcat log files in the output window. The output
44  * window name equals prefix minus trailing dot, if present.
45  *
46  * Currently only <code>org.apache.catalina.logger.FileLogger</code> logger
47  * is supported.
48  *
49  * @author Stepan Herold
50  */

51 public class LogViewer extends Thread JavaDoc {
52     private volatile boolean stop = false;
53     private InputOutput inOut;
54     private OutputWriter writer;
55     private OutputWriter errorWriter;
56     private File JavaDoc directory;
57     private String JavaDoc prefix;
58     private String JavaDoc suffix;
59     private boolean isTimestamped;
60     private boolean takeFocus;
61     
62     private ContextLogSupport logSupport;
63     private String JavaDoc catalinaWorkDir;
64     private String JavaDoc webAppContext;
65     private boolean isStarted;
66     
67     /**
68      * List of listeners which are notified when the log viewer is stoped.
69      */

70     private List JavaDoc/*<LogViewerStopListener>*/ stopListeners = Collections.synchronizedList(new LinkedList JavaDoc());
71
72     private String JavaDoc displayName;
73     
74     /**
75      * Create a new LogViewer thread.
76      *
77      * @param catalinaDir catalina directory (CATALINA_BASE or CATALINA_HOME).
78      * @param catalinaWorkDir work directory where Tomcat stores generated classes
79      * and sources from JSPs (e.g. $CATALINA_BASE/work/Catalina/localhost).
80      * @param webAppContext web application's context this logger is declared for,
81      * may be <code>null</code> for shared context log. It is used to look
82      * up sources of servlets generated from JSPs.
83      * @param className class name of logger implementation
84      * @param directory absolute or relative pathname of a directory in which log
85      * files reside, if null catalina default is used.
86      * @param prefix log file prefix, if null catalina default is used.
87      * @param suffix log file suffix, if null catalina default is used.
88      * @param isTimestamped whether logged messages are timestamped.
89      * @param takeFocus whether output window should get focus after each change.
90      *
91      * @throws NullPointerException if catalinaDir parameter is <code>null</code>.
92      * @throws UnsupportedLoggerException logger specified by the className parameter
93      * is not supported.
94      */

95     public LogViewer(File JavaDoc catalinaDir, String JavaDoc catalinaWorkDir, String JavaDoc webAppContext,
96             String JavaDoc className, String JavaDoc directory, String JavaDoc prefix, String JavaDoc suffix,
97             boolean isTimestamped, boolean takeFocus) throws UnsupportedLoggerException {
98         super("LogViewer - Thread"); // NOI18N
99
if (catalinaDir == null) {
100             throw new NullPointerException JavaDoc();
101         }
102         if (catalinaWorkDir == null) {
103             throw new NullPointerException JavaDoc();
104         }
105         if (className != null && !"org.apache.catalina.logger.FileLogger".equals(className)) { // NOI18N
106
throw new UnsupportedLoggerException(className);
107         }
108         if (directory != null) {
109             this.directory = new File JavaDoc(directory);
110             if (!this.directory.isAbsolute()) {
111                 this.directory = new File JavaDoc(catalinaDir, directory);
112             }
113         } else {
114             this.directory = new File JavaDoc(catalinaDir, "logs"); // NOI18N
115
}
116         if (prefix != null) {
117             this.prefix = prefix;
118         } else {
119             this.prefix = "catalina."; // NOI18N
120
}
121         if (suffix != null) {
122             this.suffix = suffix;
123         } else {
124             this.suffix = ".log"; // NOI18N
125
}
126         this.isTimestamped = isTimestamped;
127         this.takeFocus = takeFocus;
128         this.catalinaWorkDir = catalinaWorkDir;
129         this.webAppContext = webAppContext;
130         logSupport = new ContextLogSupport(catalinaWorkDir, webAppContext);
131         setDaemon(true);
132     }
133     
134     public void setDisplayName(String JavaDoc displayName) {
135         this.displayName = displayName;
136     }
137     
138     /**
139      * Stop the LogViewer thread.
140      */

141     public void close() {
142         synchronized(this) {
143             stop = true;
144             notify();
145         }
146     }
147     
148     public boolean equals(Object JavaDoc obj) {
149         if (this == obj) {
150             return true;
151         }
152         if (obj instanceof LogViewer) {
153             LogViewer anotherLogViewer = (LogViewer)obj;
154             if (catalinaWorkDir.equals(anotherLogViewer.catalinaWorkDir)
155                 && (((webAppContext != null) && webAppContext.equals(anotherLogViewer.webAppContext))
156                     || (webAppContext == anotherLogViewer.webAppContext))
157                 && directory.equals(anotherLogViewer.directory)
158                 && prefix.equals(anotherLogViewer.prefix)
159                 && suffix.equals(anotherLogViewer.suffix)
160                 && isTimestamped) {
161                     return true;
162             }
163         }
164         return false;
165     }
166     
167     /**
168      * Tests whether LogViewer thread is still running.
169      * @return <code>false</code> if thread was stopped or its output window
170      * was closed, <code>true</code> otherwise.
171      */

172     public boolean isOpen() {
173         InputOutput io = inOut;
174         return !(io == null || stop || (isStarted && io.isClosed()));
175     }
176     
177     /**
178      * Make the log tab visible
179      */

180     public void takeFocus() {
181         InputOutput io = inOut;
182         if (io != null) {
183             io.select();
184         }
185     }
186     
187     private File JavaDoc getLogFile(String JavaDoc timestamp) throws IOException JavaDoc {
188         File JavaDoc f = new File JavaDoc(directory, prefix + timestamp + suffix);
189         f.createNewFile(); // create, if does not exist
190
return f;
191     }
192     
193     private String JavaDoc getTimestamp() {
194         DateFormat JavaDoc df = new SimpleDateFormat JavaDoc("yyyy-MM-dd"); //NOI18N
195
return df.format(new Date JavaDoc());
196     }
197     
198     private void processLine(String JavaDoc line) {
199         ContextLogSupport.LineInfo lineInfo = logSupport.analyzeLine(line);
200         if (lineInfo.isError()) {
201             if (lineInfo.isAccessible()) {
202                 try {
203                     errorWriter.println(line, logSupport.getLink(lineInfo.message(), lineInfo.path(), lineInfo.line()));
204                 } catch (IOException JavaDoc ex) {
205                     ErrorManager.getDefault().notify(ex);
206                 }
207             } else {
208                 errorWriter.println(line);
209             }
210         } else {
211             writer.println(line);
212         }
213     }
214     
215     public void run() {
216         if (displayName == null) {
217             // cut off trailing dot
218
displayName = this.prefix;
219             int trailingDot = displayName.lastIndexOf('.');
220             if (trailingDot > -1) {
221                 displayName = displayName.substring(0, trailingDot);
222             }
223         }
224         inOut = IOProvider.getDefault().getIO(displayName, false);
225         try {
226             inOut.getOut().reset();
227         }
228         catch (IOException JavaDoc e) {
229             // not a critical error, continue
230
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
231         }
232         inOut.select();
233         writer = inOut.getOut();
234         errorWriter = inOut.getErr();
235         isStarted = true;
236         
237         String JavaDoc timestamp = getTimestamp();
238         String JavaDoc oldTimestamp = timestamp;
239         try {
240             File JavaDoc logFile = getLogFile(timestamp);
241             BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new FileReader JavaDoc(logFile));
242             try {
243                 while (!stop && !inOut.isClosed()) {
244                     // check whether a log file has rotated
245
timestamp = getTimestamp();
246                     if (!timestamp.equals(oldTimestamp)) {
247                         oldTimestamp = timestamp;
248                         reader.close();
249                         logFile = getLogFile(timestamp);
250                         reader = new BufferedReader JavaDoc(new FileReader JavaDoc(logFile));
251                     }
252                     int count = 0;
253                     // take a nap after 1024 read cycles, this should ensure responsiveness
254
// even if log file is growing fast
255
boolean updated = false;
256                     while (reader.ready() && count++ < 1024) {
257                         processLine(reader.readLine());
258                         updated = true;
259                     }
260                     if (updated) {
261                         writer.flush();
262                         errorWriter.flush();
263                         if (takeFocus) {
264                             inOut.select();
265                         }
266                     }
267                     // wait for the next attempt
268
try {
269                         synchronized(this) {
270                             if (!stop && !inOut.isClosed()) {
271                                 wait(100);
272                             }
273                         }
274                     } catch(InterruptedException JavaDoc ex) {
275                         // no op - the thread was interrupted
276
}
277                 }
278             } finally {
279                 reader.close();
280             }
281         } catch (IOException JavaDoc ex) {
282             TomcatManager.ERR.notify(ex);
283         } finally {
284             writer.close();
285         }
286         fireLogViewerStopListener();
287         logSupport.detachAnnotation();
288     }
289     
290     /**
291      * Add a <code>LogViewerStopListener</code>.
292      *
293      * @param listener <code>LogViewerStopListener</code> which will be notified
294      * when the <code>LogViewer</code> stops running.
295      */

296     public void addLogViewerStopListener(LogViewerStopListener listener) {
297         stopListeners.add(listener);
298     }
299
300     /**
301      * Remove all registered <code>LogViewerStopListener</code> listeners.
302      *
303      * @param listener <code>LogViewerStopListener</code> which will be notified
304      * when the <code>LogViewer</code> stops running.
305      */

306     public void removeAllLogViewerStopListener() {
307         stopListeners.removeAll(stopListeners);
308     }
309     
310     private void fireLogViewerStopListener() {
311         for (Iterator JavaDoc i = stopListeners.iterator(); i.hasNext();) {
312             ((LogViewerStopListener)i.next()).callOnStop();
313         }
314     }
315     
316     /**
317      * <code>LogViewerStopListener</code> is notified when the <code>LogViewer</code>
318      * stops running.
319      */

320     public static interface LogViewerStopListener extends EventListener JavaDoc {
321         public void callOnStop();
322     }
323     
324     /**
325      * Support class for context log line analyzation and for creating links in
326      * the output window.
327      */

328     static class ContextLogSupport extends LogSupport {
329         private final String JavaDoc CATALINA_WORK_DIR;
330         private String JavaDoc context = null;
331         private String JavaDoc prevMessage = null;
332         private static final String JavaDoc STANDARD_CONTEXT = "StandardContext["; // NOI18N
333
private static final int STANDARD_CONTEXT_LENGTH = STANDARD_CONTEXT.length();
334         private GlobalPathRegistry globalPathReg = GlobalPathRegistry.getDefault();
335         
336
337         public ContextLogSupport(String JavaDoc catalinaWork, String JavaDoc webAppContext) {
338             CATALINA_WORK_DIR = catalinaWork;
339             context = webAppContext;
340         }
341         
342         public LineInfo analyzeLine(String JavaDoc logLine) {
343             String JavaDoc path = null;
344             int line = -1;
345             String JavaDoc message = null;
346             boolean error = false;
347             boolean accessible = false;
348
349             logLine = logLine.trim();
350             int lineLenght = logLine.length();
351
352             // look for unix file links (e.g. /foo/bar.java:51: 'error msg')
353
if (logLine.startsWith("/")) {
354                 error = true;
355                 int colonIdx = logLine.indexOf(':');
356                 if (colonIdx > -1) {
357                     path = logLine.substring(0, colonIdx);
358                     accessible = true;
359                     if (lineLenght > colonIdx) {
360                         int nextColonIdx = logLine.indexOf(':', colonIdx + 1);
361                         if (nextColonIdx > -1) {
362                             String JavaDoc lineNum = logLine.substring(colonIdx + 1, nextColonIdx);
363                             try {
364                                 line = Integer.valueOf(lineNum).intValue();
365                             } catch(NumberFormatException JavaDoc nfe) {
366                                 // ignore it
367
TomcatManager.ERR.notify(ErrorManager.INFORMATIONAL, nfe);
368                             }
369                             if (lineLenght > nextColonIdx) {
370                                 message = logLine.substring(nextColonIdx + 1, lineLenght);
371                             }
372                         }
373                     }
374                 }
375             }
376             // look for windows file links (e.g. c:\foo\bar.java:51: 'error msg')
377
else if (lineLenght > 3 && Character.isLetter(logLine.charAt(0))
378                         && (logLine.charAt(1) == ':') && (logLine.charAt(2) == '\\')) {
379                 error = true;
380                 int secondColonIdx = logLine.indexOf(':', 2);
381                 if (secondColonIdx > -1) {
382                     path = logLine.substring(0, secondColonIdx);
383                     accessible = true;
384                     if (lineLenght > secondColonIdx) {
385                         int thirdColonIdx = logLine.indexOf(':', secondColonIdx + 1);
386                         if (thirdColonIdx > -1) {
387                             String JavaDoc lineNum = logLine.substring(secondColonIdx + 1, thirdColonIdx);
388                             try {
389                                 line = Integer.valueOf(lineNum).intValue();
390                             } catch(NumberFormatException JavaDoc nfe) { // ignore it
391
TomcatManager.ERR.notify(ErrorManager.INFORMATIONAL, nfe);
392                             }
393                             if (lineLenght > thirdColonIdx) {
394                                 message = logLine.substring(thirdColonIdx + 1, lineLenght);
395                             }
396                         }
397                     }
398                 }
399             }
400             // look for stacktrace links (e.g. at java.lang.Thread.run(Thread.java:595)
401
// at t.HyperlinkTest$1.run(HyperlinkTest.java:24))
402
else if (logLine.startsWith("at ") && lineLenght > 3) {
403                 error = true;
404                 int parenthIdx = logLine.indexOf('(');
405                 if (parenthIdx > -1) {
406                     String JavaDoc classWithMethod = logLine.substring(3, parenthIdx);
407                     int lastDotIdx = classWithMethod.lastIndexOf('.');
408                     if (lastDotIdx > -1) {
409                         int lastParenthIdx = logLine.lastIndexOf(')');
410                         int lastColonIdx = logLine.lastIndexOf(':');
411                         if (lastParenthIdx > -1 && lastColonIdx > -1) {
412                             String JavaDoc lineNum = logLine.substring(lastColonIdx + 1, lastParenthIdx);
413                             try {
414                                 line = Integer.valueOf(lineNum).intValue();
415                             } catch(NumberFormatException JavaDoc nfe) { // ignore it
416
TomcatManager.ERR.notify(ErrorManager.INFORMATIONAL, nfe);
417                             }
418                             message = prevMessage;
419                         }
420                         int firstDolarIdx = classWithMethod.indexOf('$'); // > -1 for inner classes
421
String JavaDoc className = classWithMethod.substring(0, firstDolarIdx > -1 ? firstDolarIdx : lastDotIdx);
422                         path = className.replace('.','/') + ".java"; // NOI18N
423
accessible = globalPathReg.findResource(path) != null;
424                         if (className.startsWith("org.apache.jsp.") && context != null) { // NOI18N
425
if (context != null) {
426                                 String JavaDoc contextPath = context.equals("/")
427                                                         ? "/_" // hande ROOT context
428
: context;
429                                 path = CATALINA_WORK_DIR + contextPath + "/" + path;
430                                 accessible = new File JavaDoc(path).exists();
431                             }
432                         }
433                     }
434                 }
435             }
436             // every other message treat as normal info message
437
else {
438                 prevMessage = logLine;
439                 // try to get context, if stored
440
int stdContextIdx = logLine.indexOf(STANDARD_CONTEXT);
441                 int lBracketIdx = -1;
442                 if (stdContextIdx > -1) {
443                     lBracketIdx = stdContextIdx + STANDARD_CONTEXT_LENGTH;
444                 }
445                 int rBracketIdx = logLine.indexOf(']');
446                 if (lBracketIdx > -1 && rBracketIdx > -1 && rBracketIdx > lBracketIdx) {
447                     context = logLine.substring(lBracketIdx, rBracketIdx);
448                 }
449             }
450             return new LineInfo(path, line, message, error, accessible);
451         }
452     }
453 }
Popular Tags