dimanche 10 avril 2016

Log all method calls to a Java object

I'm working on a Clojure wrapper for some Java library. In order to help me debug, I would like to log all calls to specific Java objects.

After searching how I might do this from a raw Java perspective, I discovered the java.lang.reflect.Proxy class and java.lang.reflect.InvocationHandler interfaces.

This led me to find a small snipped posted by R.H. a few years ago original thread here :

(defn debug-proxy [obj] 
  (java.lang.reflect.Proxy/newProxyInstance 
    (.. obj getClass getClassLoader) 
    (.. obj getClass getInterfaces) 
    (proxy [java.lang.reflect.InvocationHandler] [] 
      (invoke [proxy m args] 
        (apply println m args) 
        (.invoke m obj args)))))

Small note: I had to change java.lang.reflect.Proxy.newProxyInstance into java.lang.reflect/newProxyInstance to make it work in my REPL, since newProxyInstance is a static method. Corrected version is given so that you may cut & paste it.

With this version, calling methods on the proxy appear work at first :

(.charAt (debug-proxy "foo") 2) 
#object[java.lang.reflect.Method 0x53ce1eb0 public abstract char java.lang.CharSequence.charAt(int)] 2
=> \o

But sadly, calling a method that accepts no argument will not work:

(.toUpperCase (debug-proxy "foo")) 
IllegalArgumentException No matching field found: toUpperCase for class com.sun.proxy.$Proxy1  clojure.lang.Reflector.getInstanceField (Reflector.java:271)

I tried to update the invoke implementation following advice given in this clojuredocs comment, but it didn't fix.

Here is my (still broken) attempt at implementing the comment's suggestion:

(defn- debug-print-invoke [obj proxy m args]
  (apply println m args)
  (.invoke m obj args))

(defn debug-proxy [obj]
  (java.lang.reflect.Proxy/newProxyInstance
    (.. obj getClass getClassLoader)
    (.. obj getClass getInterfaces)
    (proxy [java.lang.reflect.InvocationHandler] []
      (invoke
        ([proxy m] (debug-print-invoke obj proxy m []))
        ([proxy m args] (debug-print-invoke obj proxy m args))))))

A good answer would either fix the snippet I've been working with, or suggest an alternative way to achieve the same goal from Clojure.





Aucun commentaire:

Enregistrer un commentaire