In my last post BuildListener using Groovy In Ant Scriptdef, I got a scripted BuildListener to work. Was wondering if there were a way to use a Proxy to stand in for the listener.
See update below for another attempt that succeeds.
In short, I can’t. The first problem was classloader issues, but I think I fixed that by clearing the CLASSPATH:
set CLASSPATH =
Then a host of issues, until finally I give up. I think the big complexity is that BuildListener is an Interface. That means you have to jump hoops to proxy it. I’m sure Groovy can do this, but I don’t see it yet. May need more info on Groovy’s MOP.
Perhaps there is too much indirection being used. Is modern software engineering just ingenious ways to hide a GOTO? Just kidding.
Using commons-proxy library
Apache’s commons-proxy puts a more usable framework around different proxy factories.
Listing 1 – Proxy using Javassist library
import org.apache.commons.proxy.factory.javassist.* import java.lang.reflect.* import org.apache.commons.proxy.* import org.apache.tools.ant.* class MyListener implements Invoker { static int count = 0; Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable{ new File("finished.run").setText("count: ${count++}") } } MyListener proxy = new MyListener() java.lang.ClassLoader loader = proxy.class.getClassLoader() JavassistProxyFactory factory = new JavassistProxyFactory() Object listener = factory.createInvokerProxy( loader, proxy, [BuildListener.class] as Class[] ); project.addBuildListener(listener) // end source
Listing 2 – build script
<project name="listener" default="default" basedir="."> <!-- File: build.xml, author: J.Betancourt, date:2011-08-18T2137 --> <path id="libs"> <fileset dir="lib"> <include name="commons-proxy-1.0.jar" /> <include name="javassist.jar"/> <include name="groovy-all-1.8.0.jar"/> </fileset> </path> <!-- Groovy library --> <typedef resource="org/codehaus/groovy/antlib.xml" classpathref="libs" /> <!-- sets a BuildListener to the project --> <scriptdef name="set-listener" language="Groovy" classpathref="libs" src="lib/BuildListener.groovy"> <attribute name="endTarget"/> </scriptdef> <!-- install the listener --> <set-listener endTarget="list"/> <target name="default"> <fail message="doh!"/> </target> </project>
Listing 3 – stacktrace snippet
C:tempantgroovyListener>ant Buildfile: C:tempantgroovyListenerbuild.xml BUILD FAILED java.lang.ClassCircularityError: groovy/runtime/metaclass/java/io/FileMetaClass at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:169) at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127) at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122) at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165) at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:242) at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751) at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:71) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190) at MyListener.invoke(Script1.groovy:10) at JavassistUtilsGenerated_0.messageLogged(JavassistUtilsGenerated_0.java) at org.apache.tools.ant.Project.fireMessageLoggedEvent(Project.java:2254) at org.apache.tools.ant.Project.fireMessageLogged(Project.java:2290)
This works, but just as wordy as the original using a BuildListener class implementation.
import org.apache.tools.ant.* listenerMap = [ theTarget : attributes.get("endtarget"), buildFinished: { event -> new File("finished.run"). setText( "Build Finished: message='${event.message}' exception='${event.exception}'" ) project.executeTarget(listenerMap.theTarget) }, buildStarted : {}, messageLogged : {}, targetFinished : {}, targetStarted : {}, taskFinished : {}, taskStarted : {}, ] listener = listenerMap as BuildListener project.addBuildListener(listener )
Uses the technique outlined at:
Groovy Way to implement interfaces
Conclusion
Updates
Further Reading
- commons-proxy
- Overriding Java methods in Groovy for unit testing
- http://stackoverflow.com/questions/4244038/groovy-ant-task-using-different-classloader
- http://jira.codehaus.org/browse/GROOVY-3064
- http://stackoverflow.com/questions/472559/how-to-load-an-optional-task-into-ant-without-lib-or-global-installation
- http://stackoverflow.com/questions/3862525/how-to-create-a-proxy-of-an-interface-in-java
- How to create proxy for List interface ?
- Groovy Ant Task using different classloader
ant task setContextClassloader- AntBuilder: Groovy Meets Ant
- Practically Groovy: The @Delegate annotation
- Extending Ant
- Groovy
- Using Ant from Groovy
- Scriptdef
- PropertyFile Task
- Ant Groovy Task
- Ouroboros
- SED
Listing 4 – Full stacktrace
C:tempantgroovyListener>ant Buildfile: C:tempantgroovyListenerbuild.xml BUILD FAILED java.lang.ClassCircularityError: groovy/runtime/metaclass/java/io/FileMetaClass at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:169) at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127) at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122) at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165) at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:242) at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751) at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:71) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190) at MyListener.invoke(Script1.groovy:10) at JavassistUtilsGenerated_0.messageLogged(JavassistUtilsGenerated_0.java) at org.apache.tools.ant.Project.fireMessageLoggedEvent(Project.java:2254) at org.apache.tools.ant.Project.fireMessageLogged(Project.java:2290) at org.apache.tools.ant.Project.log(Project.java:470) at org.apache.tools.ant.Project.log(Project.java:459) at org.apache.tools.ant.AntClassLoader.log(AntClassLoader.java:396) at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1310) at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1064) at java.lang.ClassLoader.loadClass(ClassLoader.java:247) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:169) at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127) at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122) at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165) at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:242) at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751) at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:71) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190) at MyListener.invoke(Script1.groovy:10) at JavassistUtilsGenerated_0.taskFinished(JavassistUtilsGenerated_0.java) at org.apache.tools.ant.Project.fireTaskFinished(Project.java:2206) at org.apache.tools.ant.Task.perform(Task.java:364) at org.apache.tools.ant.Target.execute(Target.java:390) at org.apache.tools.ant.helper.ProjectHelper2.parse(ProjectHelper2.java:180) at org.apache.tools.ant.ProjectHelper.configureProject(ProjectHelper.java:82) at org.apache.tools.ant.Main.runBuild(Main.java:793) at org.apache.tools.ant.Main.startAnt(Main.java:217) at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280) at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109) Total time: 1 second java.lang.ClassCircularityError: groovy/runtime/metaclass/java/io/FileMetaClass at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:169) at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127) at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122) at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165) at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:242) at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751) at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:71) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190) at MyListener.invoke(Script1.groovy:10) at JavassistUtilsGenerated_0.messageLogged(JavassistUtilsGenerated_0.java) at org.apache.tools.ant.Project.fireMessageLoggedEvent(Project.java:2254) at org.apache.tools.ant.Project.fireMessageLogged(Project.java:2290) at org.apache.tools.ant.Project.log(Project.java:470) at org.apache.tools.ant.Project.log(Project.java:459) at org.apache.tools.ant.AntClassLoader.log(AntClassLoader.java:396) at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1310) at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1064) at java.lang.ClassLoader.loadClass(ClassLoader.java:247) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:169) at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127) at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122) at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165) at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:242) at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751) at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:71) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190) at MyListener.invoke(Script1.groovy:10) at JavassistUtilsGenerated_0.taskFinished(JavassistUtilsGenerated_0.java) at org.apache.tools.ant.Project.fireTaskFinished(Project.java:2206) at org.apache.tools.ant.Task.perform(Task.java:364) at org.apache.tools.ant.Target.execute(Target.java:390) at org.apache.tools.ant.helper.ProjectHelper2.parse(ProjectHelper2.java:180) at org.apache.tools.ant.ProjectHelper.configureProject(ProjectHelper.java:82) at org.apache.tools.ant.Main.runBuild(Main.java:793) at org.apache.tools.ant.Main.startAnt(Main.java:217) at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280) at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109) groovy/runtime/metaclass/java/io/FileMetaClass
“Timeless” solo 12 string guitar played by Ralph Towner on his “Solo Concert” CD. YouTube