How do I append a line to a file unzipped by Ant? - java

I have a default log4j properties file to which I want to append an application specific configuration. The file is contained (with other files) within a .zip file. I use Ant to unzip the contents of the zip (including the log4j properties). I want to append the line when the unzip happens. Is this possible?
<unzip dest="some.dest">
<fileset refid="some.fileset" />
<!-- Append a line to 'log4j.properties` file -->
</unzip>
Maybe the solution is just to echo after I've unzipped.

You can use the Ant "echo" task with the "append" flag:
<echo file="log4j.properties" append="true">${line.separator}</echo>
Echo task doc here for further reference:
http://ant.apache.org/manual/Tasks/echo.html

No need to unzip the whole zipfile, use
if log4j.properties resides in rootdirectory of zip :
<project>
<!-- unzip log4j.properties only -->
<unzip src="foo.zip" dest=".">
<patternset>
<include name="log4j.properties" />
</patternset>
</unzip>
<!-- put new key in or overwrite if already existing -->
<propertyfile file="log4j.properties">
<entry key="log4j.logger.com.foobar.xyz" value="ERROR" />
</propertyfile>
<!-- update zip with modified log4j.properties -->
<zip destfile="foo.zip" update="true">
<fileset dir="." includes="log4j.properties" />
</zip>
</project>
else if log4j.properties resides in any subfolder of zip :
<project>
<!-- unzip log4j.properties only -->
<unzip src="foo.zip" dest=".">
<patternset>
<include name="**/log4j.properties" />
</patternset>
</unzip>
<fileset dir="." includes="**/log4j.properties" id="foo"/>
<!-- put new key in or overwrite if already existing -->
<propertyfile file="${toString:foo}">
<entry key="log4j.logger.com.foobar.xyz" value="ERROR" />
</propertyfile>
<!-- update zip with modified log4j.properties -->
<zip destfile="foo.zip" update="true">
<fileset dir="." includes="${toString:foo}" />
</zip>
</project>

Related

Spring boot :unable to open nested entry 'WEB-INF/lib-provided/ecj-3.12.3.jar'

I am trying to obfuscate the spring boot application.
Package : war
I was following the ant script approach with yguard
Link : http://codeaweso.me/2009/02/obfuscating-a-webapp-war-file-with-yguard-and-ant/
Project structure:
main.war
a.jar
b.jar
and many more third party dependencies.
I can see we have successfully obfuscate the required third party dependencies jar file.
Also we have kept the other libraries unobfuscated and uncompressed but some how they are getting compressed.
Exception that we getting is
java -jar webapp_obf.war
Exception in thread "main" java.lang.IllegalStateException: Failed to get nested archive for entry WEB-INF/lib-provided/ecj-3.12.3.jar
at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:109)
at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchives(JarFileArchive.java:87)
at org.springframework.boot.loader.ExecutableArchiveLauncher.getClassPathArchives(ExecutableArchiveLauncher.java:72)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:49)
at org.springframework.boot.loader.WarLauncher.main(WarLauncher.java:59)
Caused by: java.io.IOException: Unable to open nested jar file 'WEB-INF/lib-provided/ecj-3.12.3.jar'
at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:252)
at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:237)
at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:104)
... 4 more
Caused by: java.lang.IllegalStateException: Unable to open nested entry 'WEB-INF/lib-provided/ecj-3.12.3.jar'. It has been compressed and nested jar files must be stored without compression. Please check the mechanism used to create your executable jar file
at org.springframework.boot.loader.jar.JarFile.createJarFileFromFileEntry(JarFile.java:285)
at org.springframework.boot.loader.jar.JarFile.createJarFileFromEntry(JarFile.java:260)
at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:248)
... 6 more
<project xmlns='antlib:org.apache.tools.ant'>
<!-- prepare a temporary directory in which the war file is expanded and
obfuscated -->
<tempfile property="unwar.dir" destdir="obfuscation/"
deleteonexit="false" />
<mkdir dir="${unwar.dir}" />
<unwar src="../target/webapp.war"
dest="${unwar.dir}" />
<!-- create a jar of webapp classes (required by yguard) for obfuscation -->
<jar destfile="${unwar.dir}/WEB-INF/lib/webapp.jar"
whenempty="fail">
<zipfileset dir="${unwar.dir}/WEB-INF/classes" />
<zipfileset dir="${unwar.dir}/META-INF" />
</jar>
<!-- <delete dir="${unwar.dir}/WEB-INF/classes" /> -->
<!-- create a fileset of internal libraries to be obfuscated -->
<fileset dir="${unwar.dir}/WEB-INF/lib" id="internal.lib.set">
<include name="first.jar" />
<include name="second.jar" />
</fileset>
<!-- move the internal libraries to a temporary directory and make a fileset
out of them -->
<tempfile property="obfuscation.dir"
destDir="obfuscation/temp" deleteonexit="false" />
<mkdir dir="${obfuscation.dir}" />
<move todir="${obfuscation.dir}">
<fileset refid="internal.lib.set" />
</move>
<!-- create a jar of web.xml (required by yguard) for obfuscation -->
<jar destfile="${obfuscation.dir}/web.xml.jar" whenempty="fail">
<zipfileset dir="${unwar.dir}/WEB-INF" includes="web.xml" />
</jar>
<!-- <delete file="${unwar.dir}/WEB-INF/web.xml" /> -->
<!-- make a fileset of all jars to be obfuscated -->
<fileset dir="${obfuscation.dir}" includes="*.jar"
id="in-out.set" />
<!-- make a fileset of the remaining libraries, these are not obfuscated -->
<path id="external.lib.path">
<fileset dir="${unwar.dir}/WEB-INF/lib" includes="*.jar" />
<fileset dir="${unwar.dir}/WEB-INF/lib-provided" includes="*.jar" />
</path>
<taskdef name="yguard" classname="com.yworks.yguard.YGuardTask"
classpath="yguard.jar" />
<yguard>
<inoutpairs>
<!-- these filesets are inputs to be obfuscated -->
<fileset refid="in-out.set" />
</inoutpairs>
<externalclasses refid="external.lib.path" /> <!-- external libs, not obfuscated -->
<rename>
<adjust replaceContent="true">
<include name="web.xml" /> <!-- modified to reference the obfuscated Servlet -->
</adjust>
<keep>
<!-- classes, packages, methods, and fields which should not obfuscated
are specified here -->
<class classes="none" methods="none" fields="none" >
<patternset>
<include name="com.Application" />
</patternset>
</class>
</keep>
</rename>
</yguard>
<!-- move our newly obfuscated classes back into the lib area -->
<move todir="${unwar.dir}/WEB-INF/lib">
<fileset dir="${obfuscation.dir}" includes="*_obf.jar" />
</move>
<!-- unjar the adjusted web.xml -->
<unzip dest="${unwar.dir}/WEB-INF/"
src="${unwar.dir}/WEB-INF/lib/web.xml_obf.jar">
<patternset includes="web.xml" />
</unzip>
<delete>
<fileset dir="${unwar.dir}/WEB-INF/lib"
includes="web.xml*.jar" />
</delete>
<!-- rebuild the war file -->
<war destfile="webapp_obf.war" basedir="${unwar.dir}"
needxmlfile='false'>
<manifest>
<attribute name="Main-Class"
value="org.springframework.boot.loader.WarLauncher" />
<attribute name="Start-Class"
value="com.Application" />
<attribute name="Spring-Boot-Classes"
value="WEB-INF/classes/" />
<attribute name="Spring-Boot-Lib" value="WEB-INF/lib/" />
<attribute name="Build-Jdk" value="1.8.0_171" />
</manifest>
</war>
</project>
Any suggestion why we might the compression exception.
When you are attempting to build the jar then only provide below options.
maven-assembly-plugin
<archive>
<compress>false</compress>
</archive>
Also we can provide the same in spring-boot maven plugin.

Deployed jar can't find the main class while using Apache Ant

I built a jar file using the following build.xml below:
<project name="sampleproject">
<!--Source Directory-->
<property name="src.dir" value="src" />
<property name="deploy.name" value="samplejar.jar" />
<property name="lib.dir" value="./lib" />
<property name="deploy.dir" value="target" />
<property name="compile.dir" value="target/classes" />
<!--Define directory name for third party libraries-->
<property name="jar-all" location="${lib.dir}" />
<!--Fileset is a group of files, here **/* matches all jar files across all directory levels-->
<fileset id="jars" dir="${jar-all}">
</fileset>
<!--Setting the classpath-->
<path id="classpath">
<fileset refid="jars" />
<pathelement location="." />
<pathelement location="${src.dir}" />
<pathelement location="${compile.dir}" />
</path>
<path id="cp">
<fileset refid="jars" />
</path>
<pathconvert property="classpath.path" refid="cp" pathsep=" " dirsep="/">
<map from="${jar-all}" to="lib" />
</pathconvert>
<echo>${classpath}</echo>
<!--Clean-->
<target name="clean">
<delete dir="${deploy.dir}" />
</target>
<!--Jar-->
<target name="jar" depends="compile">
<jar destfile="${deploy.dir}/${deploy.name}.jar" basedir="${compile.dir}">
<manifest>
<attribute name="Main-Class" value="main.java.Main" />
<attribute name="Class-Path" value="${classpath.path}" />
</manifest>
<include name="${src.dir}/META-INF/persistence.xml" />
<restrict>
<not>
<or>
<name name="**/*.RSA" />
<name name="**/*.SF" />
<name name="**/*.DSA" />
</or>
</not>
<archives>
<zips>
<fileset dir="lib" includes="**/*.jar" />
</zips>
</archives>
</restrict>
</jar>
</target>
<!--Compile-->
<target name="compile">
<mkdir dir="${compile.dir}" />
<javac includeantruntime="false" srcdir="${src.dir}" destdir="${compile.dir}">
<classpath refid="classpath" />
</javac>
</target>
</project>
My folder structure is like this:
.
./.settings
./bin
./bin/main
./bin/main/java
./bin/META-INF
./lib
./lib/aws
./lib/hibernate
./src
./src/main
./src/main/java
./src/META-INF
./target
./target/classes
./target/classes/main
./target/classes/main/java
My MANIFEST.MF
Created-By: 1.8.0_45-b14 (Oracle Corporation)
Built-Date: ${TODAY}
Main-Class: main.java.Main
Class-Path: lib/activation.jar lib/aws/aws-java-sdk-1.3.2.jar lib/aws/
httpclient-4.1.1.jar lib/aws/httpcore-4.4.1.jar lib/aws/mail-1.4.3.ja
r lib/aws/stax-1.2.0.jar lib/aws/stax-api-1.0.1.jar lib/commons-beanu
tils-1.8.3.jar lib/commons-codec-1.4.jar lib/commons-configuration-1.
8.jar lib/commons-httpclient-3.0.1.jar lib/commons-io-2.1.jar lib/com
mons-lang-2.4.jar lib/commons-logging-1.1.1.jar lib/hibernate/antlr-2
.7.7.jar lib/hibernate/c3p0-0.9.1.jar lib/hibernate/commons-collectio
ns-3.2.1.jar lib/hibernate/dom4j-1.6.1.jar lib/hibernate/hibernate-c3
p0-4.0.1.Final.jar lib/hibernate/hibernate-commons-annotations-4.0.1.
Final.jar lib/hibernate/hibernate-core-4.0.1.Final.jar lib/hibernate/
hibernate-entitymanager-4.0.1.Final.jar lib/hibernate/hibernate-jpa-2
.0-api-1.0.1.Final.jar lib/hibernate/javassist-3.15.0-GA.jar lib/hibe
rnate/jboss-logging-3.1.0.CR2.jar lib/hibernate/jboss-transaction-api
_1.1_spec-1.0.0.Final.jar lib/jackson-all-1.9.5.jar lib/log4j-1.2.16.
jar lib/mail.jar lib/mongo-2.8.0.jar lib/morphia-0.99.jar lib/mysql-c
onnector-java-5.1.12-bin.jar lib/mysql-connector-java-5.1.25-bin.jar
lib/org.json.jar lib/ymmi-utilities-2.0.jar
Whenever I run the jar produced it gives me, Error: Could not find or load main class main.java.Main.
Also, I want to include lib folder & it's subfolders as well since it contains the libraries required to run the jar.
Before it gave me, No Persistence provider for EntityManager named but I solved that by using <include name="${src.dir}/META-INF/persistence.xml" />. Any where I am might wrong?
Your problem is the <include> element in the jar task.
I executed your script and the Main.class file is not added to the jar.
Replace the
<include
by
<fileset dir="${src.dir}" includes="META-INF/persistence.xml" />
This way it will still add all files located in your ${compile.dir}.
Are you new to java ? Normally you put your source files in src/main/java and resources in src/main/resources.
These folders are the root folder of your classpath meaning: If your project classes would be in package org.myproject, than you would put the Main.java file in src/main/java/org/myproject/Main.java
In your build script you than set the property "src.dir" to "src/main/java".
With all resources (=files which do not need to be compiled but must be available in the jar) in src/main/resources the fileset element in the jar task could just be:
<fileset dir="scr/main/resources"/>
This way all resource files you plan to add to your project will automatically added to your jar.
Why do I tell you this ? If one day you would think of using maven instead of ant, maven expects the sources to be in src/main/java and resources in src/main/resources. (https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html)
Regarding you other dependant jars:
Now you include all jars twice: you add the content of them using the <archives> element within the jar and you define them in the Class-Path attribute of the manifest.mf file.
If you really like to build a single jar containing all dependant files, you don't need to specify the manifest attribute Class-Path.
If you want your jar file containing only your files and deliver the dependant jars separately: remove the <archives> element within the jar task.

Ant Nested Update

This is a follow up question related to my question from yesterday:
Ant Ear Update Without Full Exploding Ear
I'm using Ant 1.8.2 and am able to update files in an ear, using the example I made in the provided link above.
I have a war file inside my ear file, and I'm hoping to see if there is a way to do a nested update (e.g. update a file in the war that is in the ear).
My other option is to extract the war, update the war, then update the ear with the updated war. If there is a way to do the nested update, I think it would save me time, as my war file is pretty big.
The following is my alternative approach POC, in case anyone is interested. I will use this if we cannot find a "nested" update.
<property name="ear.file1" value="file1.ear"/>
<property name="war.file1" value="war1.war"/>
<property name="war.file" value="war.war"/>
<property name="war.file.backup" value="warBk.war"/>
<property name="text.file1" value="1.txt"/>
<property name="text.file2" value="2.txt"/>
<property name="xml.application1" value="application.xml"/>
<target name="clean">
<delete file="${ear.file1}"/>
<delete file="${war.file}"/>
<delete file="${war.file.backup}"/>
</target>
<target name="run">
<!-- Our war file contains 1.txt, allows us add 2.txt and verify updates properly -->
<copy file="${war.file1}" tofile="${war.file}"/>
<!-- simple ear that will be updated -->
<ear earfile="${ear.file1}" appxml="${xml.application1}">
<fileset dir="." includes="${text.file1}"/>
<fileset dir="." includes="${war.file}"/>
</ear>
<!-- Backup war, for comparision purposes -->
<move file="${war.file}" tofile="${war.file.backup}" overwrite="true" />
<!-- Extact the war we just added -->
<unzip dest="." src="${ear.file1}" overwrite="true" >
<patternset>
<include name="${war.file}" />
</patternset>
</unzip>
<!-- Update the war by adding a file -->
<war destfile="${war.file}" update="true">
<fileset dir="." includes="${text.file2}"/>
</war>
<!-- Update the ear with our updated war -->
<ear earfile="${ear.file1}" appxml="${xml.application1}" update="true">
<fileset dir="." includes="${war.file}"/>
</ear>
</target>
<target name="main" depends="clean,run"/>

Ant task to filter JARs for zip file and manifest

I have an Ant file where I am creating a zip file and a manifest for several JAR files. Both the zip and the manifest reference the same libraries, but in slightly different ways. If possible I would like to combine the references to the files instead of explicitly writing them twice and hoping the references in both tasks sync up. Below is an example of what I am currently doing.
<target name="zip" depends="default">
<zip destfile="${dist.dir}/${project.name}_v${project.version}.zip">
<zipfileset prefix="lib" dir="lib/Dom4J" includes="*.jar"/>
<zipfileset prefix="lib" dir="lib/GSON" includes="*.jar"/>
<zipfileset prefix="lib" dir="lib/Guava" includes="*.jar"/>
<!-- ... A bunch more (Note I don't want everything
in the lib directory, just certain subfolders
within the lib directory which are explicitly
listed here like GSON. -->
</zip>
</target>
<target name="createManifest">
<!-- Hard code the classpath by hand and hope
they sync up with the zip task -->
<property name="mfClasspath"
value="dom4j-1.6.1.jar gson-2.1.jar guava-11.0.2.jar" />
<!-- Code to use the mfClasspath when creating the manifest
omitted for brevity -->
</target>
What I would ideally like to have is a fileset of some sort that I could reference in both tasks. Note that the manifest does not contain any folders/paths. The manifest only contains the JAR files found within the directories mentioned in the zip task.
You are right. You can accomplish this with a common fileset shared by both the zip and createManifest tasks. For the zip task, copy the files to a temporary location and then zip them up.
For the createManifest task, use character replacement to remove the folders from the paths. Character-replacement strategies are discussed in "Replacing characters in Ant property." If you have Ant-Contrib, you can simplify the character-replacement algorithm below by using the PropertyRegex Ant task.
<project default="all">
<fileset id="jars" dir=".">
<include name="lib/Dom4J/dom4j-1.6.1.jar" />
<include name="lib/GSON/gson-2.1.jar" />
<include name="lib/Guava/guava-11.0.2.jar" />
</fileset>
<target name="zip">
<copy todir="tmp.dir" flatten="true">
<fileset refid="jars" />
</copy>
<zip destfile="example.zip">
<zipfileset dir="tmp.dir" prefix="lib" />
</zip>
<delete dir="tmp.dir" />
</target>
<target name="createManifest">
<property name="jars.property" refid="jars" />
<echo message="${jars.property}" file="some.tmp.file" />
<loadfile property="mfClasspath" srcFile="some.tmp.file">
<filterchain>
<tokenfilter>
<replaceregex pattern="(?:[^;/]+/)+?([^;/]+\.jar)"
replace="\1" flags="g" />
<replacestring from=";" to=" " />
</tokenfilter>
</filterchain>
</loadfile>
<delete file="some.tmp.file" />
</target>
<target name="all" depends="zip, createManifest">
<echo message="$${jars.property} = "${jars.property}"" />
<echo message="$${mfClasspath} = "${mfClasspath}"" />
</target>
</project>
When I executed the above Ant buildfile, the following was output to the console:
Buildfile: /workspace/StackOverflow/build.xml
zip:
[zip] Building zip: /workspace/StackOverflow/example.zip
[delete] Deleting directory /workspace/StackOverflow/tmp.dir
createManifest:
[delete] Deleting: /workspace/StackOverflow/some.tmp.file
all:
[echo] ${jars.property} = "lib/Dom4J/dom4j-1.6.1.jar;lib/GSON/gson-2.1.jar;lib/Guava/guava-11.0.2.jar"
[echo] ${mfClasspath} = "dom4j-1.6.1.jar gson-2.1.jar guava-11.0.2.jar"
BUILD SUCCESSFUL
Total time: 675 milliseconds
Also, example.zip contained the following entries:
lib/dom4j-1.6.1.jar
lib/gson-2.1.jar
lib/guava-11.0.2.jar

Convert to jar while retaining classpath

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

Categories

Resources