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.
Related
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.
Say I have a library and a binary target, let's call them MyLib and MyBin,
MyBin depends on MyLib.
I'm trying to create an Ant buildfile for MyBin that first builds MyLib and then includes it in the classpath when building MyBin.
I've tried using Ant tasks as in Building other, dependent projects with Ant .
However, it's not working, and from ant -v I think the MyBin build-deps target is not even building MyLib. Seems like it's confusing MyBin and MyLib properties? I'm not sure how to prevent this though.
I'm dumping only MyBin/build.xml below, but the MyLib is almost identical, except it does not have the build-deps target.
<project name="MyBin" default="main" basedir=".">
<property name="projectName" value="MyBin" />
<property name="src.dir" location="src" />
<property name="build.dir" location="bin" />
<property name="dist.dir" location="dist" />
<property name="dist.lib.dir" location="dist/lib" />
<property name="lib.dir" value="lib" />
<target name="build-deps" depends="init">
<!-- MyLib main target does clean -> build -> jar to dist folder -->
<!-- Its build.xml uses many of the same property values as above -->
<ant antfile="../MyLib/build.xml" target="main"/>
</target>
<path id="classpath">
<fileset dir="${basedir}/">
<include name="../MyLib/dist/**/*.jar" />
</fileset>
</path>
<!-- Need classpath to run this -->
<target name="compile" depends="build-deps" description="compile the source ">
<javac includeantruntime="false" srcdir="${src.dir}"
destdir="${build.dir}" classpathref="classpath" />
</target>
<!-- Group all dependencies into a big dependency-all.jar -->
<target name="copy-dependencies">
<mkdir dir="${dist.lib.dir}" />
<jar jarfile="${dist.lib.dir}/dependencies-all.jar">
<zipgroupfileset dir="${lib.dir}">
<include name="**/*.jar" />
</zipgroupfileset>
</jar>
</target>
<!-- jar it, extract above dependency-all.jar and zip it with project files -->
<target name="jar" depends="compile, copy-dependencies"
description="package, output to JAR">
<mkdir dir="${dist.dir}" />
<mkdir dir="${dist.lib.dir}" />
<jar jarfile="${dist.dir}/${projectName}.jar" basedir="${build.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}" />
</manifest>
<zipfileset src="${dist.lib.dir}/dependencies-all.jar"
excludes="META-INF/*.SF" />
</jar>
</target>
<target name="clean" description="clean up">
<delete dir="${build.dir}" />
<delete dir="${dist.dir}" />
</target>
<!-- Default, run this -->
<target name="main" depends="clean, compile, jar" />
</project>
What I see with ant -v in MyBin is something along the lines of:
build-deps:
Project base dir set to: /MyBin
[ant] calling target(s) [main] in build file /MyLib/build.xml
parsing buildfile /MyLib/build.xml with URI = file:/MyLib/build.xml
Project base dir set to: /MyBin
Override ignored for property "projectName"
Override ignored for property "build.dir"
Override ignored for property "dist.dir"
Override ignored for property "dist.lib.dir"
Override ignored for property "lib.dir"
[pathconvert] Set property classpath.name =
[ant] Entering /MyLib/build.xml...
Build sequence for target(s) `main' is [clean, init, copy-dependencies, jar, main]
Complete build sequence is [clean, init, copy-dependencies, jar, main, ]
clean:
[delete] Deleting directory /MyBin/bin
[delete] Deleting directory /MyBin/bin
init:
[mkdir] Created dir: /MyBin/bin
copy-dependencies:
[ant] Exiting /MyLib/build.xml.
On your specific question:
seems like it's confusing MyBin and MyLib properties? I'm not sure how
to prevent this though.
You are using this to invoke the MyLib build:
<ant antfile="../MyLib/build.xml" target="main" />
A build invoked via the <ant> task this way by default inherits all properties from the caller, including basedir, hence the build is run in the wrong place.
Instead you could use, for example:
<ant dir="../MyLib" />
That will run build.xml in the specified directory, set the basedir property, and call the default target, which should be main if you are using a very similar buildfile for the library as you say. If you don't want to inherit properties from MyBin when executing the MyLib task, specify inheritAll=false in the task.
From the <ant> task docs for the dir attribute:
the directory to use as a basedir for the new Ant project (unless
useNativeBasedir is set to true). Defaults to the current project's
basedir, unless inheritall has been set to false, in which case it
doesn't have a default value. This will override the basedir setting
of the called project. Also serves as the directory to resolve the
antfile and output attribute's values (if any).
On Eclipse I create war files by using ant.
The issue is that in the war file isn't included the right mypropfile.properties.
The file is properly copied, but also if I use <eclipse.refreshLocal resource="projectdir" depth="infinite"/> the old file is included. I have to refresh manually the project.
For Ant I use the "Run in the same JRE as the workspace" option.
<?xml version="1.0" encoding="UTF-8"?>
<project name="MyProject" basedir=".">
<description>
My Project
</description>
<property name="workspace.dir" value="${basedir}/../../"/>
<property name="src" value="${basedir}/../src"/>
<property name="build" value="${basedir}/../build"/>
<property name="build.classes" value="${basedir}/../build/classes"/>
<property name="lib.dir" value="${basedir}/WEB-INF/lib"/>
<property name="web.dir" value="${basedir}/WEB-INF"/>
<property environment="env"/>
<property name="real.dir" value="${basedir}/real"/>
<property name="real2.dir" value="${basedir}/real2"/>
<path id="classpath.server">
<fileset dir="${env.CATALINA_HOME}/lib" includes="*.jar"/>
<pathelement path="${build.classes}"/>
</path>
<path id="classpath.app">
<fileset dir="${lib.dir}" includes="*.jar"/>
</path>
<target name="refreshResource" if="eclipse.refreshLocal">
<eclipse.refreshLocal resource="projectdir" depth="infinite"/>
</target>
<target name="clean">
<delete dir="${build}/classes"/>
<delete dir="${build}"/>
</target>
<target name="init" depends="clean, refreshResource">
<tstamp/>
<mkdir dir="${build}"/>
<mkdir dir="${build}/classes"/>
</target>
<target name="compile" depends="init">
<javac encoding="UTF8" srcdir="${src}" destdir="${build}/classes" includeantruntime="false">
<compilerarg value="-Xlint:unchecked"/>
<classpath>
<path refid="classpath.server.bin"/>
</classpath>
<classpath>
<path refid="classpath.server"/>
</classpath>
<classpath>
<path refid="classpath.app"/>
<fileset dir="${lib.dir}" includes="*.jar"/>
</classpath>
</javac>
</target>
<target name="deleteConfig">
<delete file="${src}/mypropfile.properties"/>
</target>
<target name="real" depends="deleteConfig">
<copy file="${real.dir}/realprop.properties" tofile="${src}/mypropfile.properties"/>
</target>
<target name="real2" depends="deleteConfig">
<copy file="${real2.dir}/real2prop.properties" tofile="${src}/mypropfile.properties"/>
</target>
<target name="war-real" depends="real, compile">
<input message="Warname (without .war):" addproperty="warname"/>
<war destfile="${workspace.dir}/${warname}.war" webxml="${web.dir}/web.xml">
<fileset dir="${basedir}">
<include name="**/*.*"/>
</fileset>
<classes dir="${build.classes}"/>
</war>
</target>
<target name="war-real2" depends="real2, compile">
<input message="Warname (without .war):" addproperty="warname"/>
<war destfile="${workspace.dir}/${warname}.war" webxml="${web.dir}/web.xml">
<fileset dir="${basedir}">
<include name="**/*.*"/>
</fileset>
<classes dir="${build.classes}"/>
</war>
</target>
EDIT
The target clean was wrong, so I've corrected it, but now build failed with error
BUILD FAILED ... Reference classpath.server.bin not found.
Ant doesn't care if Eclipse has refreshed the file or not. eclipse.refreshLocal is only relevant for editors and compilers inside of the IDE.
When you run the Ant build.xml, Ant copies the file in question in the real target into the source folder and compile copies it into ${build}/classes (at least it should do that). So before you create the WAR, you must make sure the compile step has done its work (i.e. look into each file to make sure that a change is visible in each copy).
What worries my is that you use different ways to access the classes:
${build}/classes
${build.classes}
${basedir}/../build/classes
So the first step should be to define a single way to locate the folder and then use this pattern everywhere.
If that doesn't solve your problem, you need to make sure Ant notices that the file has changed. Old filesystems like FAT support only timestamps which have second resolution. If you use an USB stick for your sources, it's possible to change the file and run Ant so fast that Ant thinks the file hasn't changed.
Lastly, you need to check your classpath. If one of the JAR dependencies also contains a file called mypropfile.properties, then Java resource loading can find either version.
This and other problems made me use a different solution to configure WAR files: I pass a system property with the absolute path of the config file. That way, the WAR file doesn't change when the config changes and I have full control over which config file is loaded.
I have a Java program that uses an external library, whose location is specified through the classpath. I would now like to make the Java program into a standalone jar file (so I can use my IDE for other things whilst the program is running).
How do I turn my existing .java file into an executable jar file?
I am able to make a jar file that includes the class file, manifest file, and the jar file of the library (that was specified in the classpath), but that still appears to be wrong because I get class not found errors.
Ant script for you. What you missed was the classpath generation as a string for the jar task.
<target name="all">
<property name="dir" value="yourProjectDir" />
<property name="name" value="$yourProjectName" />
<!-- clean -->
<delete dir="temp/" />
<mkdir dir="temp/bin/" />
<!-- prepare libs -->
<copy todir="temp/libs/"><fileset dir="${dir}/lbs/" /></copy>
<!-- prepare classpath -->
<pathconvert property="classpath" pathsep=" ">
<path><fileset dir="temp/libs/" /></path>
<chainedmapper><flattenmapper /><globmapper from="*" to="libs/*" /></chainedmapper>
</pathconvert>
<!-- compile -->
<javac destdir="temp/bin/" srcdir="${dir}/src/" target="1.6" source="1.6" includeAntRuntime="false">
<classpath>
<pathelement location="temp/bin/" />
<fileset dir="temp/libs/" />
</classpath>
</javac>
<!-- jar -->
<jar destfile="temp/${name}.jar" basedir="temp/bin/">
<manifest>
<attribute name="Main-Class" value="Main" />
<attribute name="Class-Path" value="${classpath}" />
</manifest>
</jar>
<!-- zip jar + libs -->
<zip destfile="${name}-${version}.zip">
<fileset dir="temp" includes="${name}.jar, libs/" />
</zip>
</target>
The following is the steps, how run stand alone App from command prompt.
1. Create a sample java file then save in particular location(like d:\sample\Hello.java.
2. open command prompt compile that java class, then create jar like Hello.jar file
3. then set classpath in Environment file(like D:\sample\Hello.jar ;
4. Now run your java class , it will work(d:sample>java Hello
I'm working on a small library for our in-company use, and have been heavily documenting it. Now I'm building my jar with the following code:
<project name="commonutils" default="compile" basedir=".">
<property name="src" location="src" />
<property name="build" location="buildDirecotry" />
<target name="compile">
<delete file="${ant.project.name}.jar" />
<mkdir dir="${build}"/>
<javac srcdir="${src}" destdir="${build}" debug="on" target="1.5">
<classpath>
<pathelement location="lib/build/server.zip" />
<pathelement path="${java.class.path}/"/>
</classpath>
</javac>
<jar basedir="${build}" destfile="${ant.project.name}.jar" />
<delete dir="${build}" />
</target>
</project>
Which works fine, it builds my jar file with all the src files in it, but when I include the jar file in another project I no-longer have any of my javadoc comments. Using JDDecompiler I cannot see the comments in the class file, although I'm not sure if its the java compiler that's stripping them or JD.
My question is: How can I build my jar file so that users who use the library will be able to see the javadoc in Eclipse.
If you include the source files in the jar (each class and java file in the same package-directory) it should work.
<target name="jar.noCompile.src">
<jar destfile="${ant.project.name}.jar">
<fileset dir="${build}"/>
<fileset dir="${src}" includes="**/*.java"/>
</jar>
</target>
AFAIK the documentation is an Eclipse feature. You have to configure it manually. In your build generate the documentation (usually into folder 'javadoc') and package it with the JAR. Once someone wants to use your library, he/she has to go into Java Build Path select libraries, add yours, click next to it to open the tree node and then double click on Javadoc location to configure it.