NoClassDefFoundError when using system scope - java

I am currently facing and issue when using Maven with NetBeans 7.1 - I have included a lib that I can't place into a Maven repository in the System scope. It looks something like the following:
<dependency>
<groupId>org.company</groupId>
<artifactId>FBTM</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/FBTM-1.0-SNAPSHOT.jar</systemPath>
</dependency>
The IDE seems to pick up this dependency fine, as it shows in Dependencies folder (Project view) and I am able to view the jar contents. I can also import classes from the jar without any issues.
However, when I attempt to run my project (and the runtime has any of the classes in use) I get a NoClassDefFoundError for the class in question. It can be any class.
Stacktrace:
Exception in thread "main" java.lang.NoClassDefFoundError: com/company/otaupdate/sim/commands/Select
at com.company.fbtranslationlibrary.mc.scripts.UserData$1.<init>(UserData.java:30)
at com.company.fbtranslationlibrary.mc.scripts.UserData.<init>(UserData.java:28)
at com.company.fbtranslationlibrary.mc.MS.get_profile_script(MuscadeScript.java:80)
at com.company.fbtranslationlibrary.mc.MS.access$300(MS.java:16)
Any help or pointers would be greatly appreciated!

For system scope:
Maven Doc says:
system
This scope is similar to provided except that you have to
provide the JAR which contains it explicitly. The artifact is always
available and is not looked up in a repository.
Your IDE provides the jar somehow. But when running you need to provide it.
Try with the default scope (that is no scope tag at all).
edit:
If you want maven to keep the system scope, you need to provide the jar runtime via the classpath.

It would appear that the NetBeans application runner is not smart enough to put this JAR file on the execution classpath.
Why don't you add the JAR and POM files for this dependency to your local repository? Then you can use a regular dependency.
Alternatively, see if there's a way to tweak the runtime classpath for the NetBeans application launcher for this application. (I'm not sure I'm using the right terminology here ... I'm an Eclipse person.)

I resolved this issue by creating the following build.xml for the project:
<!-- make the repo dir -->
<mkdir dir="${m2.home}" />
<!-- copy libs -->
<copy todir="${m2.home}">
<fileset dir="dist"/>
</copy>
<!-- copy pom -->
<copy todir="${m2.home}">
<fileset dir="pom"/>
<mapper>
<mapper type="regexp"
from="pom.xml" to="${m2.app.name}-${m2.app.ver}.pom" />
</mapper>
</copy>
<!-- use naming policy -->
<copy todir="${m2.home}">
<fileset dir="dist"/>
<mapper>
<mapper type="regexp"
from="${m2.app.name}.jar" to="${m2.app.name}-${m2.app.ver}.jar" />
</mapper>
</copy>
<!-- remove unnecessiary files -->
<delete>
<fileset dir="${m2.home}">
<include name="${m2.app.name}.jar"/>
<include name="README.TXT"/>
</fileset>
</delete>
</target>
I also had to manually create a pom.xml file for the project, which I copy over to the local repository using the same naming conventions. It's not the best solution possible, but works well enough.

Try to avoid system scope and use workaround noted in
http://eureka.ykyuen.info/2014/06/10/maven-include-system-scope-dependency-in-maven-assembly-plugin/

Related

Is there a single JAR somewhere for google-cloud-translate to compile with ANT vs. dealing with 40+ separate JARs?

Working with a client who still lives in the ant build environment world (on a Windows box). Want to use google-cloud-translate library in a Java program but if I include all the dependencies of google-cloud-translate that's over 40 jars with everything from guava to threetenbp to joda-time to google-cloud-core and more if I include all the dependencies of the dependencies and the optional ones. Is there an easy way to package up all the dependencies into a single JAR so I only need the one jar file referenced in the build.xml?
If you absolutely need to use Ant, there's a pretty simple option for this, although it uses a package that is no longer maintained: http://maven.apache.org/ant-tasks/index.html
With the Maven Ant Tasks, you can define an artifact in your build with the <artifact:dependencies> task, and that artifact will be resolved to your local repo along with all of its own dependencies. You can also give it a filesetId so you can easily manipulate the files after that point:
<project xmlns:artifact="antlib:org.apache.maven.artifact.ant">
<typedef
classpath="lib/maven-ant-tasks.jar"
resource="org/apache/maven/artifact/ant/antlib.xml"
uri="antlib:org.apache.maven.artifact.ant"
/>
<target name="resolve-google-cloud-translate-artifacts">
<artifact:dependencies filesetId="google-cloud-translate">
<dependency artifactId="google-cloud-translate" groupId="com.google.cloud" version="1.38.0" />
<remoteRepository id="central" url="https://repo.maven.apache.org/maven2/" />
</artifact:dependencies>
<delete dir="google-cloud-translate.libs" />
<mkdir dir="google-cloud-translate.libs" />
<copy todir="google-cloud-translate.libs">
<fileset refid="google-cloud-translate" />
<flattenmapper />
</copy>
</target>
</project>
Note the artifact: namespace definition in the project element. The Maven tasks won't work without it.

Java Ant Job how to ship Log4j jar with my jar

Just trying to upgrade some old stuff and part of that I need to bundle my custom jar which uses Log4j. I did add the following for my <javac> task which compiles successfully.
<path id="my.classpath">
<fileset dir="${mainpath}">
<include name="**/*log4j*.jar"/>
</fileset>
</path>
<javac srcdir="src/java" destdir="build/filez/java" debug="on" deprecation="no"
includes="my/instruments/**/*, org/apache/log4j/**/*">
<classpath refid="my.classpath"/>
</javac>
However, In my <jar> job I cannot see any log4j dependency packed with my custom jar. This might be a silly question, but how do ensure that my custom-jar does not fail when called from another application since the dependency isn't packed? Will it be okay as long as log4j has been loaded by classloader in the target application?
Additionally, do I need to add something in my Manifest for this?
I cannot use Maven (yes I know) for a little while, so cannot solve this problem with maven
You can use One-JAR to package your code along with it's dependencies into one big executable JAR.
It can be used either as a standalone tool from the command line or as a task defined in build.xml.
<!-- Construct the One-JAR file -->
<one-jar destfile="hello.jar" manifest="hello.mf">
<main>
<!-- Construct main.jar from classes and source code -->
<fileset dir="${classes.dir}/src"/>
</main>
<lib>
<fileset file="${build.dir}/lib.jar" />
</lib>
</one-jar>

How to build Android project with Ant that depends on Java project?

I have an Android Library project, AndroidLib, that depends on a Java project in my workspace, JavaLib.
In Eclipse this is done by adding the JavaLib project to Java Build Path > Projects for AndroidLib and setting it to be exported in the Order and Export tab, and it builds fine.
However Ant doesn't seem to pick up the dependency on the JavaLib project when building AndroidLib (I have run android update lib-project). What is the best way to add this dependency to my build.xml?
I had the same problem. I ended solving it in a very hackish way.
In AndroidLib/build.xml (or better in AndroidLib/custom_rules.xml), I defined a -pre-build target that builds the JavaLib and copies the resulting jar in libs/. I also defined a -post-package target to remove the copied jar, otherwise Eclipse will get confused.
<property name="lib.javalib.project.dir" location="${basedir}/../JavaLib" />
<target name="-pre-build">
<subant buildpath="${lib.javalib.project.dir}" target="package" failonerror="true" />
<copy todir="${basedir}/libs" failonerror="true" verbose="true">
<fileset dir="${lib.javalib.project.dir}/target">
<filename name="javalib*.jar"/>
</fileset>
</copy>
</target>
<target name="-post-package">
<delete verbose="true">
<fileset dir="${basedir}/libs" includes="javalib*.jar" />
</delete>
</target>
This solution is far from satisfying, but it gets the job done.
You will find a similar question and answer there : Android Ant Include Java Library projects
I'll answer my own question: the only way to do this properly is to not use Ant.
You need a proper dependency management system. Maven is your only real choice here.
You need a build system that supports Maven. I currently use Maven again for this, because at the time it was the only mature build tool that also supported Maven dependency management -- now (albeit for Android Library Projects, since the format for these is not yet final) Gradle should be your build tool of choice.
I changed my build.xml to run the java project's ant file and then copy the jar to the libs folder:
<target name="-pre-compile">
<ant antfile="build.xml" dir="dependencies/JavaUtils" target="clean"/>
<ant antfile="build.xml" dir="dependencies/JavaUtils"/>
<copy todir="${jar.libs.dir}" failonerror="true" file="dependencies/JavaUtils/dist/Java-utils.jar"/>
<sleep seconds="3"/> <!-- Delay for the file to be recognized after the copy -->
</target>
How this helped someone.
BTW, I started reading about Gradle's dependency/build/whatever system and got the creeps from it. Ahhh, what a relief to get back to ant...

Java - How to create JavaDocs in Apache Ivy

I know how to create javadocs for my source file in Apache Ant Build.
<target name="doc" description="generate documentation">
<delete dir="${doc.dir}"/>
<mkdir dir="${doc.dir}"/>
<javadoc sourcepath="${source.dir}" destdir="${doc.dir}"/>
</target>
But I don't know how to create them in Apache Ivy.
Could some one show me a sample example ?
Apache Ivy is a dependency management library for use with (not a replacement for) Apache Ant. Therefore, you will use the same <javadoc> task as always.
Apache Ivy is a dependency manager that works with Ant (a build manager). Apache Ivy is usually (or possible always?) used with Ant to handle builds. Since javadoc creation is a build task not a dependency task, it wouldn't make sense to generate javadocs using Ivy.
It looks like you're going around in a circle here. Ivy works with Ant. You still have a build.xml file that you use for your builds. The ivy.xml file simply contains a list of the third-party jars your project needs in order to build. Thus, the direct answer to your question would be:
Put the following in your build.xml:
<target name="doc" description="generate documentation">
<delete dir="${doc.dir}"/>
<mkdir dir="${doc.dir}"/>
<javadoc sourcepath="${source.dir}" destdir="${doc.dir}"/>
</target>
When you use Ivy, you still use Ant. And, you still have a build.xml file. And, you still write compose your build with various Ant tasks.
Download this project. It's a simple build that contains three class files and a build.xml file. You will notice there's a jar directory with the following two jars:
commons-logging-1.1.1.jar
spring.jar
If you look at lines 36 to 40 of the build.xml file, the project is creating a compile classpath like this:
<path id="java">
<fileset file="jar/spring.jar"/>
<fileset file="jar/commons-logging-1.1.1.jar"/>
</path>
So, when he compiles, he does this:
<javac destdir="bin">
<src path="${src}"/>
<classpath refid="java"/>
</javac>
Now, let's look at how this may change with Ivy. In Ivy, I create a ivy.xml file that contains a description of what jars I need. However, I only have to specify classes I use directly. In this case, I only need the spring.jar. Here's my ivy.xml:
<ivy-module version="1.0>
<info organisation="com.seantheflexguy"
name="ContextExample"
revision="1.0"/>
<configurations>
<conf="default" visibility="public"/>
<conf="compile" visibility="public"/>
</configurations>
<dependencies>
<dependency org="org.springframework" name="spring"
rev="2.0.4" conf="compile->default"/>
</dependencies>
</ivy-module>
Now, instead, of using the jars in the jar directory, I'll have Ivy construct the classpath:
<ivy:resolve/>
<ivy:cachepath pathid="java"/>
<javac destdir="bin">
<src path="${src}"/>
<classpath refid="java"/>
</javac>
Notice instead of using the <path> task to create a classpath, I use two Ant tasks that Ivy uses. The <ivy:resolve/> looks at my ivy.xml and resolves my dependencies on the jars I request. These jars will be downloaded into my $HOME/.ivy2/cache directory.
The <ivy:cachepath> task creates a classpath I'm calling the classpath java because that's what it was previously called.
With those two Ivy tasks, I've created a classpath that I can use with the <javac> task. In fact, I'm not even bothering to change the <javac> task.
So, in Ivy:
I still need my build.xml. It's how I define the various build tasks I need to do. In fact, Ivy defines even more Ant tasks I need in my build.xml file.
The ivy.xml file simply defines my jar dependencies. When I implement Ivy in this project, I can delete the jar directory.
Does this help you understand how Ivy works?

How to configure Ivy for Ant build

I currently have ANT_HOME located at /home/<myuser>/ant/1.8.4/ant-1.8.4.
I just downloaded the Apache Ivy tarball that includes its dependencies. I extracted it to /home/<myuser>/ivy/2.3.0-rc1/ivy-2.3.0-rc1.
I then copied /home/<myuser>/ivy/2.3.0-rc1/ivy-2.3.0-rc1/lib/*.jar to ANT_HOME/lib. If my understanding of how Ant works with plugins/extensions is correct, then Ant should now be able to access all of Ivy's tasks at runtime.
My next question is, how do I define Ivy tasks inside my Ant buildfile? Say I want to use ivy-retrieve, ivy-resolve and ivy-publish tasks. What are all the configurations I need to do (in the XML) to get these tasks working when I run my Ant build from the command-line (I will not be building through the Ant-Eclipse plugin). Thanks in advance!
First, you have to define a <taskdef> to point to the Ivy tasks.
<property environment="env"/>
<property name="ivy.home" value="${env_IVY_HOME}"/>
<taskdef resource="org/apache/ivy/ant/antlib.xml">
<classpath>
<fileset dir="${ivy.home}">
<include name="*.jar"/>
</fileset>
</classpath>
</taskdef>
That will give you access to the Ivy tasks. You'd use these tasks like this:
<cachepath pathid="main.classpath" conf="compile"/>
The problem is that your Ivy tasks names might clash with other Ant tasks. For example, there's an Ivy task <report>. To solve this, you can create an Ivy namespace. To do that, you put a reference in your namespace in the <project> entity like this:
<project name="my.proj" default="package" basedir="."
xmlns:ivy="antlib:org.apache.ivy.ant"/>
Now, when you define the Ivy tasks, you can use that antlib:org.apache.ivy.ant reference to your ivy namespace. Same taskdef as before, but with a uri field:
<property environment="env"/>
<property name="ivy.home" value="${env_IVY_HOME}"/>
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="antlib:org.apache.ivy.ant">
<classpath>
<fileset dir="${ivy.home}">
<include name="*.jar"/>
</fileset>
</classpath>
</taskdef>
By the way, there's nothing special about that uri. I could have done this:
<project name="my.proj" default="package" basename="."
xmlns:ivy="pastrami:with.mustard">
[...]
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="pastrami:with.mustard">
<classpath>
<fileset dir="${ivy.home}">
<include name="*.jar"/>
</fileset>
</classpath>
</taskdef>
The point is now you can prefix your task names with ivy:. Instead of this:
<cachepath pathid="main.classpath" conf="compile"/>
You can now do this:
<ivy:cachepath pathid="main.classpath" conf="compile"/>
And that's how you gain access to your Ivy Ant tasks.
Now, you have access to your Ivy Ant tasks, you need to define an ivysettings.xml file and use the <ivy:settings/> task to point there:
<ivy:settings file="${ivy.home}/ivysettings.xml"/>
There is a default ivysettings.xml file embedded in Ivy that will point you to the world wide Maven repository system. If you don't have a company wide Maven repository, then you can use the default ivysettings.xml file:
<ivy:settings/>
That's pretty simple.
Once you've done that, you need to read in and resolve your ivy.xml file which usually sits in the root of your project in the same directory as your build.xml file.
Basically, your ivy.xml file contains references to the third party jars you want to bring into your project. For example:
<dependencies>
<dependency org="log4j" name="log4j" rev="1.2.17" conf="compile->default"/>
<dependency org="junit" name="junit" rev="4.10" conf="test->default"/>
</dependencies>
What this is saying is that I need the log4j.jar (revision 1.2.17) for compilation (and for compiling tests too) and I need junit.jar (revision.4.10) for compilation of my test code.
The compile->default is a mapping of my compile configuration to Maven's default configuration (which says I just want the Jar and any other jars that it might depend upon.
Where's does my compile configuration come from? I define it in my ivy.xml. There are ten standard configurations. This also goes into your ivy.xml file:
<configurations>
<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
<conf name="optional" visibility="public" description="contains all optional dependencies"/>
</configurations>
You can use any configuration name you want, but these map to the default Maven configurations and are widely used.
Once you have your ivy.xml file defined, you can use <ivy.resolve> to resolve your dependencies:
<ivy:resolve/>
So, we have the following:
How to use <taskdef> in your build.xml to incorporate the Ivy Ant tasks into your build.
How to use the Ivy Ant task <ivy:settings> to configure Ivy.
How to use <ivy:resolve/> to read in your ivy.xml file and resolve your third party jar dependencies.
Now, you probably want to actually use those jar files. There are three ways to do this:
<ivy:cachepath pathid="main.classpath" conf="compile"/>
The <ivy:cachepath/> task will create a classpath (in this case called main.classpath) that points to the jars you have in your ivy.xml file's compile configuration. This is used most of the time.
If you need a fileset, you can use this:
<ivy:cachefileset setid="compile.fileset" conf="compile"/>
In this case, it will create a fileset with a refid of compile.fileset.
Sometimes you have to bring the jars into your project. For example, if you create a war or ear file, you want to enclose your jars. In that case, you can use this:
<property name="lib.dir" value="${target.dir}/lib"/>
<ivy:retrieve pattern="${lib.dir}/[artifact].[ext]"
conf="runtime"/>
That will fetch your jars into the ${lib.dir} directory, so you can include them in wars or ears.
Sorry for the long answer, but there are a lot of steps to cover. I highly recommend Manning's book Ant in Action which has a whole chapter on Ivy.
David gave a very fine answer, but I'd like to point out that the taskdef is not required.
Provided the ivy.jar is in the expected location the namespace declaration at the top of the ANT file is enough:
<project ..... xmlns:ivy="antlib:org.apache.ivy.ant">
For more detail I'd recommend reading about how ANT libs work.
The following answer provides some more "setting up ivy" advice:
Ivy fails to resolve a dependency, unable to find cause

Categories

Resources