JAVA custom Annotation & Concurrent Versions System
I manage my sources using CVS (Concurrent Versions System). When a user reported a bug in one of my java program, I faced the problem to get the version of the java class where the Exception was throwed..... I don't know if this was described but here is my solution using a custom java annotation. CVS can use a mechanism known as keyword substitution (or keyword expansion) to help identifying the files. Embedded strings of the form $keyword$ and $keyword:…$ in a file are replaced with strings of the form $keyword:value$ whenever you obtain a new revision of the file. Here I used Date,Source and Revision.
First I defined a new custom Annotation called @RCS returning the author, a date and a revision.
import java.lang.annotation.*;
/**
* describe a Revision Control System
* @author pierre
*
*/
@Retention(RetentionPolicy.RUNTIME) /* The annotation should be available for reflection at runtime.*/
@Target(ElementType.TYPE) /* place in : class, interface, enum */
public @interface RCS {
/** author of this source */
String author() default '[undefined]';
/** revision number of this source */
String revision() default '[undefined]';
/** date of revision of this source */
String date() default '[undefined]';
/** file name for this source */
String source() default '[undefined]';
}
and here are two files used for test. Both classes contains a @RCS annotation which can be find at runtime. Test2 jsut throws an Exception. Test1 catch this exception and display the stack trace. for each StackTraceElement it tries to find a @RCS annotation.
import java.lang.annotation.*;
@RCS( author='$Author: $',
date='$Date: $',
source='$Source: $',
revision='$Revision: $'
)
public class Test
{
Test()
{
Test2 t=new Test2();
t.doSomething();
}
public static void main(String args[])
{
try
{
Test t= new Test();
}
catch(Exception err)
{
System.err.println(err.getLocalizedMessage());
for(StackTraceElement e: err.getStackTrace())
{
System.err.println('Class :\t\t\t'+e. getClassName());
System.err.println('File :\t\t\t'+e. getFileName());
System.err.println('Line :\t\t\t'+e. getLineNumber());
if(e.getClassName()!=null)
{
try {
Class c= Class.forName( e.getClassName());
RCS rcsInfo=(RCS)c.getAnnotation(RCS.class);
System.err.println('Revision:\t\t'+(rcsInfo==null?'N/A':rcsInfo.revision()));
System.err.println('Date:\t\t\t'+(rcsInfo==null?'N/A':rcsInfo.date()));
System.err.println('Author:\t\t\t'+(rcsInfo==null?'N/A':rcsInfo.author()));
}
catch (Exception err2)
{
}
}
System.err.println();
}
}
}
}
@RCS( author='$Author: $',
date='$Date: $',
source='$Source: $',
revision='$Revision: $'
)
public class Test2
{
Test2()
{
}
void doSomething()
{
throw new RuntimeException('Test');
}
}
when the files where committed the CVS fields were substituted by their correct values. Here is the stack trace as it was printed:
pierre@linux:~/tmp/src> cvs commit
(...)
pierre@linux:~/tmp/src> javac Test.java
pierre@linux:~/tmp/src> java Test
Test
Class : Test2
File : Test2.java
Line : 13
Revision: $Revision: 1.2 $
Date: $Date: 2006/10/31 18:10:44 $
Author: $Author: pierre $
Class : Test
File : Test.java
Line : 15
Revision: $Revision: 1.5 $
Date: $Date: 2006/10/31 18:54:08 $
Author: $Author: pierre $
Class : Test
File : Test.java
Line : 24
Revision: $Revision: 1.5 $
Date: $Date: 2006/10/31 18:54:08 $
Author: $Author: pierre $
That's it.
Pierre