Update Apr 7, 2012: Hmm. Seems this is a very popular post. I intend to expand it and cover how to approach the details more clearly.
I like groovy.
Problem is, we don't have it installed on our live servers.
So I have to compile the groovy code into java class files and deploy them.
But when you're using the @Grab annotation, things go bad.
I kinda like using @Grab to snarf the specific dependencies, but you always run into the dreaded:
Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
Now all that @Grab does is download the jars you needed and pops them into ~/.groovy/grapes.
Once it's done it's job you don't need it for either running groovy scripts or for the java class.
Here's a rather artificial contrived explanation of how to get around the ClassLoader issue.
(Of course you could just use grape directly, or specify your own .m2 jars or whatever, but I noted this question open in a few places, so I contrived this example)
First create your script, say, x.groovy...
@Grab(group='com.gmongo', module='gmongo', version='0.8') import com.gmongo.GMongo // Instantiate a com.gmongo.GMongo object instead of com.mongodb.Mongo def mongo = new GMongo("the.mongo.ip.address", 27017) def db = mongo.getDB("some_database") println db.my_collection.find([SomeKey:[$ne:"0"],SomeOtherKey:"0"]).count()
Now run it:
groovy x.groovy 149845
Cool.
Now we use groovyc to compile it...
groovyc -d classes x.groovy
But when we try to run it using java...
# Building CLASSPATH here purely for clarity... # We compiled the groovy class into classes: CLASSPATH="classes" # We need the gmongo jar: CLASSPATH="$CLASSPATH:~/.groovy/grapes/com.gmongo/gmongo/jars/gmongo-0.8.jar" # And because tis is just an example, I knew I needed this one as well: CLASSPATH="$CLASSPATH:~/.groovy/grapes/org.mongodb/mongo-java-driver/jars/mongo-java-driver-2.5.2.jar" # And I dev on a Mac and use MacPorts, so groovy is installed in /opt/local/share: CLASSPATH="$CLASSPATH:/opt/local/share/java/groovy/lib/*" # Now run it: java -cp $CLASSPATH x Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ...elided for brevity... at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:165) at x.<clinit>(x.groovy) </clinit>
Rats. So we need to remove the @Grab now that it's done it's work and downloaded the jars...
//@Grab(group='com.gmongo', module='gmongo', version='0.8') import com.gmongo.GMongo // Instantiate a com.gmongo.GMongo object instead of com.mongodb.Mongo def mongo = new GMongo("192.168.4.112", 27017) def db = mongo.getDB("some_database") println db.my_collection.find([SomeKey:[$ne:"0"],SomeOtherKey:"0"]).count()
And re-compile using the specific gmongo jar:
groovyc -cp ~/.groovy/grapes/com.gmongo/gmongo/jars/gmongo-0.8.jar -d classes x.groovy
And now we can run it as before
# Building CLASSPATH here purely for clarity... ...elided... java -cp $CLASSPATH x 149845
Hope that explains it for those stuck with that issue.
Oh, and if you need to see the classpath from within a script go here: http://blog.blindgaenger.net/print_groovys_classpath_for_debugging.html
Waaaay cool.
Thanks for posting this explanation. It's summer 2013 now and no easier solution in sight ?
ReplyDeleteDear lord it's like 2018 and i still have to deal with stuff like this!
ReplyDeleteThanks for the workaround Kim!