/*******************************************************************************
*
* Copyright (c) 2010-2011, CloudBees, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*
*
*******************************************************************************/
/*
* The MIT License
*
* Copyright (c) 2010, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.console;
import hudson.Extension;
import hudson.MarkupText;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Placed on the beginning of the exception stack trace produced by Hudson,
* which in turn produces hyperlinked stack trace.
*
* <p> Exceptions in the user code (like junit etc) should be handled
* differently. This is only for exceptions that occur inside Hudson.
*
* @author Kohsuke Kawaguchi
* @since 1.349
*/
public class HudsonExceptionNote extends ConsoleNote<Object> {
@Override
public ConsoleAnnotator annotate(Object context, MarkupText text, int charPos) {
// An exception stack trace looks like this:
// org.acme.FooBarException: message
// <TAB>at org.acme.Foo.method(Foo.java:123)
// Caused by: java.lang.ClassNotFoundException:
String line = text.getText();
int end = line.indexOf(':', charPos);
if (end < 0) {
if (CLASSNAME.matcher(line.substring(charPos)).matches()) {
end = line.length();
} else {
return null; // unexpected format. abort.
}
}
text.addHyperlinkLowKey(charPos, end, annotateClassName(line.substring(charPos, end)));
return new ConsoleAnnotator() {
public ConsoleAnnotator annotate(Object context, MarkupText text) {
String line = text.getText();
Matcher m = STACK_TRACE_ELEMENT.matcher(line);
if (m.find()) {// allow the match to happen in the middle of a line to cope with prefix. Ant and Maven put them, among many other tools.
text.addHyperlinkLowKey(m.start() + 4, m.end(), annotateMethodName(m.group(1), m.group(2), m.group(3), Integer.parseInt(m.group(4))));
return this;
}
int idx = line.indexOf(CAUSED_BY);
if (idx >= 0) {
int s = idx + CAUSED_BY.length();
int e = line.indexOf(':', s);
if (e < 0) {
e = line.length();
}
text.addHyperlinkLowKey(s, e, annotateClassName(line.substring(s, e)));
return this;
}
if (AND_MORE.matcher(line).matches()) {
return this;
}
// looks like we are done with the stack trace
return null;
}
};
}
// TODO; separate out the annotations and mark up
private String annotateMethodName(String className, String methodName, String sourceFileName, int lineNumber) {
return "http://stacktrace.hudson-ci.org/search/?query=" + className + '.' + methodName + "&entity=method";
}
private String annotateClassName(String className) {
return "http://stacktrace.hudson-ci.org/search?query=" + className;
}
@Extension
public static final class DescriptorImpl extends ConsoleAnnotationDescriptor {
@Override
public String getDisplayName() {
return "Exception Stack Trace";
}
}
/**
* Regular expression that represents a valid class name.
*/
private static final String CLASSNAME_PATTERN = "[\\p{L}0-9$_.]+";
private static final Pattern CLASSNAME = Pattern.compile(CLASSNAME_PATTERN + "\r?\n?");
/**
* Matches to the line like "\tat org.acme.Foo.method(File.java:123)" and
* captures class name, method name, source file name, and line number
* separately.
*/
private static final Pattern STACK_TRACE_ELEMENT = Pattern.compile("\tat (" + CLASSNAME_PATTERN + ")\\.([\\p{L}0-9$_<>]+)\\((\\S+):([0-9]+)\\)");
private static final String CAUSED_BY = "Caused by: ";
private static final Pattern AND_MORE = Pattern.compile("\t... [0-9]+ more\n");
}