#Grab annotation fails under Ant - ant

I'm using a #Grab annotation to grab the definition of an Html parser I can give to the XMLSlurper (I think it's the tagsoup parser) and all is good when I run my script from the cmd line. If I invoke the same script from Ant I get an Ivy NoClassDefFound error. I think it may berelated to having Ivy in Antlib. Is there another way to parse Html without customizing the slurper via #Grab?
This:
#Grab(group='org.ccil.cowan.tagsoup', module='tagsoup', version='1.2' )
doc = new XmlSlurper(new org.ccil.cowan.tagsoup.Parser()).parse(confluenceWebPageInputStream)
Works just fine from the command line but when I run it from an Ant build target:
<target name="update-wiki-chart">
<echo message="Will update chart for version ${version}"/>
<java dir="${basedir}" classname="groovy.lang.GroovyShell">
<arg value="ParseWikiPage.groovy"/>
<classpath refid="groovylib"/>
</java>
</target>
where groovyLib is a path ref pointing to the Groovy-1.8.6 jar downloaded from our internal Nexus repo, I get the NoClassDefFound error. I'm thinking this is probably due to having Ivy installed in Antlib causing the class loader to find it in two places. I just thought of something while writing this post. I can probably run java in forked mode or do something to cause it to not see/share Ant's classpath.It's been a few years since I've wrestled w/ Ant and class loader issues. My project is a little delinquent due to the bug and I'm looking for a quick/easy fix.
I just tried running my groovy on the cmd line via the "java" cmd and loading groovy-all jar in the class path and I realized that I get the NoClassDefFound error there as well. It has nothing to do with collisions with Ivy under Antlib. Rather, I am missing Ivy altogether. I had assumed it was included in Groovy-all.jar. I just need a clever way of passing Ivy from AntLib into my java task to get this all up and running.

Sounds like you're missing one or more jars from the classpath. I'd suggest digging around the classpathref you've labelled "groovylib".
A less error prone way to launch groovy from within ANT using the groovy ANT task.
Here's a similar example to your use-case:
Parse HTML using with an Ant Script
My example uses ivy directly to manage all build dependencies. The Grab annotations are still supported but obviously these would only manage the dependencies of the groovy script.

Related

In Ivy what does impossible to publish artifacts FileNotFoundException Error Mean?

I'm working within a large build system that uses Ant/Ivy. I try to use a predefined ant task that uses ivy:publish ant task and get this error:
impossible to publish artifacts for com.company.project1.proj1#MySupportJar;working#server1: java.io.FileNotFoundException: /path_to/ivy-repository/com.company.project1.proj1/MySupportJar/5.1.3.part/MySupportJar-5.1.3.jar (No such file or directory)
The directory in the error message exists up to the version number part (5.1.3.part).
I am new to Ivy but think I get the basics of how it works. I can not find much on the exact meaning of this error so if someone could help or point me to an explanation I think I could resolve the issue from there.
Ant target
<target name="publish-shared" depends="ivyInit, resolve"
description="Publish to the shared repository">
<ivy:publish pubrevision="5.1.3"
resolver="shared"
pubdate="${timestamp}"
forcedeliver="true"
update="true"
conf="distro, docs">
<artifacts pattern="dist/[artifact].[ext]"/>
</ivy:publish>
</target>
Ivy file snippet
<publications>
<artifact name="MySupportJar" type="jar" conf="distro" />
<artifact name="MySupportJar-source" type="source" ext="jar" conf="docs" />
</publications>
Thanks.
Thanks for all the suggestions. Turns out to be a simple solution that I was not looking for.
The problem was permissions at /path_to/ivy-repository/com.company.project1. I did not have write permission. The .part file is a temporary file written by Ivy. Ivy could not write the temporary file so when it got to reading the file it failed to find it.
I'm answering this so that it might help someone later.
Thanks.
I'm fairly new to Ant with Ivy too. What I've done is combine a local Maven repository (Artifactory), with Jenkins as a continuous integration server. When we build a jar, I also produce the Maven pom.xml with it. Then, I use the mvn deploy:file command to deploy the desired build to our Maven repository.
The developer manually deploys the jar to our Maven repository via the Promoted Build plugin to Jenkins. The developer selects the build to deploy to Maven, and then pretty much presses a button, and that build will be deployed.
I actually produce two pomswith each build. One ispom.xmland the other ispom-snapshot.xml`. We deploy the snapshot with each build, so other developers can use the latest jar instead of the officially deployed one. I've put the whole thing in github if you're interested.
The only decent Ant/Ivy documentation I've seen is Manning's Ant in Action by Steve Loughram. If it wasn't for that, I probably would have never even tried Ant with Ivy. The online Ant/Ivy documentation at Apache is just plain awful.
I've looked over what you have. I suspect it might be an issue with your ivy-settings.xml file. Somewhere, it's getting the part string as the valid location for publishing the file. Otherwise, I have no idea.
As I said, we use a Maven repository for our site repository, and then use Maven to actually deploy the jars to the repository. I simply found that much easier to do it that way than to figure out how to do this in pure Ivy. Besides, it also means that our Maven projects can also use the jars from our Ant/Ivy projects.

Worklight ant task using apache commons - where is this loaded from?

I'm trying to diagnose a failure in my ant file when it runs a Worklight task. I have a small ant file containing a simple reference to a Worklight task. This works correctly.
<target name="rawBuildApplication" >
<app-builder
applicationFolder="${applicationSource}"
environments="mobilewebapp"
nativeProjectPrefix="xxx"
outputFolder="${outputFolder}"/>
</target>
However when I invoke this ant file from a build control ant file (actually from the RTC build system) I get a failure (below), showing worklight failing to find an apache Java class. I assume there's some simple environmental difference, perhaps a classpath. It might help to figure it out if I knew where Worklight loaded the apache commons from. Right now I can't see anything in my environment in the case that works that points any apache Jar.
myAntFile.xml:146: java.lang.NoSuchMethodError: org.apache.commons.io.FileUtils.deleteQuietly(Ljava/io/File;)Z
at com.worklight.builder.util.BuilderUtils.<clinit>(BuilderUtils.java:672)
at com.worklight.builder.config.UserBuildConfiguration$Builder.<init>(UserBuildConfiguration.java:203)
at com.worklight.ant.builders.ApplicationBuilderTask.createBuildConfiguration(ApplicationBuilderTask.java:149)
at com.worklight.ant.builders.ApplicationBuilderTask.execute(ApplicationBuilderTask.java:80)
Edited: the cause is the use of -lib to add the RTC toolkit directory, exactly why this clashes and how to work around yet to be determined
Usually means you have version of the commons jar in your classpath, and its overriding the one packaged in the worklight-ant.jar. the apache commons files are inside the worklight-ant.jar file
Additional info from djna: I can confirm that when adding the Rational Team Concert (RTC) 3.0 toolkit to the ant classpath, either explicitly with -lib, or when selecting that option in the RTC Build definition some conflicting commons jars are added to the classpath. Worklight packages the classes it needs in its jar, but the -lib folder seems to take precedence.
My workaround is to replace the conflicting jars with later ones. I used these jars
commons-io-2.4.jar
commons-codec-1.8.jar
httpclient-4.2.5.jar
httpcore-4.2.4.jar
httpmime-4.2.5.jar
I guess the other alternative is to upgrade to a newer RTC, but in our environment that's not currently possible.

Runnable jar file without ant java task - how is this possible?

I've inherited some code from a previous developer, which gets built using Ant into an executable jar file that runs by double clicking. The application runs, but under some conditions crashes with an OutOfMemoryError. To investigated this, I'd like to add the -XX:+HeapDumpOnOutOfMemory jvm arg to the Ant buildfile, and as I understand it, the <jvmarg value="-XX:+HeapDumpOnOutOfMemory" /> element needs to go under a <java ...> task. However, there is no <java ...> task to be found in this or any other Ant buildfiles in this code base.
How is this even possible? How can the jar file be executable without a <java ...> task?
I'm asking primarily to find out what in fact makes my jar file executable so that I can figure out where to put that <jvmarg /> element to debug the OOME.
Thanks!
A <java> task doesn't create an executable jar file. It executes a Java program.
I don't think it's possible to specify VM parameters when starting an executable jar file by double-clicking on it. If you want to pass VM parameters, open a command prompt and execute the jar this way:
java -XX:+HeapDumpOnOutOfMemory -jar nameOfTheJar.jar
Your jar is executable because it has a Main-Class defined in the META-INF/MANIFEST.MF file. Double clicking it to run doesn't do anything with Ant. Ant is simply used to package the jar.
In order to add the parameter and still launch via a double click you could create a shortcut that runs the command in JB Nizet's answer

ANT is failing to find org.dbunit.ant.DbUnitTask

So I'm writing a ant build file to run some tests, and I use the following line without any issue for MXUnit:
<taskdef name="mxunittask" classname="org.mxunit.ant.MXUnitAntTask" classpath="../mxunit/ant/lib/mxunit-ant.jar"/>
But when I download dbunit-2.4.8.jar from http://sourceforge.net/projects/dbunit/files/ and put it in the same directory as mxunit-ant.jar, then added the following line to the ANT build file:
<taskdef name="dbunit" classname="org.dbunit.ant.DbUnitTask" classpath="../mxunit/ant/lib/dbunit-2.4.8.jar"/>
For which I see a warning in Eclipse that says:
taskdef A class needed by class
org.dbunit.ant.DbUnitTask cannot be
found: org/slf4j/LoggerFactory
When I extract the jar file it does have the org.dbunit.ant.DbUnitTask class.. so I'm very confused as to what ANT is complaining about.. any idea what the issue is?
It looks like you need to make sure all the dependencies of DbUnit are satisfied, as well as installing the dbunit-2.4.8.jar.
The specific error you quote with class org/slf4j/LoggerFactory not being found suggests that you don't have the SLF4J classes needed. (Probably the latest version of slf4j-api.jar is needed there.)

How to load an optional task into ant without -lib or global installation?

I want to use the FTP task in ant, and I have found the appropriate jar files and got everything working fine. I have put the jar files in a "libs" directory alongside the other files used in the build. The only problem is that the user must run "ant -lib commons-net-ftp-2.0.jar" to make a build; I would really prefer that it were possible to just run "ant" with no arguments.
Reading the ant optional tasks intallation page, I see that there are five ways one can load up extra libraries in ant, and none of them are really what I'm looking for. I do not want to force the user to make any modifications to their system to run this task; it should be possible to just load it from the "libs" directory inside of our product's source folder. So that means setting the global CLASSPATH is also out (which is a bad idea anyways).
The last option, as noted in the documentation, is the preferred approach... loading the jarfiles individually from the build script itself. I have done this in the past with the ant-contrib tasks and JUnit, and would like to do that here, but I don't see how I can accomplish this. The FTP task doesn't support a nested classpath element, and I don't know the XML resource I would need to load this library via a taskdef. How can I load the libraries from within ant?
Edit: In response to the answers and questions which have been posted here so far, I'm using ant 1.7.1. Making an ftp taskdef definitely does not work; that throws the following error:
BUILD FAILED
/my/path/build.xml:13: taskdef class org.apache.tools.ant.taskdefs.optional.net.FTP cannot be found
Perhaps this is because the classname is wrong. How exactly do I find the classname I'm supposed to use if I only have a jarfile? It's not documented anywhere, and I couldn't find anything in the jar itself resembling that path.
The problem you are having is due to the different class-loaders in use. The Commons Net classes must be loaded by the same class-loader that loads the FTP task. Because the FTP task is loaded by Ant on start-up, you need to add the Commons Net to Ant's classpath so that it is loaded by the same class-loader. That's why the documentation gives you 4 different ways to do this.
I agree that none of them are ideal (the CLASSPATH environment variable being the worst). One way around this is to supply a shell script with your project that invokes Ant and passes the apporpriate -lib argument. You then get people to use this rather than invoking Ant directly. In fact, you could deviously name it 'ant' so that it gets run instead of the existing 'ant' on the path (this only works if the current directory is on the path, ahead of other directories).
The fifth option in the documentation is great in theory. They finally fixed the class-loading problems in 1.7.0. Unfortunately, as you mention, nobody retro-fitted the FTP task to take a classpath. You could try submitting an enhancement request, but this won't help in the short term.
There is one other option, which isn't any better than the others. Instead of making sure that the Commons Net classes are loaded by the class-loader that loads the FTP task, you could make sure that the FTP task is loaded by the class-loader that loads the Commons Net classes. To do this you have to remove the ant-commons-lib.jar file from the 'lib' directory of the Ant installation. This means that the FTP task won't get loaded on start-up. This is actually why the optional tasks are broken up into so many separate JARs - so that they can be individually removed. Put this JAR file alongside the Commons Net JAR file so that it can be loaded at the same time. Then you can do something like this (I tried this and it works):
<taskdef name="ftp"
classname="org.apache.tools.ant.taskdefs.optional.net.FTP">
<classpath>
<pathelement location="${basedir}/lib/ant-commons-net.jar"/>
<pathelement location="${basedir}/lib/commons-net-2.0.jar"/>
</classpath>
</taskdef>
<ftp server="yourserver.com"
userid="anonymous"
password="blah">
<fileset dir="somedirectory"/>
</ftp>
But this is probably a worse option than just using the -lib switch (with or without a wrapper script). The only other thing I can think of is to try to find a third-party FTP task to use instead of the default one.
I have a solution:
you can download a new "classloader" task from http://enitsys.sourceforge.net/ant-classloadertask/ and load it whith:
<taskdef resource="net/jtools/classloadertask/antlib.xml"
classpath="XXX/ant-classloadertask.jar"/>
Naw can do things like loading classes with the same classloader that ant use for his task:
<classloader loader="system" classpath="XXX/commons-net-2.0.jar"/>
or "loader="project""
Then you definde your task:
<taskdef name="ftp" classname="org.apache.tools.ant.taskdefs.optional.net.FTP"/>
and go :-)
So I succeeded in doing this for the ant-salesforce.jar that you get when trying to do salesforce work (fun...)
Check to see if the jar has an xml file in it that looks something like this:
<antlib>
<typedef name="compileAndTest" classname="com.salesforce.ant.CompileAndTest"/>
....
</antlib>
Then in ant give it a taskdev that reads that file from inside the given jar, like this:
<taskdef resource="com/salesforce/antlib.xml" classpath="lib/ant-salesforce.jar" />
Hope that helps some.
Ah, man, this is just so nasty. I run ant from eclipse. I don't want to reconfigure ant in eclipse for new workspaces, so here's what I decided to do, to decouple running the task and configuring ant. I extracted the ftp task to a separate build file. Next I added a native call to the command line to start a completely new ant process with the required libraries on the path:
<target name="deploy-ftp">
<exec command="ant">
<arg line="-buildfile ftp.xml deploy-ftp -lib lib/ant"/>
</exec>
</target>
Now the master build file can be run without any special arguments and no modifications are required to the ant installation. It's nasty though, since the ftp task runs in a completely clean environment. None of the properties and paths from the master build file are available. Luckily I had all of these in a separate property file anyway, so I only needed a single import.
I would like to add a big thanks to Dan Dyer. Without your extensive explanation of what's going on behind the scenes, I wouldn't have found this solution.
Will this work assuming libs is directly under you project's base directory
<taskdef name="ftp" classname="org.apache.tools.ant.taskdefs.optional.net.FTP">
<classpath>
<pathelement location="${basedir}\libs\commons-net-1.4.0.jar"/>
</classpath>
</taskdef>
Your users all have ant installed on their machines but you can't / don't want to make them add the FTP jar? Can you bundle ant with your project make tasks that call YOUR ant bundle, with the jars placed so it'll work as follows?
<taskdef name="ftp" classname="org.apache.tools.ant.taskdefs.optional.net.FTP">
<classpath>
<pathelement location="\lib\commons-net-1.4.0.jar"/>
</classpath>
</taskdef>
<target name="testFtp">
<ftp server="blah" userid="foo" password="bar">
<fileset file="test.file" />
</ftp>
</target>

Resources