Can ant replace text in files inside a jar/ear/war? - java

I want to build my ear file once and then use ant to change some settings in application.xml, property files etc.
Is there way to do this with ant?
[edit] Just found this
How do I modify a file in a jar file using ANT?

The only way you can modify a file inside your jar or ear is to use the <unzip> task, use the <replace> task to modify the fields in the file, and then rezip the file back up with either the <zip> or <jar>/<ear> task.
There are several ways you can handle this without having to unzip and rezip your ear/jar/war files:
The preferred method is to setup your application server, so it can find your properties outside of the ear itself. It is also possible to configure the application.xml file not to use relative directories when specifying locations instead of specifying locations from the root of the machine. By removing embedded environment information from your ear, you can use the same earfile on all of your environments.
We, unfortunately, are unable to do the above and must provide separate ear files for each environment. We use Jenkins as our continuous build server. When Jenkins does our builds, we build multiple ears, one for each environment, at the same time. This way, as we move from Dev to QA to STAGE to Production, we can at least refer to the same build number. We compile once, then use the AntContrib <foreach> task to ear up the earfile with the correct properties file settings and the correct application.xml file. We use <filterset> in our <copy> task to modify the properties and application.xml as we build the ear.

You can do something like this
<zip destfile="tmp.jar" >
<zipfileset src="lib/myjar.jar" excludes="org/example/My*.class" />
<zipfileset dir="bin" includes="org/example/My*.class" />
</zip>
<move file="tmp.jar" tofile="lib/myjar.jar"/>
In this example we create a tmp.jar using myjar.jar as the source but excluding all classees beginning My in the org/example directory. We then add our new version in from the bin directory. We then replace the jar file with our new version.

Related

Ant jar task - Include files not underneath basedir

I am trying to write an ant target that will create a jar based on a bunch of java files and some properties files, but am having trouble including the properties files the way the project is set up currently.
These are the ant targets I have:
1 - Compile the java souce files from the "myjar.src" folder and put the resulting classes into a "myjar.classes" folder. Once this is done copy all non .java files from "myjar.src" to "myjar.classes".
2 - Create the jar using the "jar" command using basedir = ${myjar.classes} and tell it to include everything.
These are the ant targets I want:
1 - Compile the java souce files and put the resulting classes into a "myjar.classes" folder. Only .java files are included
2 - Create the jars using the "jar" command using basedir = ${myjar.classes.location} but also include the .properties and .xml files from "myjar.src."
The key difference is I want the properties and xml files from "myjar.src" to be included when I package up the classes in "myjar.classes" using basedir = ${myjar.classes} - How do I include these fies when they are not underneath "myjar.classes"?
This is the ant target I want to modify:
<CreateManifest title="myjar classes etc"/>
<jar
destfile="${myProject.build.jars.dir}\ta_test_driver.jar"
basedir="${myjar.classes}"
manifest="${manifest}">
<include name="**"/>
</jar>
The reason I am not just using a directory one level up for basedir is that none of the other jar creation calls in the project do that, and I am hesitant to change that for just this one. I did try to do that, but had trouble specifying the right directory. (There are other jars that use a similar directory structure, and I don't want to interfere with them either now or in the future.) I was just wondering if these is a better way to do this? Thanks very much.
I managed to figure this out from here: How to include file in Jar through Ant at specific location
This was my modified ant call in the end:
<CreateManifest title="myjar classes etc"/>
<jar
destfile="${myProject.build.jars.dir}\ta_test_driver.jar"
basedir="${myProject.classes}\ta_test_driver"
manifest="${manifest}">
<include name="**"/>
<zipfileset dir="${myjar.src}"
includes="**/*.xml, **/*.properties, **/*.gif"
/>
</jar>
Notice the zipfileset tag.

Launch4J - how to attach dependent jars to generated exe

I have a simple java project, which requires external jars.
I build this with netbeans and after Clean and Build command, I can find in dist directory the following structure:
-myApp.jar
-lib/
library1.jar
library2.jar
typical, I would say.
Now, I'd like to distribute myApp.jar with dependent libraries as one exe.
Is this possible? I am trying to use Launch4J. In the GUI I create the config file, there are some options in cp section
<cp>lib/swing-layout-1.0.4.jar</cp>
but it seems to be classpath, and it is the only place I can refer to my extra jars.
After exe file is created, I can't find dependend libs in the exe (exe can be opened with winrar) and thus my application crashes.
How can I make the exe file properly then?
Thanks for your help.
As it often happens being unable to solve the problem I published it on StackOverflow ... and pretty soon after publishing the question I got an idea.
So the answer to my question is:
Put all the dependent jars into one main jar.
It took me some time to find info how can I do that.
To help people I decided to publish detailed instruction here - they are based on Netbeans 7.4.
Following article from http://mavistechchannel.wordpress.com/2010/08/17/how-to-build-a-single-jar-file-with-external-libs/ I created the ant script that build one-jar-app for me.
I could then manually create exe via Launch4J
I then decided that I want more automated task, and I did that, Ant builds exe for me (via Launch4J)
Then I realized that I must do "clean and build" before my automated task (in point 2)/ I decided that I want clean and build to be done automatically before the exe build
Putting all together I am attaching my ant build script consisting of points 1,2,3:
It is required to edit build.xml and put the content found below before "project" end tag
<target name="package-for-launch4j" depends="clean,compile,jar">
<property name="launch4jexe.dir" location="C:\Program Files (x86)\Launch4j" />
<taskdef name="launch4j"
classname="net.sf.launch4j.ant.Launch4jTask"
classpath="${launch4jexe.dir}/launch4j.jar
:${launch4jexe.dir}/lib/xstream.jar" />
<property name="launch4j.jar.name" value="MyAppJarName"/>
<property name="launch4j.dir" value="exe"/>
<property name="launch4j.jar" value="${launch4j.dir}/${launch4j.jar.name}.jar"/>
<echo message="Packaging ${application.title} into a single JAR at ${launch4j.jar}"/>
<delete dir="${launch4j.dir}"/>
<mkdir dir="${launch4j.dir}"/>
<jar destfile="${launch4j.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="${launch4j.jar}">
<zipfileset src="${launch4j.dir}/temp_final.jar"
excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
</zip>
<delete file="${launch4j.dir}/temp_final.jar"/>
<launch4j configFile="misc/l4j-myapp.xml" />
</target>
then in Netbeans rightclick on the build.xml and choose:
Run Target / Other Targets / package-for-launch4j
exe file is ready in exe folder :-)
When you are converting your .jar file
Go to classpath tab
Check custom classpath
On main class select your .jar from your dist folder after building the project
On the classpath textarea add your libraries, you add them right below that
textarea writting the full path to the lib (of course including the
lib, ie "C:\folder\lib\file.jar")
I have spent hours on this issue. So here is my contribution.
The problem here: how to sucessfully put your external jars that your .JAR program needs INSIDE the .exe that you generate.
We assume that you already, and correctly, configured the external jars on eclipse/netbeans and ALL WORK FINE with the command: java -jar yourprogram.jar.
So the real problem is how to ensure that this .EXE file will contain the external jars, otherwise it will not work properly.
1) First of all, you need to forget Launch4J and anyother program.
2) Install JSmooth, I recommend that you use the windows version.
3) On the left menu there is a button "Application". Click on it.
4) You will see a tab "Classpath" panel. Click on the plus (+) and add your external .jar's files. And that is it!!
Don't forget to put your .jar application marking checkbox "use am embedded jar" and choose the main class properly. It will work.
I also faced the same issue while migrating my .jar to exe. I also had many dependent libraries as well. So These were the steps I performed :
Download and Install launch4j.
Open your project in netbeans. Clean and build the project.
Make sure you have a folder named 'dist' in the project directory. It will have your jar files with lib folder(containing the dependent libraries).
Open launch 4j.
Create output file in the dist folder. For example : OutputFile : D:******\My_App\dist\my_application.exe
Browse your jar file in the next row. For example : Jar : D:******\My_App\dist\my_application.jar
Go to classpath tab. Tick CustomClasspath. Press browse icon, and browse to your jar file which is located in the dist folder.
Specify Min Jre version in the JRE tab.
Save the configration.
Build the wrapper(by clicking the settings icon)
Your exe file will be generated in the dist folder.
Thats it :)
Putting different links of places which had helped me
How to include all dependent Jars within a single non-executable jar?
How can I create an executable JAR with dependencies using Maven?
http://www.mkyong.com/java/how-to-make-an-executable-jar-file/
and most importantly
http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html
quick tut
http://www.mkyong.com/java/how-to-add-your-manifest-into-a-jar-file/
To include external libraries with Launch4j you have to have the ".jar" files of the external libraries near your .exe (mine are just in the same folder) then in "Classpath" you put in the path to the .jar files into "Edit Item: "
In launch4j go to the classpath tab. Tick the custom classpath field. In the edit field, enter the full path of each jar you want included and press accept. When finished type just the name of the main class into the separate field (ie MyProg). All the jars will now be included in the exe.
PS I have all the jars in the same directory. I'm using version 3.12

Generate jar files from required project

I have a Java project composed by many jar files, let's say the application directory is composed by the following jar files:
File_1.jar
File_2.jar
...
File_N.jar
In addition there is a file executable file which makes use of the jar files
For some reason I am responsible of modifying two jar files, les's say File_1.jar and File_2.jar with the particularity that File_2.jar requieres the project associated to File_1.jar in the build path.
I generated the jar files without problems, so rigth now the application has the following jar files:
File_1_modif.jar
File_2_modif.jar
File_1.jar
File_2.jar
...
File_N.jar
Everything went fine, I mean I could execute the application with my modifications but my problem came up when I removed the old jar files(File_1.jar, File_2.jar) from the application directory. Actually I do not get an error message but the application does not behave as it did before removing the old jar files, I was wondering if I should generate the file File_1_modif.jar in an special way due to the fact that this file is requiered to build the file File_2_modif.jar. Could anybody advice my some tries to do in order to fix my problem?
It appears your project is still referencing the File_1.jar and File_2.jar. You may need to locate how the jar files are being loaded into your project, if in eclipse or netbeans, you'll need to add the File_1_modif and file_2_modif files to your build path and remove the file_1.jar and file_2.jar. If you are using ant build, you will have to include the .jar files using directives like this:
<fileset dir="${lib}">
<include name="file_1_modif.jar"/>
<include name="file_1_modif.jar"/>
<exclude name="**/${ant.project.name}.jar"/>
</fileset>

Configure Eclipse to pre-bundle App Engine classes into a single JAR for faster warm-up

After some discussion with a colleague from another company that also uses App Engine, he told me that he managed to cut down his app warm up time from ~15 seconds to ~5 seconds using the following steps:
Configure Eclipse to bundle classes produced during compilation into a single JAR file.
Configure Eclipse to upload this single JAR file instead of hundreds (or thousands) of separate Java classes during App Engine deployment.
He argues that during instance warm up, since the instance need to load only a single bundled JAR file instead of thousands of separate classes, the warm up would be significantly faster. Any thoughts or opinions about this?
I would definitely like to try it by myself, but I don't have enough Eclipse-mojo to know how to configure such steps. Does anyone know how to configure Eclipse or the Google Plugin for Eclipse to do the steps outlined above? (And have the deployed apps successfully runs in App Engine, of course)
Thank You,
What we did in Eclipse was:
In Project Properties > Java Build Path (Source tab) change output folder from war/WEB-INF/classes to something else, e.g. bin (I believe Eclipse complained about this once)
In Project Properties > Builders add a new builder (I used type "program builder" and pointed it to the jar executable in my JDK, but as others mentioned an Ant builder would work too). Obviously you should configure this builder to take its input from wherever you decided to write your class files to, and output to something like war/WEB-INF/lib/myclasses.jar
You can configure the jar builder to execute automatically as class files change (which in turn are usually automatically recompiled as your source files change).
There are some downsides, though. For some reason the google eclipse plugin gets confused by your changing the output directory of the java compiler. This means you will have to manually point to the war directory when deploying, and I believe you'll have to manually copy some GAE jars into the war/WEB-INF/lib folder.
I don't know how (or if) you can integrate it into eclipse, but it's fairly trivial to do with ant:
<import file="${appengine.sdk.dir}/config/user/ant-macros.xml" />
<target name="deploy">
<delete dir="${staging.dir}" />
<mkdir dir="${staging.dir}" />
<copy todir="${staging.dir}">
<fileset dir="war">
<exclude name="WEB-INF/classes/**" />
<exclude name="WEB-INF/appengine-generated/**" />
</fileset>
</copy>
<jar destfile="${staging.dir}/WEB-INF/lib/classes.jar" basedir="${classes.dir}" />
<appcfg action="update" war="${staging.dir}" />
</target>
I will add that I did not experience a 3X reduction in app startup time. I posted some experimental numbers in this thread:
https://groups.google.com/d/msg/google-appengine/dStBW4wIemY/K69f9ufDiN0J
What I found is that instead of varying wildly from 20-45s, it made my app consistently load in 20s. It has not subsequently remained this consistent, but I still jar my classes as a standard part of deployment now.
One way this can be achieved if by doing the deployment through Ant, as described in: https://developers.google.com/appengine/docs/java/tools/ant
Next you can modify the ant build.xml file to call the ant command for building the jar file. Just before the actual deployment you can either delete or move the compiled artifacts away. The build jar-file should be placed in the WAR/WEB-INF/lib folder.
Drawback of this solution is that you have to deploy through the build.xml, i.s.o. through the appengine eclipse plugin.
As stated in an earlier answer, the App Engine SDK supports packaging WEB-INF/classes into a jar file, which will end up in WEB-INF/lib/_ah_webinf_classes-0000.jar. You can activate this
using the appcfg tool with the option --enable_jar_classes.
using the Google Plugin for Eclipse by configuring the properties of either your WAR or EAR project: Project properties > Google App Engine > Deployment > "Package WEB-INF/classes as a jar"
For me, on App Engine 1.9.4, this resulted in only a minor improvement in instance spin-up (about 5-10 %, if any).
Note that this will package all files in WEB-INF/classes (not only .class ones). Following the change, I got an error message during instantiation about not being able to read the logging.properties file anymore; probably because the new jar file hasn't been read at that time:
Unable to read the java.util.logging configuration file, WEB-INF/classes/logging.properties
As a workaround, I changed the path in appengine-web.xml to WEB-INF/logging.properties and configured the maven-war-plugin to copy the file to that location:
<webResources>
<resource>
<directory>lib</directory>
<targetPath>WEB-INF/lib</targetPath>
</resource>
<resource>
<!-- Workaround: During GAE deployment, all files in WEB-INF/classes will be packaged into WEB-INF/lib/_ah_webinf_classes-0000.jar,
which prevents the logging.properties referenced in appengine-web.xml from being read. -->
<directory>src/main/resources</directory>
<includes>
<include>logging.properties</include>
</includes>
<targetPath>WEB-INF</targetPath>
</resource>
</webResources>
Note that as of version 1.7.4:
You can now package all the WEB-INF/classes/* classes into jar files. This can be done via the new
--enable_jar_classes option in the appcfg tools. By default, this option is not set.
http://code.google.com/p/googleappengine/wiki/SdkForJavaReleaseNotes
I think the easiest way to do this would be to have a separate project that you write all the code in as well as store any other dependencies. When you're ready to deploy, Eclipse's export to runnable JAR file can repack all the dependencies and your classes into a single JAR file, which you would then use as your one dependency in your deployable Google App Engine project which would basically consist of any config files in the WEB-INF folder as well as other static resources + the one big JAR file.
Alternatively to the eclipse export, you can set that up as a Maven or Ant build which can also do the repacking.
He argues that during instance warm up, since the instance need to load only a
single bundled WAR file instead of thousands of separate classes, the warm up
would be significantly faster. Any thoughts or opinions about this?
I doubt it. WAR is just a ZIP file, which gets unpacked when it's deployed on the server. So there is an additional step, meaning the process can be equally fast (if unpacked when uploaded) or slower (if unpacked when instance is spun up).

Updating files in a WAR/JAR reguardless of their date

I have an ANT task that uses the jar task to update a few files inside of a previously built war. [The files are processed between compilation of a WAR and deliverable.] How can I get ANT to update all of the files I've specified to be updated? There is an attribute for jar called update = "[...]" that will allow you to either force a new creation of the Jar file or (add new files/"update existing ones if deemed necessary").
An example:
Java class(es) are compiled
Jars are created
A .war is created
A script runs to modify the Jars
The .war needs to update the Jars that were modified [the Jars are a subset of all of the files in the war]
The problem I'm running into is that the Jars that are being instructed to be updated aren't getting updated in the final step. The log claims that the just processed Jars are "out of date." Is there a way I can force the update to happen? If I switch "jar [...] update" back to false (which its by default) the correct Jar files get placed there, but the rest of the files in the war don't.
Note I realize that this could be moved to produce the WAR after everything is done. But this is not an option for me.
The ant task in question:
<target name="(the 'Rewaring task')" depends="step-3">
<echo>Adding modifed jars to war</echo>
<jar destfile="${output.war.dir}/existing.war" update="true">
<zipfileset dir="${output.jar.dir}/modded-jars" prefix="folder" />
</jar>
</target>
More specifically, the problem I'm having is with the "update" behavior.
It looks like the Jar task is correct.
Are you sure the files being updated have the correct Date modified time - that is what is actually compared in the zipfileset/update option?
I've seen this issue when signing Jars [and had the preservelastmodified option set to "true" (default : false)].
To fix it, you should only need to change the signing option.

Categories