Q:

How to avoid 'javassist.CannotCompileException: no method body'

I'm using Java instrumentation and Javassist to insert print statements into methods. This mostly works without errors, but for some class methods (e.g. java.util.TimeZone.getSystemTimeZoneID) I get the following exception:
javassist.CannotCompileException: no method body
        at javassist.CtBehavior.insertBefore(CtBehavior.java:695)
        at javassist.CtBehavior.insertBefore(CtBehavior.java:685)

My code attempts to avoid this issue by checking CtBehaviour.isEmpty(), but this makes no difference. Any suggestions on how to avoid this?
Here is a minimal example:
public class ExceptionExample implements ClassFileTransformer {

  private static final ClassPool pool = ClassPool.getDefault();

  public static void premain(String agentArgument,
      Instrumentation instrumentation) {
    instrumentation.addTransformer(new ExceptionExample());
  }

  @Override
  public byte[] transform(final ClassLoader loader, final String className,
      final Class<?> classBeingRedefined,
      final ProtectionDomain protectionDomain, final byte[] classfileBuffer)
      throws IllegalClassFormatException {

    String dottedClassName = className.replace('/', '.');

    if (dottedClassName.startsWith("java.lang")
        || dottedClassName.startsWith("java.util")) {

      try {
        System.out.println("Instrumenting: " + dottedClassName);
        return adjustClass(dottedClassName, classBeingRedefined,
            classfileBuffer);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }

    return classfileBuffer;
  }

  private byte[] adjustClass(final String className,
      final Class<?> classBeingRedefined, final byte[] classfileBuffer)
      throws IOException, RuntimeException, CannotCompileException {

    CtClass cl = null;

    try {
      cl = pool.makeClass(new java.io.ByteArrayInputStream(classfileBuffer));
      if (!cl.isInterface()) {
        CtBehavior[] methods = cl.getDeclaredBehaviors();
        for (CtBehavior method : methods) {
          if (!method.isEmpty()) {
            try {
              method
                  .insertBefore(String.format(
                      "System.out.println(\"CALLING: %s\");",
                      method.getLongName()));
            } catch (Throwable t) {
              System.out.println("Error instrumenting " + className + "."
                  + method.getName());
              t.printStackTrace();
            }
          }
        }
        return cl.toBytecode();
      }
    } finally {
      if (cl != null) {
        cl.detach();
      }
    }
    return classfileBuffer;
  }
}

And here's a little class I'm testing against:
public class Main {

  public static void main(String[] args) throws Exception {
    Calendar c = Calendar.getInstance();
    System.out.println(c.get(Calendar.YEAR));
  }
}

Example output (abbreviated):
Instrumenting: java.util.Calendar
CALLING: java.util.Calendar.<clinit>()
CALLING: java.util.Calendar.getInstance()
Instrumenting: java.util.TimeZone
Error instrumenting java.util.TimeZone.getSystemTimeZoneID
javassist.CannotCompileException: no method body
        at javassist.CtBehavior.insertBefore(CtBehavior.java:695)
        at javassist.CtBehavior.insertBefore(CtBehavior.java:685)
        at com.cryptomathic.test.instrument.ExceptionExample.adjustClass(ExceptionExample.java:56)
        at com.cryptomathic.test.instrument.ExceptionExample.transform(ExceptionExample.java:34)
        at sun.instrument.TransformerManager.transform(Unknown Source)
        at sun.instrument.InstrumentationImpl.transform(Unknown Source)
        at java.util.Calendar.getInstance(Unknown Source)
        at test.Main.main(Main.java:8)
Error instrumenting java.util.TimeZone.getSystemGMTOffsetID
javassist.CannotCompileException: no method body
        at javassist.CtBehavior.insertBefore(CtBehavior.java:695)
        at javassist.CtBehavior.insertBefore(CtBehavior.java:685)
        at com.cryptomathic.test.instrument.ExceptionExample.adjustClass(ExceptionExample.java:56)
        at com.cryptomathic.test.instrument.ExceptionExample.transform(ExceptionExample.java:34)
        at sun.instrument.TransformerManager.transform(Unknown Source)
        at sun.instrument.InstrumentationImpl.transform(Unknown Source)
        at java.util.Calendar.getInstance(Unknown Source)
        at test.Main.main(Main.java:8)
CALLING: java.util.TimeZone.<clinit>()
CALLING: java.util.TimeZone.getDefaultRef()
CALLING: java.util.TimeZone.getDefaultInAppContext()
CALLING: java.util.TimeZone.setDefaultZone()
CALLING: java.util.TimeZone.getTimeZone(java.lang.String,boolean)
CALLING: java.util.TimeZone.setID(java.lang.String)
...

A:

You need to first check if it is a native method as these methods (insertBefore(), insertAfter()..) are not supported for native methods. I have written a small method to detect whether CtMethod is native or not:
public static boolean isNative(CtMethod method) {
    return Modifier.isNative(method.getModifiers());
}

I have tested it and it works and solves your problem.
Note: Native methods cannot be instrumented because they have no bytecodes. However if native method prefix is supported ( Transformer.isNativeMethodPrefixSupported() ) then you can use Transformer.setNativeMethodPrefix() to wrap a native method call inside a non-native call which can then be instrumented.

