The proxy function makes it easy for Clojure to interface with the Java layer, but I was dealing with an interface (the AIM Java API) that had an punitive number of things that needed to be overridden…
public void OnIdleStateChange(AccSession arg0, int arg1) { } public void OnInstanceChange(AccSession arg0, AccInstance arg1, AccInstance arg2, AccInstanceProp arg3) { } public void OnLookupUsersResult(AccSession arg0, String[] arg1, int arg2, AccResult arg3, AccUser[] arg4) { } public void OnSearchDirectoryResult(AccSession arg0, int arg1, AccResult arg2, AccDirEntry arg3) { } // ... go on like this for pagesThe Java code is here, if you’re interested in the entire set of calls. Now, I didn’t care about most of those events, but I had to override them, since they didn’t have a default implementation. What made this seem painful was that I was really only interested in two of the callbacks. So I started to record an Emacs macro to convert the Java code to the equivalent Clojure proxy statement, and then I realized that I didn’t have to — I was using a Lisp.
(defmacro auto-proxy [interfaces variables & args] (let [defined (set (map #(str (first %)) args)) names (fn [i] (map #(.getName %) (.getMethods i))) all-names (into #{} (apply concat (map names (map resolve interfaces)))) undefined (difference all-names defined) auto-gen (map (fn [x] `(~(symbol x) [& ~'args])) undefined)] `(proxy ~interfaces ~variables ~@args ~@auto-gen)))Auto-proxy works just like proxy, but it makes an empty implementation for any call that wasn’t defined. So, suddenly, what would have been a bunch of lines collapsed into just:
(defn create-aim-proxy [] (auto-proxy [com.aol.acc.AccEvents] [] (OnImReceived [session imSession participant im] (handle-im session imSession participant im)) (OnStateChange [arg0 arg1 arg2] (handle-state-change arg0 arg1 arg2))))Macros ftw. The nice thing about Clojure/Lisp is that it makes coding up this kind of reusable framework stuff really easy.
Comments are moderated whenever I remember that I have a blog.