External XML Annotations

There are several cases when you either cannot put annotations in the source code to make the thread checker work: Either you decide you do not want to pollute the source with the annotations (even though I see them more as enhanced documentation), or you do not have the source to begin with. The latter is sort of the case with the Java Standard API. The source is there, but I do not want to recompile it.

For these cases, I have developed an XML format that allows the developer to specify the threads allowed or not allowed to run certain methods or classes in an external file. It’s based on the XML configuration library that I wrote a while ago. Consider the file below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<concutest>
  <threadcheck>
    <sample>
      <threadCheck>
        <ThreadCheckSample4>
          <class>
            <name type="only" value="childclass1"/>
            <name type="not" value="childclass2"/>
            <group type="only" value="childclassgroup1"/>
            <group type="not" value="childclassgroup2"/>
            <id type="only" value="1001"/>
            <id type="not" value="1002"/>
            <eventThread type="only" value="true"/>
          </class>
          <method sig="run()V">
            <name type="only" value="childclass-method1"/>
            <name type="not" value="childclass-method2"/>
          </method>
        </ThreadCheckSample4>
      </threadCheck>
    </sample>
  </threadcheck>
</concutest>

This XML file expresses the same checks as the annotations in the Java file here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
@OnlyRunBy(@ThreadDesc(name="childclass1"),
           @ThreadDesc(group="childclassgroup1"),
           @ThreadDesc(id=1001),
           @ThreadDesc(eventThread=true))
@NotRunBy(@ThreadDesc(name="childclass2"),
          @ThreadDesc(group="childclassgroup2"),
          @ThreadDesc(id=1002))
public class ThreadCheckSample4 ... {

    @OnlyRunBy(@ThreadDesc(name="childclass-method1"))
    @NotRunBy(@ThreadDesc(name="childclass-method2"))
    public void run() { ... }
}

The rationale for this design was the paths I use to access nodes in the XML. The path to get to the <ThreadCheckSample4> node, for example, is concutest.threadcheck.sample.threadCheck.ThreadCheckSample4, so if you ignore the initial concutest.threadcheck, which designates the project and sub-project, then that’s exactly the fully-qualified class name, so look-up is trivial.

Then there is a <class> tag that can contain any number of nested <name>, <group>, <id> and <eventThread> tags that correspond to the @ThreadDesc annotations. A type attribute chooses between @OnlyRunBy and @NotRunBy, and a value attribute actually contains the thread description.

For methods, there can be any number of <method> nodes. Which method they describe is chosen by the sig attribute that contains a concatenated method name and descriptor. The rest is the same as for the <class> node.

What I need now is an easy way to generate these XML files, perhaps by using some sort of class/jar file browser, and generally an easier way to perform the instrumentation so I can release the thread checker independently from the rest of the framework.

Now that I have a list of methods that are deemed save, I can start marking many, many classes and methods as unsafe to be executed outside the event thread. That’s when things will actually get interesting. And I also need to run the DrJava unit test suite. In normal operation and without Swing annotated, I didn’t find a serious problem in DrJava yet. That’s good, of course. It may stay that way. But it would also be kind of nice to show my tools on buggy programs…

I’ve extended the strategy that adds thread checks a little: It can now handle an arbitrary number of XML annotation files, separated by the "path.separator". That way I can create a dedicated file just for the Java runtime.

The instrumentation is running now and I’m taking a blogging break, and it’s taking a while… I noticed that when instrumenting the drjava-15.jar file already. It seems like the frequent queries for superclasses and superinterfaces take a long time whenever jar files are involved.

So there are two things that I need to check:

  1. Does caching of class and method annotations work? What kind of cache efficiency am I getting?
  2. Should I create a temporary directory, unpack a jar file, work with loose class files, then pack them up again and delete the directory?

This was a good night’s work on the eve of labor day, but I should seriously work on sleeping soon.

Share

About Mathias

Software development engineer. Principal developer of DrJava. Recent Ph.D. graduate from the Department of Computer Science at Rice University.
This entry was posted in Concurrent Unit Testing. Bookmark the permalink.

Leave a Reply