I'm encountering a strange issue in an ANT file I use for building a Java app. When generating the jar file, eventually I include resource files (images, fonts and config files) in the JAR using zipfileset, like this:
<zipfileset dir="src/res" prefix="res"/>
<zipfileset dir="src/res/images" prefix="res/images" />
<zipfileset dir="src/res/images/Bubbles" prefix="res/images/Bubbles"/>
<zipfileset dir="src/res/images/Clocks" prefix="res/images/Clocks"/>
<zipfileset dir="src/config" prefix="res/config"/>
<zipfileset dir="src/ontology" prefix="res/ontology"/>
To mantain original structure, that looks like this:
res
|-images
| |-Bubbles
| |-Clocks
|-fonts
|-config
|-ontology
Within the JAR, I'm using the prefix parameter in zipfileset. I'm getting duplicated images in res/images and triple images (3 copies of the same image) in any of the res/images/Bubbles and res/images/Clocks folders, which, in the other hand, are 2 and 3 depth levels respectively. res/config and res/ontology are correct, no duplicated files there...a screenshot to see what I mean:
I forgot to mention, but obviously, I only have one instance of each image in every folder. Any ideas what is causing this behaviour?
Regards,
Alex
ant actually does exactly what you told it do. You told him to:
pack all the files under src/res and to map them to res.
pack all the files under src/res/images and to map them to res/images
pack all the files under src/res/images/Bubbles and to them under res/images/Bubbles
Now let's assume that you have a src/res/images/Bubbles/activity_bubble_orange.png files. That file is contained in the first zipfileset, the second zipfileset and the third zipfileset. Ergo it will be packed three times.
To do what you want you need to do a single <zipfileset dir="src/res" prefix="res" /> but filter the contents using includes/excludes filters.
See here: http://ant.apache.org/manual/Types/zipfileset.html where it say that is a type of fileset. and here: http://ant.apache.org/manual/Types/fileset.html to see how you specify includes/excludes filter for a fileset.
You can use the following attribute to include the whole path without hardcoding each sub-directory.
includes="*/.*"
<zipfileset src="examples.zip" includes="**/*.html" prefix="docs/examples"/>
In above example, I'm including *.html recursively.
Sukhbir Dhillon
Addteq
Related
I have a multi-module native Netbeans Java EE project. In it I have a Java Class Library project that is used by multiple other projects which in turn are packaged into the root .ear project.
I'm adding the "build timestamp" and the "build user" attributes to a custom manifest using the library's build.xml:
<target name="-post-jar">
<jar destfile="${dist.jar}" update="true">
<manifest>
When I "clean and build" the root project, each project that refers the library calls:
<ant antfile="${call.script}" target="jar">
And my -post-jar target is called multiple times. This wouldn't be a problem, but sometimes the second invocation of the <jar> task fails with Unable to rename old file (probably due to Netbeans scanning the files in background, but I can't tell for sure).
There are repeating pairs of Building jar and Updating jar messages in Ant's output. However, if I remove my -post-jar target, the second invocation of the jar target does nothing, because it thinks that the jar is up to date and I see only one Building jar message.
How do I mark the updated jar up to date, so the second invocation of the jar target does nothing?
There's a github repo that demonstrates the problem.
I haven't found a way to not re-generate the manifest every time, but I found a way to make the generated file look the same as the zipped file (and we know that the <jar> task doesn't repack when the contents are the same).
Instead of updating the zipped manifest in -post-jar I now update the source file in -pre-jar. This way the final version of the manifest is zipped and since its contents don't change during build, subsequent <jar> invocations update nothing.
It worth mentioning that before adding the attributes Main-Class, Profile, etc. the build-impl.xml of Netbeans creates an empy manifest template, if the user doesn't provide a valid path in the manifest.file= property. The addition happens after -pre-jar, however the existence of the user-provided manifest is checked much earlier, during the init target and the result is saved to the manifest.available property.
My manifest template is not a static file. It contains the "build timestamp" and the "build user" attributes. Therefore the file doesn't exist during the init target, so I had to add the following line at the beginning of my build.xml:
<property name="manifest.available" value="true"/><!-- It will be available by the time we need it -->
Secondly, manifest.file still has to be set and I set it in project.properties (there's no UI for that setting yet and I wonder how it would behave in the presence of the variable in path)
manifest.file=${build.dir}/manifest.tmp
Next, I overwrite the manifest template in the -pre-jar target:
<tstamp>
<format property="current.time" pattern="HH:mm:ss" locale="de,DE"/>
</tstamp>
<target name="-pre-jar" >
<manifest file="${manifest.file}">
<attribute name="MyApp-built-time" value="${current.time}"/>
<attribute name="MyApp-built-by" value="${user.name}"/>
</manifest>
After that, the new problem became obvious: the timestamp was different for each invocation of
<ant antfile="mylib/build.xml" target="jar">
in the multi-module project and Ant had to repack the jar with the new timestamp in the manifest. I solved this by defining the timestamp property in every project's build.xml. Although the properties are not inherited due to inheritall="false", Netbeans allows for overcoming that:
<property name="transfer.current.time" value="${current.time}"/>
This mechanism is broken in Java EE projects, but the workaround is simple:
<presetdef name="ant">
<!-- workaround transfer.* not working in Java EE projects -->
<ant>
<propertyset>
<propertyref prefix="transfer."/>
<mapper from="transfer.*" to="*" type="glob"/>
</propertyset>
</ant>
</presetdef>
I have an Ant zip task that looks like this:
<zip basedir="${workspace.dir}"
destfile="${build.output.dir}/test.zip"
includes="${eLibrary}/bin/com/**,
${Common}
excludes="${eLibrary}/lib,
${eLibrary}/src"
>
</zip>
The eLibrary folder has a structure similar to:
`--bin
`--com
`--lib
`--src
I'd like to have the zip file look like this:
`--eLibrary
`--com
`--Common
When the zip is created though it has the following structure:
`--eLibrary
`--bin
`--com
`--Common
I've tried various types of include statements, but they all include the bin folder:
includes="${eLibrary}/bin/**
includes="${eLibrary}/bin/com/**
includes="${eLibrary}/bin
includes="${eLibrary}/bin/com
Changing the basedir won't work because I also need the Common folder included. Any help would be appreciated. Thanks.
You can check the zip task documentation for examples on how to achieve this. The below snippet uses a zipfileset nested element which maps the included entries to the directories inside the archive.
<zip destfile="${build.output.dir}/test.zip">
<zipfileset dir="${eLibrary}/bin/com" prefix="${eLibrary}/com"/>
<zipfileset dir="${Common}" prefix="${Common}"/>
</zip>
I am stuck in a very common problem.
I am plugging my jar (which has many dependencies on third party vendor) into an application server lib directory. If I just copy my jar along with its dependencies into server lib then server classpath becomes to long and hence server is not able to work. Therefore I want to package this Jar with all its dependencies in a single jar so that server's classpath doesn't become too long. I found on various forums that there is a utility to do this i.e. OneJar. But this utility works on executable jar. In my case, my final jar will not be executable.
Also I tried ZIPFileSetGroup utility provided by ANT but that is causing security issues with Manifest file.
Can you please help me in resolving this issue?
Thanks!
If you use Maven to build, you can use the maven dependency plugin and use the copy-dependency task. It will copy all dependencies into your jar file when it creates it.
If you manually add the jars to your jar file, then you need to make sure your jar file has a Manifest.mf file in it and specify the main class and classpath inside of that.
Manifest-Version: 1.0
Main-Class: com.mypackage.MainClass
Class-Path: my.jar log4j.jar
Another option may be to build an .ear file, that is usually how you see enterprise apps or a .war file for web apps when they package specific jar files with them. It sounds like you are using a server, so one of those formats may be a better fit for you.
Using zipgroupfileset in the jar task in ANT is the easiest approach.
<jar destfile="MyApplication.jar" filesetmanifest="mergewithoutmain">
<zipgroupfileset dir="lib" includes="*.jar" />
<!-- other options -->
<manifest>
<attribute name="Main-Class" value="Main.MainClass" />
</manifest>
</jar>
Note the filesetmanifest flag set to mergewithoutmain merges everything but the Main section of the manifests.
Signed jars are causing the SecurityException which need to be handled manually. If any classes associated with signed jars verify the signature on the jar as a whole then those will fail at runtime. Digest signatures against a particular file will be added to the manifest without a problem. Since problem is your classpath getting too large you may not be able to bundle all the jars into a single jar but merge most of them making the CLASSPATH manageable.
There is also : http://code.google.com/p/jarjar/
Create target directory with all dependent jars. Next move 10 jars into a temp directory and keep moving the jars in batches of 10 and each time try to create the single jar from that group. When you get the security exception you can isolate which one is causing the problem. Try divide-and-conquer approach. If you have 300 jars then only have to do this 30 times.
When you say
child process picks up classpath from server/lib directory
is this a process that is under your control? If the parent process were to specify the classpath just as
server/lib/*
(i.e. a literal *) then the target java process will enumerate the jar files in the lib directory itself - they do not all need to be named on the classpath.
But if the parent process is explicitly enumerating server/lib/*.jar to build the -cp value then you could take advantage of the fact that the Class-Path in a JAR manifest takes effect even if a JAR is not "executable". You could use a stanza like this to create a manifest-only JAR file
<!-- location of your 300 dependency JAR files, file1.jar ... file300.jar -->
<property name="lib.dir" location="lib" />
<fileset id="dependencies" dir="${lib.dir}" includes="*.jar" />
<pathconvert property="manifest.classpath" dirsep="/" pathsep=" "
refid="dependencies">
<map from="${lib.dir}" to="myapp" />
</pathconvert>
<jar destfile="myapp-manifest.jar">
<manifest>
<attribute name="Class-Path" value="${manifest.classpath}" />
</manifest>
</jar>
This will produce a JAR file named myapp-manifest.jar whose manifest contains
Class-Path: myapp/file1.jar myapp/file2.jar ... myapp/file300.jar
You put this file into server/lib and the 300 dependencies into a new directory server/lib/myapp. Now the generated -cp will include just one file (myapp-manifest.jar) but the resulting java process will have all the 300 myapp JAR files available to it.
How does one go about creating two Jars from one project source folder? Is that possible, or must I create another project? My project uses Ant right now to generate one Jar. For example, say I want to split up the class files like this:
Jar 1:
com.myproject.Foo
com.myproject.Bar
Jar 2:
com.myproject.FooBar
com.myproject.BarFoo
com.myproject.FooBarFoo
...
See http://ant.apache.org/manual/Tasks/jar.html. You just have to use filesets or includes/excludes inside your jar task to include only the files you want in each jar:
<target name="makeJars">
<jar destfile="jar1.jar"
basedir="classes"
includes="com/myproject/Foo.class, com/myproject/Bar.class"/>
<jar destfile="jar2.jar"
basedir="classes"
includes="com/myproject/FooBar.class, com/myproject/BarFoo.class, com/myproject/FooBarFoo.class" />
</target>
I am currently doing this:
<jar update="yes"
jarfile="${pwd}/dist/${release}_installer.jar">
<zipfileset src="${pwd}/dist/app.jar" includes="com/izforge/izpack/panels/**"/>
<zipfileset src="${pwd}/dist/app.jar" includes="com/xyz/img/logo.png"/>
</jar>
My existing installer JAR gets updated to include the files as needed, extracted from the app JAR.
So far, so good.
However, I want to modify the behaviour such that the path of the image file is different than what is being copied from:
Currently:
com/izforge/izpack/panels/MyIzPanel.class
com/xyz/img/logo.png
What I want:
com/izforge/izpack/panels/MyIzPanel.class
blah/img/logo.png
So I need to copy the files, but use <zipfileset> and <jar> in such a way that I can modify the directory structure.
Is there a way to do this, apart from unzipping the entire contents copying file and then zipping it back up again?
EDIT:
Link to earlier related question: ant task to remove files from a jar
You can use the fullpath attribute:
<zipfileset src="${pwd}/dist/app.jar"
includes="com/xyz/img/logo.png" fullpath="blah/img/logo.img"/>
If you need to copy several files you may want to have a look at the prefix attribute, e.g.:
<zipfileset src="${pwd}/dist/app.jar"
includes="**/*.png" prefix="blah/img"/>
In order to modify the directory structure within the archive on the fly you can use the task in combination with <mappedresources>, eg:
<jar file="target.jar" update="true">
<mappedresources>
<zipfileset src="source.jar">
<include name="com/xyz/img/*.png"/>
</zipfileset>
<mapper type="glob" from="com/xyz/img/*.png" to="bla/img/*.png" />
</mappedresources>
</jar>
You should probably look into zipgroupfileset as explained here.