Groovy script won't run due to NoClassDefFoundError - groovy

I wrote a very simple groovy script to test if a cron expression is valid:
import hudson.scheduler.CronTabList
try {
def cron = CronTabList.create("#daily")
println("Valid cron!")
} catch(Exception e) {
println("Invalid cron!")
e.printStackTrace()
}
Running this fails with the message:
Caught: java.lang.NoClassDefFoundError: javax/servlet/ServletContextListener
java.lang.NoClassDefFoundError: javax/servlet/ServletContextListener
at hudson.scheduler.BaseParser.<clinit>(BaseParser.java:149)
at hudson.scheduler.CronTab.set(CronTab.java:113)
at hudson.scheduler.CronTab.<init>(CronTab.java:100)
at hudson.scheduler.CronTabList.create(CronTabList.java:121)
at hudson.scheduler.CronTabList.create(CronTabList.java:96)
at hudson.scheduler.CronTabList$create.call(Unknown Source)
at validate_crontab.run(validate_crontab.groovy:7)
Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletContextListener
... 7 more
Process finished with exit code 1
My build.gradle dependencies look like:
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.11'
compile group: 'org.quartz-scheduler', name: 'quartz', version: '2.3.0'
compile group: 'org.jenkins-ci.main', name: 'jenkins-core', version: '2.85'
testCompile group: 'junit', name: 'junit', version: '4.12'
}
I simply can't figure out what's to blame and why I can't run the script.
Any help is much appreciated!

Apparently what you miss is the servlet API. For example:
dependencies {
compile group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0'
}
If you put that in your dependencies your script will most likely run.
But I guess that it is fairly important to understand why did you get this error. If you have a closer look at the jenkins-core library you will notice that it has a "provided" dependency to the servlet API. A provided dependency in simpler words means that the library (jenkins-core in your case) is compiled with the assumption that the servlet API jar will be present in the class path at your production environment - e.g. when using the lib in a web application running within a servlet container.
I guess that you run your groovy script as a standalone app, that is why you are getting an error. And... DISCLAIMER - I do not know if using jenkins-core in standalone apps is intended, though :-).

Related

Orchestrating Gradle build invocations with custom tasks

I’m trying to define two new Gradle tasks, buildAll and pubLocal, to run other tasks in a specific order.
When gradle buildAll is invoked, I want Gradle to do the same thing as if I had executed gradle clean build writePom (see below for writePom).
When gradle pubLocal is executed, I want Gradle to do the same thing as if gradle buildAll install had been executed.
Here’s my best attempt thus far:
// build.gradle
task writePom << {
pom {
project {
groupId 'mygroup'
artifactId 'mylib'
version version
inceptionYear '2015'
licenses {
license {
name 'Blah'
url 'blah'
distribution 'blah'
}
}
}
}.writeTo("build/libs/pom.xml")
}
task buildAll(dependsOn: clean, build, writePom)
task pubLocal(dependsOn: buildAll, install)
When I run gradle buildAll on this, I get:
myuser#mymachine:~/tmp/myapp$./gradlew buildAll
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/myuser/tmp/myapp/build.gradle' line: 67
* What went wrong:
A problem occurred evaluating root project 'myapp'.
> Could not find method buildAll() for arguments [{dependsOn=task ':clean'}, task ':build', task ':writePom'] on root project 'myapp'.
Any ideas as to where I’m going awry?
This may be a left-over from copy-pasting, but your strings are not quoted consistently using standard single- or double-quotes. Example:
}.writeTo(“build/libs/pom.xml")
does not quote the string properly, as it opens with the “ character instead of ". Same with the single-quotes above it.
You can see from the way your code is highlighted, that everything in red is interpreted as a string. If this is the case in your actual code, the buildAll and pubLocal tasks will not be recognized, as they are part of a string rather than code.
UPDATE:
Since the above answer is irrelevant now, here is another possibility. The error message shows that only the "clean" task is listed in the dependsOn parameter. The buildAll task dependencies should be declared like this:
task buildAll(dependsOn: [clean, build, writePom])
Similar with the pubLocal task.
I'm using Gradle 2.4. The following file includes the Maven plugin, uses a list [] in the dependsOn, and ensures that clean must be executed before build:
apply plugin: 'maven'
task writePom << {
pom {
project {
groupId 'mygroup'
artifactId 'mylib'
version version
inceptionYear '2015'
licenses {
license {
name 'Blah'
url 'blah'
distribution 'blah'
}
}
}
}.writeTo("build/libs/pom.xml")
println "TRACER writePom"
}
task clean << { println "TRACER clean" }
task build << { println "TRACER build" }
build.mustRunAfter clean
task install << { println "TRACER install" }
task buildAll(dependsOn: [clean, build, writePom])
task pubLocal(dependsOn: [buildAll, install])
I get this output (minus Gradle 3 warnings):
bash-3.2$ gradle buildAll
:clean
TRACER clean
:build
TRACER build
:writePom
TRACER writePom
:buildAll
BUILD SUCCESSFUL
and this:
bash-3.2$ gradle pubLocal
:clean
TRACER clean
:build
TRACER build
:writePom
TRACER writePom
:buildAll
:install
TRACER install
:pubLocal
BUILD SUCCESSFUL

gradle configurations for tasks

I have gradle build script with plugins: groovy, jacoco and maven.
In dependencies beside jars is:
testCompile group: 'org.spockframework', name: 'spock-core', version: '0.7-groovy-2.0'
Now when I created task integTest(Type: Test) and :
configurations{
integTestCompile {
extendsFrom testCompile
}
integTestRuntime {
extendsFrom integTestCompile
}
}
everything works OK, but I wanted to add some modularization and now all integTest tasks are created that way:
task "${module}_${suite}" (type: Test)
and when I tried implement same changes in configurations I got an error:
Could not find method ModuleName_Suite1Runtime() for arguments [build_55s7pmm2c6k8n8e2n1i59t3b5b$_run_closure5_closure28_closure29_closure31#3394214b] on root project 'projectName'.
for
configurations {
"${module}_${suite}Compile"{
extendsFrom testCompile
}
"${module}_${suite}Runtime"{
extendsFrom "${module}_${suite}Compile"
}
}
and another error with different configuration:
No signature of method: java.lang.String.extendsFrom() is applicable for argument types: (org.gradle.api.internal.artifacts.configurations.DefaultConfiguration_Decorated) values: [configuration ':testCompile']
for
configurations{
"${module}_${suite}Compile".extendsFrom(testCompile)
"${module}_${suite}Runtime".extendsFrom("${module}_${suite}Compile")
}
Without "taskName+Compile" and "taskName+Runtime" I got ClassNotFound Exception for spock specification. So I'm sure I need this like in previous version.
I'm pretty sure it's something straightforward, but I can't find any tip in google.
Any help would be appreciated.
The syntax being used inside the configurations {...} block is implemented via some Groovy magic that unfortunately is not called when you use a String literal. Instead, if you want to create a configuration with a dynamic name you'll want to call the create() method on the ConfigurationsContainer.
configurations.create("${module}_${suite}Compile") {
extendsFrom configurations.testCompile
}

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.

Programmatically download dependencies in custom gradle task

I want to create task which depends on few maven libraries. Is it possible to download those libraries from groovy code? What I want to do is to put this code
configurations {
sshexecAntTask
}
repositories {
mavenCentral()
}
dependencies {
sshexecAntTask 'org.apache.ant:ant-jsch:1.7.0'
}
Which I use in this way:
ant.taskdef(name: 'sshexec', classname: 'org.apache.tools.ant.taskdefs.optional.ssh.SSHExec', classpath: project.configurations.sshexecAntTask.asPath)
ant.sshexec(host: host, username: username, password: password, command: command, trust: 'true', failonerror: 'true')
Into my DefaultTask class. Is it possible?
It should look similar to this:
class MyCustomTask extends DefaultTask {
public MyCustomTask() {
super()
// set and download dependencies here
}
}
[EDIT]
I've found that I can do it in this way:
project.getRepositories().mavenLocal()
project.getConfigurations().create('sshexecAntTask')
project.getDependencies().add('sshexecAntTask', 'org.apache.ant:ant-jsch:1.7.0')
project.getConfigurations().getByName('sshexecAntTask').resolve()
println('project.configurations.sshexecAntTask.asPath: '+project.getConfigurations().getByName('sshexecAntTask').getAsPath());
But it still doesn't work.
You would typically do something along the lines of:
MyCustomTask lives in its own build (possibly buildSrc).
The MyCustomTask project declares ant-jsch as a compile dependency.
The task's action (not constructor) defines (taskdef) and executes the Ant task. (Might have to be wrapped with project.ant.execute { ... }.)
Builds that wish to use MyCustomTask declare a build script dependency on its module (not necessary in case of buildSrc). Transitive dependency management automatically brings in ant-jsch along with it.
The customPlugin sample in the full Gradle distribution is a good place to get started. (Just declare a compile instead of a testCompile dependency.)

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