Groovy with Grape and AntBuilder classloader problem - groovy

I wanted to use groovy for a little ftp script and found this post http://www.hhhhq.org/blog/2009/05/01/ftp-using-groovy-and-ant/
Since there were several dependencies I wanted to use Grape. All dependencies are resolved and present in the cache. But I can't get Ant to find the optional tasks in the other libs.
It always says
Caught: : Problem: failed to create task or type ftp
Cause: the class org.apache.tools.ant.taskdefs.optional.net.FTP was not found.
This looks like one of Ant's optional components.
Action: Check that the appropriate optional JAR exists in
-ANT_HOME\lib
-the IDE Ant configuration dialogs
Do not panic, this is a common problem.
The commonest cause is a missing JAR.
This is not a bug; it is a configuration problem
at GrabTest.runMe(GrabTest.groovy:15)
at GrabTest.run(GrabTest.groovy:26)
Groovy Version: 1.6.5 JVM: 1.6.0_15
Here is my source code
#Grab(group='ant', module='ant', version='[1.6.5,)')
#Grab(group='ant', module='ant-nodeps', version='[1.0,)')
#Grab(group='ant', module='ant-apache-oro', version='[1.0,)')
#Grab(group='ant', module='ant-commons-net', version='[1.0,)')
#Grab(group='apache-oro', module='jakarta-oro', version='[2.0.8,)')
#Grab(group='commons-net', module='commons-net', version='[1.4,)')
def runMe() {
// works
println getClass().getClassLoader().loadClass("org.apache.tools.ant.taskdefs.optional.net.FTP")
def ant = new AntBuilder()
println getClass().getClassLoader() //groovy.lang.GroovyClassLoader$InnerLoader
println ant.getClass().getClassLoader() //org.codehaus.groovy.tools.RootLoader
ant.ftp( server:"ftp.foo.com",
userid:"user",
password:"passwd",
passive:"yes",
verbose:"yes",
remotedir:"/pub/incoming",
binary:"yes" ) {
fileset( dir:"." ) { include( name:"**/*.gz" ) }
}
}
runMe()
As you can see I suspect the classloader of being the problem, it seems that
Grape doesn't inject the dependencies there.
Any idea of how I can get it to work?

You're right suspecting the classloader to be the root of the problem. As your code already reveals, the AntBuilder is loaded from the RootLoader, that doesn't have access to the classes loaded by the #Grab annotation. As GROOVY-3730 shows, Groovy 1.7 is going to address this problem.
However, you can solve your problem by directly using the groovy.grape.Grape.grab(Map dependency) method, in which you can set a specific classloader that should be used to load the dependencies:
import groovy.grape.Grape;
Grape.grab(group:'ant', module:'ant', version:'1.6.5', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'ant', module:'ant-nodeps', version:'1.6.5', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'ant', module:'ant-apache-oro', version:'1.6.5', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'ant', module:'ant-commons-net', version:'1.6.5', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'commons-net', module:'commons-net', version:'1.4.1', classLoader:this.class.classLoader.rootLoader)
Grape.grab(group:'oro', module:'oro', version:'2.0.8', classLoader:this.class.classLoader.rootLoader)

Or just simply use
#GrabConfig(systemClassLoader=true)
Further info can be found: http://groovy.codehaus.org/Grape

Related

can we import the specified classes jar file from another mincroanut project to an micronaut project

I have 2 micronaut (groovy ) projects , called project A and project B
Project B has controllers and services ( package com.service , com.controller )
but I only created jar from package com.service
the code in com.service package has #Singleton annotation and #Scheduled
and I has enabled annotation processing as link (https://docs.micronaut.io/latest/guide/index.html#ideaSetup) to both projects
please see my gradle code below to generate JAR file ( the output file is project-b-libs-0.x.jar)
task createLibraryJar(type: Jar) {
baseName( getArchivesBaseName() + "-libs")
from sourceSets.main.output
includeEmptyDirs = false
include '**/service/**/*.class'
}
Then I added proejct-b-libs-0.x.jar to Project A
The gradle's dependencies are below
dependencies {
annotationProcessor "io.micronaut:micronaut-inject-java"
implementation("io.micronaut:micronaut-validation")
implementation("io.micronaut.groovy:micronaut-runtime-groovy")
implementation("javax.annotation:javax.annotation-api")
implementation("io.micronaut:micronaut-http-client")
runtimeOnly("ch.qos.logback:logback-classic")
compileOnly files('libs/project-b-libs-0.4.jar')
compile "io.micronaut:micronaut-inject"
}
Finally after I tried call #Inject Object from the class in JAR file, it showed error on run time
Caused by: io.micronaut.context.exceptions.BeanContextException: Error
loading bean [com.service.TestService]:
com/service/StripePaymentService
Project A has com.service.TestService to call com.service.StripePaymentService which is in JAR file
Sorry for my English and Thank you to trying to understand me
compile files('libs/project-b-libs-0.4.jar')
I just use compile , not compileOnly , and can not use jar file from gralde script above when I extract jar file , some mincronaut's stuff is missing
so I try another gradle task's script as below ( change from include to exclude )
task createLibraryJar(type: Jar) {
baseName( getArchivesBaseName() + "-libs")
from sourceSets.main.output
includeEmptyDirs = false
exclude '**/controller/**/*.class'
}
and it works because the micronaut's stuff still in JAR file, I only exclude unnecessary classes from my jar file

How add dependencies into groovy script?

I try to add HttpBuilder into groovy script, but can do it only manually (Alt+Ctrl+Shift+S add dependencie). But when I start script I have error in line of creating new httpbuilder instance java.lang.ClassNotFoundException: org.apache.http.client.HttpClient. I manualy add HttpClient, butClassNotFoundException: net.sf.json.JSONObject and so on. But when I add Ini library it works fine.
I also tried to use #Grab
main()
def main() {
#Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7' )
def http = new groovyx.net.http.HTTPBuilder('http://www.codehaus.org')
And have compilation error
Error:Groovyc: While compiling GroovyTests: java.lang.RuntimeException: Error grabbing Grapes -- [download failed: net.sf.json-lib#json-lib;2.3!json-lib.jar]
And net in def http = new groovyx.net.http.HTTPBuilder('http://www.codehaus.org') is red and Cannot resolve a symbol 'net' error
will be glad to any help
Since you have now installed the groovy executables as per the comments, the following code:
#Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.7')
import groovyx.net.http.HTTPBuilder
def http = new HTTPBuilder('https://jsonplaceholder.typicode.com')
def res = http.get(path: '/users')
println "Number of users: ${res.size()}"
should now run and print:
─➤ groovy solution.groovy
Number of users: 10
─➤
(tested on Groovy Version: 2.5.8 JVM: 1.8.0_232 Vendor: AdoptOpenJDK OS: Linux)
One thing that might be disrupting the artifact resolution is if you have a custom grapeConfig.xml file. This file (if it exists) should be under <user home dir>/.groovy/grapeConfig.xml and the default text that groovy uses if no grapeConfig.xml file is present can be found here:
https://github.com/apache/groovy/blob/master/src/resources/groovy/grape/defaultGrapeConfig.xml
In addition, if you need to debug grapes downloads, you should try the following flags (again as mentioned in the comments):
─➤ groovy -Dgroovy.grape.report.downloads=true -Divy.message.logger.level=4 yourGroovyScript.groovy
which should print information on what grapes are actually doing when the resolution fails.
What does your groovy -v look like? i.e. what version of groovy and what jdk are you on?

Groovy - How to Build a Jar

I've written a Groovy script which has a dependency on a SQL Server driver (sqljdbc4.jar). I can use the GroovyWrapper (link below) to compile it into a JAR, however how can I get dependencies into the Jar? I'm looking for a "best practice" sort of thing.
https://github.com/sdanzan/groovy-wrapper
Both of the replies below have been helpful, but how can I do this for signed Jar files? For instance:
Exception in thread "main" java.lang.SecurityException: Invalid signature file d
igest for Manifest main attributes
In the groovy wrapper script, you'll see this line near the bottom:
// add more jars here
That's where you can add your dependencies. If the jar file is in the same directory you're building from, add a line like this:
zipgroupfileset( dir: '.', includes: 'sqljdbc4.jar' )
Then rerun the script and your jar will include the classes from sqljdbc4.jar.
Edit:
If the jar file you depend on is signed and you need to maintain the signature, you'll have to keep the external jar. You can't include jar files inside of other jar files without using a custom classloader. You can, however, specify the dependency in the manifest to avoid having to set the classpath, i.e. your jar still executable with java -jar myjar.jar. Update the manifest section in the wrapping script to:
manifest {
attribute( name: 'Main-Class', value: mainClass )
attribute( name: 'Class-Path', value: 'sqljdbc4.jar' )
}
From your link, if you look at the source of the GroovyWrapper script, there's this line:
zipgroupfileset( dir: GROOVY_HOME, includes: 'embeddable/groovy-all-*.jar' )
zipgroupfileset( dir: GROOVY_HOME, includes: 'lib/commons*.jar' )
// add more jars here
I'd explicitly add it there.

What should CLASSPATH be set to in order to use NekoHTML in Groovy?

I have found several snippets that use NekoHTML library in Groovy scripts, e.g.
def page = new XmlSlurper(new org.cyberneko.html.parsers.SAXParser()).parse('http://groovy.codehaus.org/')
def data = page.depthFirst().grep{ it.name() == 'A' && it.#href.toString().endsWith('.html') }.'#href'
data.each { println it }
So I downloaded Groovy, and then I downloaded NekoHTML.
NekoHTML is located here:
D:\TOOLS\nekohtml-1.9.18\
When I run the snippet, I get:
D:\SCRIPTS\webtesting.groovy: 4: unable to resolve class org.cyberneko.html.parsers.SAXParser
# line 4, column 27.
def page = new XmlSlurper(new org.cyberneko.html.parsers.SAXParser()).parse('http://groovy.codehaus.or
g/')
My research tells me that I need to include NekoHTML in the classpath.
So I set up an environment variable CLASSPATH and it equals:
CLASSPATH=D:\TOOLS\nekohtml-1.9.18\src
I opened another cmd window and ran my test groovy script. I get the same error as above.
What should my CLASSPATH be set to refer to NekoHTML components with org.cyberneko.html.parsers.SAXParser?
You will need nekohtml-1.9.18.jar and xercesImpl-2.10.0.jar on your classpath.
Or you could just add a #Grab to the top of your script
#Grab( 'net.sourceforge.nekohtml:nekohtml:1.9.18' )
And groovy webtesting.groovy would fetch these dependencies for you. Of course, it depends how the script is being run

Running Groovy script from Gradle using GroovyShell: Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/cli/ParseException

I want to run a groovy command-line script from my Gradle build script.
I'm using this code in my Gradle script:
def groovyShell = new GroovyShell();
groovyShell.run(file('script.groovy'), ['arg1', 'arg2'] as String[])
Things work fine until my Groovy script (script.groovy) uses the CliBuilder class. Then I get the following exception:
org.codehaus.groovy.runtime.InvokerInvocationException: java.lang.NoClassDefFoundError: org/apache/commons/cli/ParseException
...
Caused by: java.lang.ClassNotFoundException: org.apache.commons.cli.ParseException
I found lots of people with similar problems and errors, but "the solution" was difficult to extract from the numerous posts I read. Lots of people suggested putting the commons-cli jar on the classpath, but doing so for the GroovyShell was not at all apparent to me. Also, I had already declared #Grapes and #Grab for my required libraries in the script.groovy, so it should have everything it needed.
Thanks to this unaccepted SO answer, I finally found what I needed to do:
//define our own configuration
configurations{
addToClassLoader
}
//List the dependencies that our shell scripts will require in their classLoader:
dependencies {
addToClassLoader group: 'commons-cli', name: 'commons-cli', version: '1.2'
}
//Now add those dependencies to the root classLoader:
URLClassLoader loader = GroovyObject.class.classLoader
configurations.addToClassLoader.each {File file ->
loader.addURL(file.toURL())
}
//And now no more exception when I run this:
def groovyShell = new GroovyShell();
groovyShell.run(file('script.groovy'), ['arg1', 'arg2'] as String[])
You can find more details about classLoaders and why this solution works in this forum post.
Happy scripting!
(Before you downvote me for answering my own question, read this)
The alternative to do this is the following:
buildScript {
repositories { mavenCentral() }
dependencies {
classpath "commons-cli:commons-cli:1.2"
}
}
def groovyShell = new GroovyShell()
....
This puts the commons-cli dependency on the classpath of the buildscript instead of on the classpath of the project to be built.

Resources