Batch Deleting Files While Performing a Copy/Merge - java

I have 2 files, "launcher.jar" and "camStudio.jar" that I need merged. I decided to try to do this using batch with the code:
copy /b launcher.jar + camStudio.jar file.jar
However, the resulting "file.jar" only contains the contents of "camStudio.jar". How can I prevent the files in "launcher.jar" from being deleted?

Combining the contents of two .jar files is a little more complicated than just calling copy from the command line. Rather than a normal directory, .jar files are a type of compressed file, so you need special utilities to manipulate them. Fortunately these tools come with the standard JKD.
The JDK comes with the utility jar that is unsurprisingly used for manipulating .jar files. It's usage is described as this:
Usage: jar {ctxui}[vfmn0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files ...
Options:
-c create new archive
-t list table of contents for archive
-x extract named (or all) files from archive
-u update existing archive
-v generate verbose output on standard output
-f specify archive file name
-m include manifest information from specified manifest file
-n perform Pack200 normalization after creating a new archive
-e specify application entry point for stand-alone application
bundled into an executable jar file
-0 store only; use no ZIP compression
-M do not create a manifest file for the entries
-i generate index information for the specified jar files
-C change to the specified directory and include the following file
If any file is a directory then it is processed recursively.
The manifest file name, the archive file name and the entry point name are
specified in the same order as the 'm', 'f' and 'e' flags.
Example 1: to archive two class files into an archive called classes.jar:
jar cvf classes.jar Foo.class Bar.class
Example 2: use an existing manifest file 'mymanifest' and archive all the
files in the foo/ directory into 'classes.jar':
jar cvfm classes.jar mymanifest -C foo/ .
Relevant commands for combining two .jar files are x and c. Even with this, combining the .jar files takes more than a line or two, so I put together this .bat files to automate it.
:: Pass one or more .jar files as command line arguments
:: Combine_Jar [file1] [file2 ...]
:: Combine_Jar Test.jar
:: Combine_Jar Test.jar Test2.jar Test3.jar
#echo off & setlocal enabledelayedexpansion
set "jarDir=%cd%"
set "newJar="
set "folders="
pushd %temp%
for %%a in (%*) do (
call :extract %%a
set "newJar=!newJar!_%%~na_"
)
set "tempDirs=!newJar:_=^"!"
set "tempDirs=%tempDirs:^"^"=^" ^"%"
set "newJar=!newJar:~1,-1!.jar"
set "newJar=!newJar:__=_!"
if exist "!newJar!" del /Q "!newJar!"
jar cf "!newJar!" %tempDirs%
for %%a in (%*) do call rd /s /q "%%~na"
move /Y "!newJar!" "%jarDir%" > nul
popd
exit /B
:extract
set "tempDir=%~n1"
if exist "%tempDir%" (
rd /s /q "%tempDir%"
)
md "%tempDir%"
pushd "%tempDir%"
jar xf "%jarDir%\%~1"
popd
exit /B
It will all jar files passed as arguments into a single jar files.

Related

How to run `jdeprscan` on an EAR

The jdeprscan tool determines lists all deprecated and non-existing dependencies. It can run on classes, directories and on a JAR.
But how to run it on an EAR ?
Inspired by https://stackoverflow.com/a/57217414/698168, I explode the EAR into JARs using the following script (Windows) :
rem remove previous run
rd /s /q ear
rem extract the EAR
"C:\Program Files\7-Zip\7z" x -oear *.ear
rem extract the WAR
cd ear
"C:\Program Files\7-Zip\7z" x -owar *.war
rem unify JAR from EAR and WAR
copy war\WEB-INF\lib\*.jar lib
rem make JAR with the classes
cd war\WEB-INF\classes
rem "C:\Program Files\7-Zip\7z" a -r my-app.jar
"C:\Program Files\Java\jdk-11\bin\jar" cvf my-app.jar -C . .
rem Note: using 7zip to create the JAR may lead to errors when running jdeprscan, thus we are using the jar command
copy my-app.jar ..\..\..
rem return to origin
cd ..\..\..
rem unpack all libraries...
cd lib
"C:\Program Files\7-Zip\7z" x -aoa -oclasses *.jar
rem .. and repack them as a fat JAR
cd classes
rem "C:\Program Files\7-Zip\7z" a -r 00lib.jar
"C:\Program Files\Java\jdk-11\bin\jar" cvf 00lib.jar -C . .
rem duplicate the fat JAR and make some cleaning
copy 00lib.jar ..\00lib.jar
copy 00lib.jar ..\01lib.jar
cd ..
rd /s /q classes
rem return to origin
cd ..\..
Note that this script does not use the librairies from the JEE Server (i.e. all the Maven librairies with scope "provided" will be reported as error: cannot find class by jdeprscan).
Then I generate a jdeprscan report using the following command :
"C:\Program Files\Java\jdk-11\bin\jdeprscan" --for-removal --verbose --class-path ear\lib\*.jar ear\my-app.jar > deprscan.log 2>&1
You can then inspect the jdeprscan.log file. The classes that are not found may not exist in the newest Java version (such as 11) or may be present in the JEE modules. A missing class looks like the following (BASE64Encoder is not provided anymore by Java 11 but is used by ChecksumHelper):
Processing class oracle/spatial/security/ChecksumHelper...
error: cannot find class sun/misc/BASE64Encoder
In the best case, you can find the JAR name above in the log file (e.g. Jar file my-lib-2.3.4.jar), otherwise you will need to determine the library from the class name.
Note: all the above was designed with the idea to migrate Java 8 to Java 11.

Extract several of jar in one command

I have folders with lots (20) of jar files. Is there a way to extract all those jars in one command in the terminal instead doing it one by one?
I'm using a MAC.
A simple solution.- Get all the jars and extract it
find ./ -name "*.jar" -exec jar -xf {} \;
You can use this from the folder in which all your jars are -
jar {ctxu}[vfm0Mi] [jar-file] [manifest-file] [-C dir]
Options:
-c create new archive
-t list table of contents for archive
-x extract named (or all) files from archive
-u update existing archive
-v generate verbose output on standard output
-f specify archive file name
-m include manifest information from specified manifest file
-0 store only; use no ZIP compression
-M do not create a manifest file for the entries
-i generate index information for the specified jar files
-C change to the specified directory and include the following file
refer the documentation for explaination - http://docs.oracle.com/javase/tutorial/deployment/jar/unpack.html
you may user *.jar in place of jar-file to extract all
This should extract in same location where the jars are present.
jar xvf *.jar

Omitting base directories when JARing multiple selective files

I have the following directorie structure:
base\
dir1\
dir11\
RequiredFile.class
NonRequiredFile.class
dir12\
AnotherRequiredFile.class
AnotherNonRequiredFile.class
As part of a makefile actions, I would like to archive RequiredFile.class and AnotherRequiredFile.class in the same JAR file, and omit the base/dir directories. The final output should be a single JAR file so that unpacking the JAR file would create the following structure:
dir11\
RequiredFile.class
dir12\
AnotherRequiredFile.class
I know that this can be done using -C flag in the bin/jar command, but it reuqires me to add -C dir1 before each one of the input file. The above example only contains two, but later there would be more and i wold like to avoid commands such as jar -C dir FILE -C dir FILE2 -C dir FILE3 ..... -C dir FILEn
Is there a way to use the -C flag for all input files?
Thanks
Assuming you have all your class files filenames in a Makefile variable already. Provided you are using gnu make, you can then use the patsubst function to achieve that:
CLASSFILES := RequiredFile.class AnotherRequiredFile.class
DIR := dir1
JAR_CMD = $(patsubst %.class, -C $(DIR) %.class, $(CLASSFILES))

Create .jar files deterministically (identical each time)

I use the jar command to build jar files. While trying to cache the jar files using md5 signatures, I found that jars built from the exact same sources had different md5 signatures.
Upon closer inspection, I found that every time the jar was created the contents were exactly the same (diff -qr was empty). It turns out that the timestamp of creation is encoded in the jar file which throws off the md5 signature. Other people have discovered the same here.
There is even a blog post on how to create jar files identically each time with maven. However, I want a simple solution using the command line using readily available commands such as jar and zip (may have to do this on a server without install permissions), possibly leading to the same "functional" jar as I'm currently getting using jar command.
EDIT: For my purpose, it also suffices to quickly find the md5 so that it is the same across builds, even if the jars are not identical. The only way I found so far is to extract the files in the jar and to md5 all component files. But I'm afraid that is slow for bigger jars and is going to defeat the purpose of caching them to avoid building them in the first place. Is there a better and faster solution?
The main issue is jar command always create META-INF\MANIFEST.MF with current time.
The file time is saved in zip entry header. This is why MD5 value is different even all file content in jar remain the same:
the different zip entry headers produce different zip file.
For jar command, the only solutionis is option -M: not to create a manifest file for the entries.
Jar command always create META-INF\MANIFEST.MF with current time. Zip stores files with timestamp and file attributes due to which sha256 or MD5 will be different for two artifacts.
We need to make sure that created, last modified, accessed timestamp and file attributes are always same of all files which are required to create jar or zip.
I have created below script which can take a jar or zip file and make it deterministic by making timestamp constant and setting the right compression level and offset.
#!/bin/bash
usage() {
echo "Usage : ./createDeterministicArtifact.sh <zip/jar file name>"
exit 1
}
info() {
echo "$1"
}
strip_artifact() {
if [ -z ${file} ]; then
usage
fi
if [ -f ${file} -a -s ${file} ]; then
mkdir -p ${file}.tmp
unzip -oq -d ${file}.tmp ${file}
find ${file}.tmp -follow -exec touch -a -m -t 201912010000.00 {} \+
if [ "$UNAME" == "Linux" ] ; then
find ${file}.tmp -follow -exec chattr -a {} \+
elif [[ "$UNAME" == CYGWIN* || "$UNAME" == MINGW* ]] ; then
find ${file}.tmp -follow -exec attrib -A {} \+
fi
cd ${file}.tmp
zip -rq -D -X -9 -A --compression-method deflate ../${file}.new .
cd -
rm -rf ${file}.tmp
info "Recreated deterministic artifact: ${file}.new"
else
info "Input file is empty. Please validate the file and try again"
fi
}
file=$1

Comparing two .jar files

How do I compare two .jar files?
Both of them have compiled .class files.
I want the difference in terms of method changes, etc.
JAPICC, sample usage:
japi-compliance-checker OLD.jar NEW.jar
Sample reports for log4j: http://abi-laboratory.pro/java/tracker/timeline/log4j/
PkgDiff, sample usage:
pkgdiff OLD.jar NEW.jar
See sample report for args4j.
Clirr, sample usage:
java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
If you select two files in IntellijIdea and press Ctrl + Dthen it will show you the diff. I use Ultimate and don't know if it will work with Community edition.
Rename .jar to .zip
Extract
Decompile class files with jad
Recursive diff
Extract each jar to it's own directory using the jar command with parameters xvf. i.e. jar xvf myjar.jar for each jar.
Then, use the UNIX command diff to compare the two directories. This will show the differences in the directories. You can use diff -r dir1 dir2 two recurse and show the differences in text files in each directory(.xml, .properties, etc).
This will also show if binary class files differ. To actually compare the class files you will have to decompile them as noted by others.
Create a folder and create another 2 folders inside it like old and new. add relevant jar files to the folders. then open the first folder using IntelliJ. after that click whatever 2 files do you want to compare and right-click and click compare archives.
I use to ZipDiff lib (have both Java and ant API).
Here is my script to do the process described by sje397:
#!/bin/sh
# Needed if running on Windows
FIND="/usr/bin/find"
DIFF="diff -r"
# Extract the jar (war or ear)
JAR_FILE1=$1
JAR_FILE2=$2
JAR_DIR=${PWD} # to assign to a variable
TEMP_DIR=$(mktemp -d)
echo "Extracting jars in $TEMP_DIR"
EXT_DIR1="${TEMP_DIR}/${JAR_FILE1%.*}"
EXT_DIR2="${TEMP_DIR}/${JAR_FILE2%.*}"
mkdir ${EXT_DIR1}
cd ${EXT_DIR1}
jar xf ${JAR_DIR}/${JAR_FILE1}
jad -d . -o -t2 -safe -space -b -ff -s java -r **/*.class
cd ..
mkdir ${EXT_DIR2}
cd ${EXT_DIR2}
jar xf ${JAR_DIR}/${JAR_FILE2}
jad -d . -o -t2 -safe -space -b -ff -s java -r **/*.class
cd ..
# remove class files so the diff is clean
${FIND} ${TEMP_DIR} -name '*.class' | xargs rm
# diff recursively
${DIFF} ${EXT_DIR1} ${EXT_DIR2}
I can run it on Windows using GIT for Windows. Just open a command prompt. Run bash and then execute the script from there.
Use Java Decompiler to turn the jar file into source code file, and then use WinMerge to perform comparison.
You should consult the copyright holder of the source code, to see whether it is OK to do so.
In Linux/CygWin a handy script I use at times is:
#Extract the jar (war or ear)
cd dir1
jar xvf jar-file1
for i in `ls *.class`
do
javap $i > ${i}.txt #list the functions/variables etc
done
cd dir2
jar xvf jar-file2
for i in `ls *.class`
do
javap $i > ${i}.txt #list the functions/variables etc
done
diff -r dir1 dir2 #diff recursively
If you are using IntelliJ IDEA or Android Studio, add your jar files to a project under the libs folder.
Then select the both jar files, right click then select "Compare Archives"
use java decompiler and decompile all the .class files and save all files as project structure .
then use meld diff viewer and compare as folders ..
Here's an aparently free tool http://www.extradata.com/products/jarc/
Please try http://www.osjava.org/jardiff/ - tool is old and the dependency list is large. From the docs, it looks like worth trying.
This application may be what you need, works great and display a simple GUI showing differences. Try Jarcomp

Categories