KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > project > JavaAntLogger


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.java.project;
21
22 import java.io.File JavaDoc;
23 import java.net.MalformedURLException JavaDoc;
24 import java.net.URL JavaDoc;
25 import java.util.Arrays JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.Collections JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.LinkedHashSet JavaDoc;
30 import java.util.StringTokenizer JavaDoc;
31 import java.util.regex.Matcher JavaDoc;
32 import java.util.regex.Pattern JavaDoc;
33 import org.apache.tools.ant.module.spi.AntEvent;
34 import org.apache.tools.ant.module.spi.AntLogger;
35 import org.apache.tools.ant.module.spi.AntSession;
36 import org.netbeans.api.java.classpath.ClassPath;
37 import org.netbeans.api.java.classpath.GlobalPathRegistry;
38 import org.netbeans.api.java.platform.JavaPlatform;
39 import org.netbeans.api.java.platform.JavaPlatformManager;
40 import org.netbeans.api.java.queries.SourceForBinaryQuery;
41 import org.openide.filesystems.FileObject;
42 import org.openide.filesystems.FileStateInvalidException;
43 import org.openide.filesystems.FileUtil;
44
45 /**
46  * Ant logger which handles Java- and Java-project-specific UI.
47  * Specifically, handles stack traces hyperlinking and suppresses
48  * hyperlinking of nbproject/build-impl.xml files.
49  * @author Jesse Glick
50  * @see "#42525"
51  */

52 public final class JavaAntLogger extends AntLogger {
53     
54     // XXX handle Unicode elements as well
55
// XXX handle unknown source too (#17734)? or don't bother?
56

57     /**
58      * Regexp matching one line (not the first) of a stack trace.
59      * Captured groups:
60      * <ol>
61      * <li>package
62      * <li>filename
63      * <li>line number
64      * </ol>
65      */

66     private static final Pattern JavaDoc STACK_TRACE = Pattern.compile(
67     "(?:\t|\\[catch\\] )at ((?:[a-zA-Z_$][a-zA-Z0-9_$]*\\.)*)[a-zA-Z_$][a-zA-Z0-9_$]*\\.[a-zA-Z_$<][a-zA-Z0-9_$>]*\\(([a-zA-Z_$][a-zA-Z0-9_$]*\\.java):([0-9]+)\\)"); // NOI18N
68

69     /**
70      * Regexp matching the first line of a stack trace, with the exception message.
71      * Captured groups:
72      * <ol>
73      * <li>unqualified name of exception class plus possible message
74      * </ol>
75      */

76     private static final Pattern JavaDoc EXCEPTION_MESSAGE = Pattern.compile(
77     // #42894: JRockit uses "Main Thread" not "main"
78
"(?:Exception in thread \"(?:main|Main Thread)\" )?(?:(?:[a-zA-Z_$][a-zA-Z0-9_$]*\\.)+)([a-zA-Z_$][a-zA-Z0-9_$]*(?:: .+)?)"); // NOI18N
79

80     /**
81      * Regexp matching part of a Java task's invocation debug message
82      * that specifies the classpath.
83      * Hack to find the classpath an Ant task is using.
84      * Cf. Commandline.describeArguments, issue #28190.
85      * Captured groups:
86      * <ol>
87      * <li>the classpath
88      * </ol>
89      */

90     private static final Pattern JavaDoc CLASSPATH_ARGS = Pattern.compile("\r?\n'-classpath'\r?\n'(.*)'\r?\n"); // NOI18N
91

92     /**
93      * Regexp matching part of a Java task's invocation debug message
94      * that specifies java executable.
95      * Hack to find JDK used for execution.
96      */

97     private static final Pattern JavaDoc JAVA_EXECUTABLE = Pattern.compile("^Executing '(.*)' with arguments:$", Pattern.MULTILINE); // NOI18N
98

99     /**
100      * Ant task names we will pay attention to.
101      */

102     private static final String JavaDoc[] TASKS_OF_INTEREST = {
103         // XXX should this really be restricted? what about stack traces printed during shutdown?
104
"java", // NOI18N
105
// #44328: unit tests run a different task:
106
"junit", // NOI18N
107
// Nice to handle stack traces from e.g. NB's own build system too!
108
"exec", // NOI18N
109
// #63065: Mobility execution
110
"nb-run", //NOI18N
111
};
112     
113     private static final int[] LEVELS_OF_INTEREST = {
114         AntEvent.LOG_VERBOSE, // for CLASSPATH_ARGS
115
AntEvent.LOG_INFO, // for some stack traces
116
AntEvent.LOG_WARN, // for most stack traces
117
AntEvent.LOG_ERR, // for some stack traces, incl. those redelivered from StandardLogger
118
};
119     
120     /**
121      * Data stored in the session.
122      */

123     private static final class SessionData {
124         public ClassPath platformSources = null;
125         public String JavaDoc classpath = null;
126         public Collection JavaDoc<FileObject> classpathSourceRoots = null;
127         public String JavaDoc possibleExceptionText = null;
128         public String JavaDoc lastExceptionMessage = null;
129         public SessionData() {}
130         public void setClasspath(String JavaDoc cp) {
131             classpath = cp;
132             classpathSourceRoots = null;
133         }
134         public void setPlatformSources(ClassPath platformSources) {
135             this.platformSources = platformSources;
136             classpathSourceRoots = null;
137         }
138     }
139     
140     /** Default constructor for lookup. */
141     public JavaAntLogger() {}
142     
143     public boolean interestedInSession(AntSession session) {
144         return true;
145     }
146     
147     public boolean interestedInAllScripts(AntSession session) {
148         return true;
149     }
150     
151     public String JavaDoc[] interestedInTargets(AntSession session) {
152         return AntLogger.ALL_TARGETS;
153     }
154     
155     public String JavaDoc[] interestedInTasks(AntSession session) {
156         return TASKS_OF_INTEREST;
157     }
158     
159     public int[] interestedInLogLevels(AntSession session) {
160         // XXX could exclude those in [INFO..ERR] greater than session.verbosity
161
return LEVELS_OF_INTEREST;
162     }
163     
164     private SessionData getSessionData(AntSession session) {
165         SessionData data = (SessionData) session.getCustomData(this);
166         if (data == null) {
167             data = new SessionData();
168             session.putCustomData(this, data);
169         }
170         return data;
171     }
172     
173     public void messageLogged(AntEvent event) {
174         AntSession session = event.getSession();
175         int messageLevel = event.getLogLevel();
176         int sessionLevel = session.getVerbosity();
177         SessionData data = getSessionData(session);
178         String JavaDoc line = event.getMessage();
179         assert line != null;
180
181         Matcher JavaDoc m = STACK_TRACE.matcher(line);
182         if (m.matches()) {
183             // We have a stack trace.
184
String JavaDoc pkg = m.group(1);
185             String JavaDoc filename = m.group(2);
186             String JavaDoc resource = pkg.replace('.', '/') + filename;
187             int lineNumber = Integer.parseInt(m.group(3));
188             // Check to see if the class is listed in our per-task sourcepath.
189
// XXX could also look for -Xbootclasspath etc., but probably less important
190
Iterator JavaDoc it = getCurrentSourceRootsForClasspath(data).iterator();
191             while (it.hasNext()) {
192                 FileObject root = (FileObject)it.next();
193                 // XXX this is apparently pretty expensive; try to use java.io.File instead
194
FileObject source = root.getFileObject(resource);
195                 if (source != null) {
196                     // Got it!
197
hyperlink(line, session, event, source, messageLevel, sessionLevel, data, lineNumber);
198                     break;
199                 }
200             }
201             // Also check global sourcepath (sources of open projects, and sources
202
// corresponding to compile or boot classpaths of open projects).
203
// Fallback in case a JAR file is copied to an unknown location, etc.
204
// In this case we can't be sure that this source file really matches
205
// the .class used in the stack trace, but it is a good guess.
206
if (!event.isConsumed()) {
207                 FileObject source = GlobalPathRegistry.getDefault().findResource(resource);
208                 if (source != null) {
209                     hyperlink(line, session, event, source, messageLevel, sessionLevel, data, lineNumber);
210                 }
211             }
212         } else {
213             // Track the last line which was not a stack trace - probably the exception message.
214
data.possibleExceptionText = line;
215             data.lastExceptionMessage = null;
216         }
217         
218         // Look for classpaths.
219
if (messageLevel == AntEvent.LOG_VERBOSE) {
220             Matcher JavaDoc m2 = CLASSPATH_ARGS.matcher(line);
221             if (m2.find()) {
222                 String JavaDoc cp = m2.group(1);
223                 data.setClasspath(cp);
224             }
225             // XXX should also probably clear classpath when taskFinished called
226
m2 = JAVA_EXECUTABLE.matcher(line);
227             if (m2.find()) {
228                 String JavaDoc executable = m2.group(1);
229                 ClassPath platformSources = findPlatformSources(executable);
230                 if (platformSources != null) {
231                     data.setPlatformSources(platformSources);
232                 }
233             }
234         }
235     }
236     
237     private ClassPath findPlatformSources(String JavaDoc javaExecutable) {
238         for (JavaPlatform p : JavaPlatformManager.getDefault().getInstalledPlatforms()) {
239             FileObject fo = p.findTool("java"); // NOI18N
240
if (fo != null) {
241                 File JavaDoc f = FileUtil.toFile(fo);
242                 if (f.getAbsolutePath().startsWith(javaExecutable)) {
243                     return p.getSourceFolders();
244                 }
245             }
246         }
247         return null;
248     }
249     
250     private static void hyperlink(String JavaDoc line, AntSession session, AntEvent event, FileObject source, int messageLevel, int sessionLevel, SessionData data, int lineNumber) {
251         if (messageLevel <= sessionLevel && !event.isConsumed()) {
252             event.consume();
253             try {
254                 session.println(line, true, session.createStandardHyperlink(source.getURL(), guessExceptionMessage(data), lineNumber, -1, -1, -1));
255             } catch (FileStateInvalidException e) {
256                 assert false : e;
257             }
258         }
259     }
260     
261     /**
262      * Finds source roots corresponding to the apparently active classpath
263      * (as reported by logging from Ant when it runs the Java launcher with -cp).
264      */

265     private static Collection JavaDoc/*<FileObject>*/ getCurrentSourceRootsForClasspath(SessionData data) {
266         if (data.classpath == null) {
267             return Collections.EMPTY_SET;
268         }
269         if (data.classpathSourceRoots == null) {
270             data.classpathSourceRoots = new LinkedHashSet JavaDoc<FileObject>();
271             StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(data.classpath, File.pathSeparator);
272             while (tok.hasMoreTokens()) {
273                 String JavaDoc binrootS = tok.nextToken();
274                 File JavaDoc f = FileUtil.normalizeFile(new File JavaDoc(binrootS));
275                 URL JavaDoc binroot;
276                 try {
277                     binroot = f.toURI().toURL();
278                 } catch (MalformedURLException JavaDoc e) {
279                     throw new AssertionError JavaDoc(e);
280                 }
281                 if (FileUtil.isArchiveFile(binroot)) {
282                     URL JavaDoc root = FileUtil.getArchiveRoot(binroot);
283                     if (root != null) {
284                         binroot = root;
285                     }
286                 }
287                 FileObject[] someRoots = SourceForBinaryQuery.findSourceRoots(binroot).getRoots();
288                 data.classpathSourceRoots.addAll(Arrays.asList(someRoots));
289             }
290             if (data.platformSources != null) {
291                 data.classpathSourceRoots.addAll(Arrays.asList(data.platformSources.getRoots()));
292             } else {
293                 // no platform found. use default one:
294
JavaPlatform plat = JavaPlatform.getDefault();
295                 // in unit tests the default platform may be null:
296
if (plat != null) {
297                     data.classpathSourceRoots.addAll(Arrays.asList(plat.getSourceFolders().getRoots()));
298                 }
299             }
300         }
301         return data.classpathSourceRoots;
302     }
303     
304     private static String JavaDoc guessExceptionMessage(SessionData data) {
305         if (data.possibleExceptionText != null) {
306             if (data.lastExceptionMessage == null) {
307                 Matcher JavaDoc m = EXCEPTION_MESSAGE.matcher(data.possibleExceptionText);
308                 if (m.matches()) {
309                     data.lastExceptionMessage = m.group(1);
310                 } else {
311                     data.possibleExceptionText = null;
312                 }
313             }
314             return data.lastExceptionMessage;
315         }
316         return null;
317     }
318 }
319
Popular Tags