How to include the lib folder in the manifest classpath in Netbeans - java

A library that my java application uses needs looks for a file (log4j.xml) in the class path. I use netbeans to manage my project, but I can't find a way to include the lib/ folder.
Netbeans automatically creates a MANIFEST.MF file inside the application jar and also creates a folder called lib/ which includes all dependencies. This manifest specifies a Class-Path attribute that overrides any -cp argument provided on the command line. I can select an arbitrary folder in netbeans' library panel, but it creates a sub folder in the manifest's classpath. I'd like all dependencies and the log4j.xml file inside the lib/ folder.
Hopefully it's possible to do this in the IDE. I include a snippet of the auto-generated build-impl.xml file.
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
<pathconvert property="run.classpath.without.build.classes.dir">
<path path="${run.classpath}"/>
<map from="${build.classes.dir.resolved}" to=""/>
</pathconvert>
<pathconvert pathsep=" " property="jar.classpath">
<path path="${run.classpath.without.build.classes.dir}"/>
<chainedmapper>
<flattenmapper/>
<globmapper from="*" to="lib/*"/>
</chainedmapper>
</pathconvert>
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
<fileset dir="${build.classes.dir}"/>
<manifest>
<attribute name="Main-Class" value="${main.class}"/>
<attribute name="Class-Path" value="${jar.classpath}"/>
</manifest>
</copylibs>
<echo>To run this application from the command line without Ant, try:</echo>
<property location="${dist.jar}" name="dist.jar.resolved"/>
<echo>java -jar "${dist.jar.resolved}"</echo>
</target>
Thanks.

Instead of editing the build-impl.xml file you should add this entry to the build.xml file. When you modify anything in your project pertaining to the building of that project, it will generate a new build-impl.xml file.
Here is an example of what I put in my build.xml file:
<target depends="init" name="-do-clean">
<delete dir="${build.dir}"/>
<delete file="${dist.jar}"/>
<delete dir="${dist.dir}/lib"/>
<delete dir="${dist.dir}/resources"/>
</target>
Since I put this in the build.xml file, it will override the "-do-clean" section of the build-impl.xml file which contains:
<target depends="init" name="-do-clean">
<delete dir="${build.dir}"/>
<delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/>
</target>
Furthermore, since it is in the build.xml it won't be modified by Netbeans.

I found a way to acheive this modifying the build-impl.xml.
I changed:
<attribute name="Class-Path" value="${jar.classpath}"/>
to:
<attribute name="Class-Path" value="${jar.classpath} /lib"/>
The problem is that netbeans will overwrite it since this file is automatically generated.

You can simply turn off project option Build/Packaging/Copy Dependent Library and manualy edit manifest.mf in root folder of your project (which is a template for manifest in jar file).

It seems that your problem is the "globmapper" that stores your log4j.xml file in /lib - you'd want it on the "/" or the jar.

Related

Java Netbeans, libraries not being packaged into jar file on build

I am using netbeans and have this in my build.xml file (under Project > Build.xml)
<target name="package-for-store" depends="jar">
<property name="store.jar.name" value="MyProjectName"/>
<property name="store.dir" value="store"/>
<property name="store.jar" value="${store.dir}/${store.jar.name}.jar"/>
<echo message="Packaging ${application.title} into a single JAR at ${store.jar}"/>
<delete dir="${store.dir}"/>
<mkdir dir="${store.dir}"/>
<jar destfile="${store.dir}/temp_final.jar" filesetmanifest="skip">
<zipgroupfileset dir="dist" includes="*.jar"/>
<zipgroupfileset dir="dist/lib" includes="*.jar"/>
<manifest>
<attribute name="Main-Class" value="${main.class}"/>
</manifest>
</jar>
<zip destfile="${store.jar}">
<zipfileset src="${store.dir}/temp_final.jar"
excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
</zip>
<delete file="${store.dir}/temp_final.jar"/>
Supposedly what this should do is package the 3 libraries i have into my jar file so that when the jar is run it has its dependencies with it.
My folder system looks like this:
Project / dist / lib /
commons-lang3-3-7.jar
commons-text-1.1.jar
jsoup-1.10.3.jar
But when i click "Clean and Build" in netbeans it does not package the libraries into the jar, instead it seems to be ignoring the code that i have put in the build.xml file.
Is there some button that i need to click in order to get it to use my build configuration?
Finally i found the answer. In Netbeans it is possible to build the dependent libraries within the jar file.
Netbeans features a tool that automatically un-archives the dependent libraries and includes them in the jar (such as MyJar.jar|org\apache\commons...).
And it is actually very easy to do!
First you need to click on the "Files" tab in the top left window which shows your project. Then you should be able to see all your files including the Build.xml (in which you have put your build code, such as that featured in the question above).
Right click on the Build.xml file and select "Run Target"
Then choose "Other Target" in the pop up menu
Then select "package-for-store" in the further pop up menu
This will then build your jar file, with dependencies under /YourProject/store/

Failing to build working .jar file using ANT

I've made a simple 'AntExecutor' app in eclipse that can run ant tasks programmatically and it works. But for university purposes I need to keep it independant from IDE. So, funnily, I'm strugling to create ant tasks which would compile,build my 'AntExecutor' app (which executes ant-tasks) :)
Stripped-down version I'm currently trying to define ant-tasks for only contains one source file in 'storageAccess' package:
./src/storageAccess/AntExecutor.java
I've got some libraries that AntExecutor.java makes use of at:
./lib
And the build file is at:
./build.xml
AntExecutor.java also needs ant libraries to execute ant tasks so they're added to CP at compile at. in build file:
<classpath path="${build};D:/DevTools/apache-ant-1.9.8/lib/;"/>
Full build.xml file:
<project name="AntExecutor" default="dist" basedir=".">
<description>
simple example build file
</description>
<!-- set global properties for this build -->
<property name="src" location="src"/>
<property name="build" location="build/classes/"/>
<property name="dist" location="build/jar/"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init"
description="compile the source " >
<!-- Compile the java code from ${src} into ${build} -->
<javac destdir="${build}">
<src path="${src}"/>
<classpath path="${build};D:/DevTools/apache-ant-1.9.8/lib/;"/>
</javac>
</target>
<target name="dist" depends="compile"
description="generate the distribution" >
<!-- Create the distribution directory -->
<mkdir dir="${dist}"/>
<!-- Put everything in ${build} into RunExecutor.jar file -->
<jar destfile = "${dist}/RunExecutor.jar" basedir="${build}">
<manifest>
<attribute name = "Main-Class" value = "storageAccess.AntExecutor"/>
<attribute name = "Class-Path" value = "D:/DevTools/apache-ant-1.9.8/lib/;"/>
</manifest>
</jar>
<copy todir="${dist}\lib">
<fileset dir="lib"/>
</copy>
</target>
<target name="clean"
description="clean up" >
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
now if i run 'ant dist' command I get no errors, build succeeds and RunExecutor.jar file is created at ./build/jar
To check contents of RunExecutor.jar, I ran: jar tf build/jar/RunExecutor.jar
result:
META-INF/
META-INF/MANIFEST.MF
storageAccess/
storageAccess/AntExecutor.class
so it seems like storageAcces.AntExecutor class was indeed successfully compiled to .jar file.
However, if i try running it like this: java -jar build/jar/RunExecutor.jar
I get this error:
Error: Could not find or load main class storageAccess.AntExecutor
Main-question:
How come it can't find the class that is clearly in it.(as 'jar tf' shows) how do I fix this?
Also, what is the corret way to add ant/lib/*.jar files to CP for compiling and running 'RunExecutor.jar' ?
is it okay just to specify the path to them as I do now? :
<attribute name = "Class-Path" value = "D:/DevTools/apache-ant-1.9.8/lib/;"/>
or, maybe I should use wildcard like:
<attribute name = "Class-Path" value = "D:/DevTools/apache-ant-1.9.8/lib/*.jar;"/>
or, should I frustratingly add all the files one by one?
<attribute name = "Class-Path" value = "D:/DevTools/apache-ant-1.9.8/lib/ant.jar;"/> , etc...
The problem with
<attribute name = "Class-Path" value = "D:/DevTools/apache-ant-1.9.8/lib/;"/>
is you are hard-coding path which is not a good practice. This jar will not execute on other machines as there are chances that they won't have lib under same location.
You can directly create executable jar from eclipse project itself. For steps refer this.
You can also put required lib in same jar file and they will by default get added to class-path.

java.util.logger.Logging: keeping logger.properties file outside the jar file

In my project I have a properties file which I use to set the level of logging. Now when I export my project as a jar and use it to run the project on a remote machine (linux), I cannot set the level. Is there a way to keep the properties file outside the jar file such that I can set the level and make the jar read that properties file. (preferred using environment variable)
There are several ways to achieve this, for example:
Configure your IDE to export resources outside the JAR: usually I don't consider this option since the specific solution depends by the developer's IDE
Use a generic build tool, for example Ant, and specify in the build.xml file which properties files should be packaged outside the jar
Integrate your project with Maven and customize the package goal in order to copy some specific properties file outside jar
From your question I guess you are exporting the JAR from your IDE, but as I stated above the solution depends by the IDE. For this reason, in order to adopt an IDE independent solution, I would suggest to use Ant. This would allow you to solve this and many similar issues that could arise in the future.
You can get Ant here: just download and unpackage it in any folder, it takes a couple of minutes. Then add a reference to Ant bin directory in your PATH variable (not strictly necessary but suggested) and create a sample build.xml file. Here it is a template example:
<project name="template" default="compile" basedir=".">
<description>Build file template</description>
<property name="project.name" value="myProject"/>
<property name="driver.log" value="log4j-1.2.15.jar"/>
<property name="driver.database" value="ojdbc6.jar"/>
<property name="library.home" value="lib"/>
<property name="env.type" value="dev"/>
<property name="src.version" value="Demo" />
<property name="src.folder" value="root/folder/template"/>
<property name="src.package" value="root.folder.template"/>
<property name="src.home" value="${basedir}/src/${src.folder}"/>
<property name="dist.home" value="${basedir}/dist"/>
<property name="build.home" value="${basedir}/build"/>
<property name="docs.home" value="${basedir}/docs"/>
<!-- Setting the classpath necessary to compile -->
<path id="compile.classpath">
<pathelement location="${library.home}/${driver.log}"/>
<pathelement location="${library.home}/${driver.database}"/>
</path>
<!-- DELETE the class files from the ${build.home} directory tree -->
<target name="clean" description="Clean up the build folder">
<delete dir="${build.home}"/>
<delete dir="${dist.home}"/>
</target>
<!-- CREATE the build directory structure used by compile -->
<target name="init" description="Creates the necessary directories">
<mkdir dir="${dist.home}"/>
<mkdir dir="${build.home}"/>
</target>
<!-- COMPILE the project and copy all necessary resources -->
<!-- Options: <compilerarg value="-Xlint"/> -->
<target name="compile" depends="init" description="Compile the sources">
<javac srcdir="${src.home}" destdir="${build.home}" includeantruntime="false">
<classpath refid="compile.classpath"/>
</javac>
<copy todir="${build.home}/${src.folder}/resources">
<fileset dir="${src.home}/resources">
<include name="messages_list.properties"/>
<include name="messages_list_en.properties"/>
</fileset>
</copy>
<copy file="${src.home}/resources/log4j_${env.type}.properties" tofile="${build.home}/${src.folder}/resources/log4j_${project.name}.properties"/>
<copy file="${src.home}/resources/configuration_${env.type}.properties" tofile="${build.home}/${src.folder}/resources/${project.name}_config.properties"/>
</target>
<!-- Creates the DISTRIBUTABLE JAR package and add 3d part libraries -->
<target name="dist" description="Create the distributable JAR archive">
<jar destfile="${dist.home}/${project.name}.jar">
<fileset dir="${build.home}">
<exclude name="place_holder\"/>
</fileset>
<!-- Setting MANIFEST properties -->
<manifest>
<section name="${ant.project.name} - ver. ${src.version}">
<attribute name="Built_By" value="${user.name}"/>
<attribute name="Created" value="${ts}"/>
</section>
<attribute name="Main-Class" value="package.mine.MainClass"/>
<attribute name="Class-Path" value=". lib/${driver.log} lib/${driver.database}"/>
</manifest>
</jar>
<!-- Adding third part libraries -->
<mkdir dir="${dist.home}/lib"/>
<copy file="${library.home}/${driver.database}" todir="${dist.home}/lib"/>
<copy file="${library.home}/${driver.log}" todir="${dist.home}/lib"/>
</target>
<tstamp><format property="ts" pattern="dd/MM/yyyy HH:mm:ss" /></tstamp>
</project>
Remark: in the template above you should replace the sample JARS (log4j and the OJDBC driver) with the actual JARS needed by your project. Then you can customize the copy task in order to place the properties files where you wish. You can copy those file in any directory you like, as long as such path appears in the application's classpath.

Why combine all jars together?

I have create RESTful web service based on the JAX-RS and used Jersey embedded web server. My ant script compiles code successfully while it gives me error ClassNotFoundException when I run my main class. So after doing research I came up with solution & here it goes java build ant file with external jar files . What I did was created a bundled jar file try to execute that & it works perfectly fine. I want to know the reason behind :
why this solution works ?
Why I should combine all jar file ?
Is it similar to war file which we create following J2EE architecture otherwise war will not be extracted by server ( say TOMCAT ) & in my case jar file for Jersey embedded HTTP server?
EDIT:
Here is my ant build.xml file
<property name="lib.dir" value="${user.dir}/lib"/>
<property name="build.dir" value="${user.dir}/build"/>
<property name="build.lib.dir" value="${build.dir}/lib"/>
<property name="build.classes.dir" value="${build.dir}/classes"/>
<property name="src.dir" value="${user.dir}/src/main/java"/>
<property name="main.class" value="com.assignment.ConsoleServer"/>
<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"/>
</path>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="init" depends="clean">
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build.dir}"/>
<mkdir dir="${build.classes.dir}"/>
</target>
<target name="copy_jars" depends="init" >
<copy todir="${build.lib.dir}" >
<fileset dir="${lib.dir}">
<include name="**/*.jar" />
</fileset>
</copy>
</target>
<target name="compile" depends="copy_jars">
<javac srcdir="${src.dir}" destdir="${build.classes.dir}" classpathref="classpath" includeantruntime="false"/>
</target>
<target name="jar" depends="compile">
<jar destfile="${build.dir}/${ant.project.name}.jar" basedir="${build.classes.dir}">
<manifest>
<attribute name="Main-Class" value="${main.class}"/>
</manifest>
<zipgroupfileset dir="${lib.dir}" includes="*.jar"/>
</jar>
</target>
<target name="run" depends="jar">
<java fork="true" classname="${main.class}">
<classpath>
<path refid="classpath"/>
<path location="${build.dir}/${ant.project.name}.jar"/>
</classpath>
</java>
</target>
Here is my folder structure
P.S. I am not java expert so pardon me if this question is stupid.
Why this solution works?
In your particular case, you probably didn't include all of the necessary dependencies in your deployment in your previous. (It is not clear from your question how you were originally doing the deployment.)
Now you have put all of the application and dependent class files, etc into one JAR file, and presumably you are deploying / running that file. It works because now it has everything that it needs to run ... which it didn't before.
Why I should combine all jar file?
In your case I suspect that it was not strictly necessary. There was probably a way to "deploy" all of the dependencies without combining them into a single JAR file.
However, there is one case where a "uber-jar" has advantages. That is when the JAR is intended to be an "executable" JAR, and you want to be able to distribute / install it as a single file. (And executable JAR
file can refer to external JARs, etc, but the way that you have to do
it is "fragile".)
Is it similar to war file ... ?
Sort of, though a WAR file contains JAR files ... and typically other kinds of resources that the web-container understands.
The solution works because you packed all you service classes and depending libraries in one jar. That jar and everything inside will be in the class path and visible to your execution virtual machines class loader.
If you leave your depending libraries out your Jersey Web server needs to have them on it's class path, then you wouldn't get ClassNotFoundExcpetion
You shouldn't pack web application in single jar. You should crate war file where you dependencies will be placed inside WEB-INF/lib. You would easily then deploy that war on any application server. Switching to Maven instead of Ant can help a lot.
EDIT: After you added more details to description and ant
If you don't want to use fat-jar you can either
modify your antjava task to specify classpath that will reference
all external libraries (basically telling ant how to build
-classpath parameter for java -jar command
even better, modify your javac ant task by making complete Manifest file that specifies Class-Path correctly, take a better
look at the solution (at the bottom) of the answer you linked (java build ant file with external jar files)
For completness reference on Manifest here

How can include multiple jars in the classpath using ant?

I have a bunch of .java files in a "src" folder that depend on three jars in a "lib" folder. I have the following build.xml file:
<?xml version="1.0"?>
<project name="MyProj" basedir=".">
<property name="src" value="src"/>
<property name="build" value="build"/>
<property name="lib" value="lib"/>
<path id="master-classpath">
<fileset dir="${lib}">
<include name="activemq-all-5.1-SNAPSHOT.jar"/>
<include name="geronimo-jms_1.1_spec-1.1.1.jar"/>
<include name="activemq-core-5.3.0.jar"/>
</fileset>
</path>
<javac destdir="${build}">
<src path="${src}"/>
<classpath refid="master-classpath"/>
</javac>
</project>
This compiles fine, but when I try and run I get
"java.lang.NoClassDefFoundError:
javax/jms/Destination"
This program runs and compiles fine when I include the jars in the buildpath using Eclipse, though.
EDIT: So I copied the jars into the folder that has the compiled classes. The class with the main method is NDriver.class. When I try:
java -classpath
./geronimo-jms_1.1_spec-1.1.1.jar:./activemq-core-5.3.0.jar:./activemq-all-5.1-SNAPSHOT.jar
NDriver
This gives:
Exception in thread "main"
java.lang.NoClassDefFoundError:
NDriver
I'd appreciate any help.
You need to put the jars used at compile time on the classpath when running the application. Sadly, you didn't provide any detail on how you are actually running it so its hard to provide more guidance.
UPDATE: The directory containing the compiled classes needs to be added to the classpath too. If you launch java from the directory containing the compiled classes, then you can use . to designate the current directory. Add it to the classpath as shown below to tell java to look for classes there too (I've added . right after activemq-all-5.1-SNAPSHOT.jar):
java -classpath ./geronimo-jms_1.1_spec-1.1.1.jar:./activemq-core-5.3.0.jar:./activemq-all-5.1-SNAPSHOT.jar:. NDriver
One way (slightly different variables than yours)
<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"/>
</path>
<manifestclasspath property="manifest.classpath" jarfile="${jarfile}">
<classpath refid="classpath"/>
</manifestclasspath>
<target name="jar" depends="compile" description="create the jar">
<jar destfile="${jarfile}" basedir="${build.dir}">
<manifest>
<attribute name="Manifest-Version" value="${manifest-version}"/>
<attribute name="Created-By" value="${ant.java.version}"/>
<attribute name="Main-Class" value="${main-class}"/>
<attribute name="Class-Path" value="${manifest.classpath}"/>
</manifest>
</jar>
</target>
Of course here I'm assuming that you are creating a jar and running it (including the classpath there). Another option would be to have a run target which use the <java> tag and explicitly use the classpath there.
Are the library jars included in the classpath when you run the program? Eclipse automatically add these, but you need to specifying them when you run the program from the command line.
From my experience it seems Eclipse will often include classes and jars in the classpath without explicitly using the classpath declaration. Indeed it can sometimes be quite hard to remove classes from Eclipse's build (they have to be deleted or clean'ed).

Categories

Resources