8

1. Generally, how is @Grape/@Grab inclusion different than classpath inclusion?

2. Specifically, what might cause the following behavior difference?

I've got a requirement on xpp3 which I express as:

// TestScript.groovy
@Grab(group='xpp3', module='xpp3', version='1.1.3.4.O')
import org.xmlpull.v1.XmlPullParserFactory;
println "Created: " + XmlPullParserFactory.newInstance()

Running $ groovy TestScript.groovy fails with

Caught: org.xmlpull.v1.XmlPullParserException: caused by: org.xmlpull.v1.XmlPullParserException:

If, however, I manually add the .jar fetched by Grape to my Groovy classpath:

$ groovy -cp ~/.groovy/grapes/xpp3/xpp3/jars/xpp3-1.1.3.4.O.jar \
         TestScript.groovy 

... then everything works.

M. Justin
  • 14,487
  • 7
  • 91
  • 130
Bosh
  • 8,138
  • 11
  • 51
  • 77

1 Answers1

13

Grab uses ivy to fetch the specified library (plus all of its dependencies) from the maven core repository. It then adds these downloaded libraries to the classpath of the loader that's running the current script.

Adding the jar to the classpath just adds the specified jar to the system classpath.

As there are no dependencies in this example, it's probably a requirement that the library needs to be loaded by the system classloader.

To check this, try adding

@GrabConfig(systemClassLoader= true)
@Grab(group='xpp3', module='xpp3', version='1.1.3.4.O')

Instead of the one line Grab you currently have

Inv3r53
  • 2,929
  • 3
  • 25
  • 37
tim_yates
  • 167,322
  • 27
  • 342
  • 338
  • 1
    That fixes my examples (so thanks!) But in the real world, xpp3 is a transitive dependency for me. If I add `systemClassLoader= true` to the `Grab` that transitively gets xpp3, I get a linkage error (`loader constraint violation: when resolving overridden method "org.apache.xerces.jaxp.SAXParserImpl.getXMLReader()Lorg/xml/sax/XMLReader;`). Ideas on this one? – Bosh Nov 15 '13 at 21:16
  • 1
    Grab only goes so far, I'd move to a build tool like gradle – tim_yates Nov 15 '13 at 21:22
  • Hmm, OK. I just want to run a groovy script that requires one mvn-accessible jar and its transitive dependencies. (This works fine in grails-land...) You'd recommend gradle for this? – Bosh Nov 15 '13 at 21:26
  • No if it's just one script (there's no mention of what you are doing or what library you're trying to load in the question). If Grab isn't working, you'll need to add them to the classpath yourself as you show in the question. – tim_yates Nov 16 '13 at 16:05
  • Thanks for the advice! It's just one script, but I'd like to distribute it in a way that users can execute that script themselves, without having to **manually** download a bunch of jars first. `@Grab` would have been great. As is, I'm using a Gradle-based solution similar to http://stackoverflow.com/questions/17360719/running-groovy-scripts-from-gradle -- which works okay but I'd be very interested to know if you think there' a better way! – Bosh Nov 17 '13 at 22:11
  • Can you give a small failing example script in the question? – tim_yates Nov 17 '13 at 23:39
  • Aha -- looks like I just had two conflicting transitive dependencies. `systemClassLoader=true` was the real trick (and then, excluding one of the dependencies). Thanks again! – Bosh Nov 18 '13 at 03:10