Relaxing visibilities using Javassist

October 25, 2010

I personally like to restrict the visibility of my classes and members as much as possible. However, package-private classes get quickly annoying when used in an interactive environment such as the Scala interpreter.

When we encountered this problem with one of our internal tools, we decided to generate derived versions of our JAR files with relaxed visibilities. Using Javassist, it was pretty straightforward.

First, we define the name of the JAR file we want to convert and the name of the directory in which in want the modified class files to be written. Re-packaging these files is left as an exercise to the reader. (Okay, we are actually re-packaging the class files with a simple shell script. There is nothing wrong with that, right?)

val jar = args(0)
val out = args(1)

Javassist’s ClassPool is then configured using the name of the JAR file.

import javassist._

val pool = ClassPool.getDefault
pool.insertClassPath(jar)

The next step is to get a CtClass for each of the class files in the JAR.

import java.util.jar._
import java.util.zip._

val classes = for (
  e   if e.endsWith(".class"))
    yield pool.getCtClass(e.replaceAll("/", ".").replaceAll("\.class",""))

Now, we relax the visibility of all the fields, methods, constructors and classes.

classes.foreach(c => 
  c.setModifiers(setPublic(c.getModifiers)))

classes.foreach(_.getDeclaredFields.foreach(f => 
  f.setModifiers(setPublic(f.getModifiers))))

classes.foreach(_.getDeclaredMethods.foreach(m => 
  m.setModifiers(setPublic(m.getModifiers))))

classes.foreach(_.getDeclaredConstructors.foreach(c => 
  c.setModifiers(setPublic(c.getModifiers))))

Finally, we write the modified classes in our output directory.

classes.foreach(_.writeFile(out))

Pretty simple, huh?