Monday, July 21, 2008

Making jar files dependency graph using jarjar and dot

Our project has grown quite a bit over the past couple of years and with it the library folder containing third party jars . So much so that we are not sure now as to what is used and what is not. So we decided to do a bit of cleanup and remove the unused jars. However before we can do that we need to know which ones are used by code and which ones are not.

I have not been able to locate a tool that can print out run time dependencies of java code on a jar file. Anyway I am not sure if that can be done at all with static analysis. Leaving aside run time dependencies there are some tools that can print out the static dependencies between two jar files. After some googling and general goofing I settled on jarjar (http://code.google.com/p/jarjar/)

jarjar can print the dependencies between any two jar files (see http://sixlegs.com/blog/java/depfind.html). jarjar can print dependencies between multiple jar files also. Just separate the jar file names with a colon (UNIX) or semi colon (MS-WIN32) What I wanted was the multiple file version of command , A panacea to print dependencies between all my jar files in lib folder.

I made a batch file with all the jar file names (Using a perl script to print the required batch file) along the lines of
/>java -jar jarjar-1.0rc7.jar find jar activation-1.1.jar;ant-1.6.5.jar;antlr-2.7.2.jar.............. 70 more files.

I used the following script to generate my batch file

$argc = @ARGV;
if($argc != 1 ) {
print " USAGE : $perl read-dir.pl \n ";
exit(0);
}


$mydir = $ARGV[0] ;

opendir(dhandle, $mydir) || die("Cannot open directory");
@thefiles= readdir(dhandle);
closedir(dhandle);

print " java -jar jarjar-1.0rc7.jar --find --level=jar " ;

foreach $f (@thefiles) {
unless ( ($f eq ".") || ($f eq "..") || ($f eq ".svn" )) {
print "$f:";
}
}




The batch file runs for a while and then dies with an ArrayOutOfBoundException. Now I did not want to tackle any problems in jarjar so I took a workaround. Since jarjar can print dependencies between any two jars , I decided to use that fact in my solution. I just wrote a perl script that would store all the jar files in a folder and compare a pair of them at a time using jarjar. If we find any dependency we print it on console. (script attached below) The output of this script can be saved as a "dot" file.

The script ran for quite some time but in the end it produced dependencies of all jar files in the folder in dot-ready format. dot is a nifty program to print graphs that comes bundled with graphviz. (Graphviz website) if you print the dependencies in a format that dot can understand then you can use dot on this output to produce a graph. That was the intention. If you install graphviz then dot should already be in your system path.

Just run dot over the outfile file like, ( This link from oreilly linux dev center may be useful )

D:/project/lib/> dot -Tpng -o graph.png .dot


And now you have the dependencies graph. If you are not satisfied with quality then you can try OmniGraffle see link ( http://paco.to/blog/000040.html). However for Omnigraffle make sure

  • You save the output of perl script as a dot file (extension dot like jars-data.dot)
  • OmniGraffle will run on a Mac only


And here is the perl script to do dependency analysis



$argc = @ARGV;
if($argc != 1 ) {
print " USAGE : $perl jarjar.pl <dirname> \n ";
exit(0);
}


$mydir = $ARGV[0] ;

opendir(dhandle, $mydir) || die("Cannot open directory");
@files= readdir(dhandle);
closedir(dhandle);

# make extra copy of files
@alljars = @files ;

# program may end abruptly for all possible combnations
# To control the jar combinations we can look for files
# starting with following alphabets only
#

%allowed = qw/o p q r s t u v w x y z/;


foreach $file (@files) {
$starts_with = substr($file,0,1);
# process $file that starts with allowed letter and ends with jar
if ( $file=~/jar$/ && exists($allowed{$starts_with})) {
# go over all jar files
foreach $jar(@alljars) {
if ( $jar=~/jar$/ ) {
# compare this jar against the file
$command = "java -jar jarjar-1.0rc7.jar find jar $file $jar \n ";
#print $command ;
#open command pipe
open(CMD, "$command|");
$output = <CMD>;
close(CMD);
# print output in graphviz dot format
if(length($output) > 0 ) {
print " \"$file\" -> \"$jar\" \n " ;
}


}
}
}


}

exit(0);




© Life of a third world developer
Maira Gall