Creating a fat jar from an existing non-fat jar - java

One application I had been using required all classes to be in the same base hierarchy (Meaning jars could not be inside the jar or else it would fail) for whatever reason. Because I was creating my own application for others to use, I had no control over whether or not they had a fat jar or not, so I had to figure out a way to "fatten?" an already made jar.
One could use the JarFile and everytime they encountered a jar file, write the jar into its own file and do it over again; however, that feels like an absolute waste of memory and time to copy stuff over like that just to have access to the JarFile entry list
Ex: test.jar
-com.package
-main.class
-dependency1.jar
-com.depedency1
-test.class
and what was needed:
test.jar
-com.package
-main.class
-com.dependency1
-test.class

Here is the solution I made for this problem, although it doesn't handle naming collisions that well.
The readJar could be helpful for several other tasks if you want to read all the classes in a jar.
public class JarUtils extends Base {
public static File fatJar(File dest, File file) throws IOException {
finer("Generating Fat Jar:" + file);
try (JarInputStream zis = new JarInputStream(new BufferedInputStream(new FileInputStream(file)));
JarOutputStream zos = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)), zis.getManifest())) {
final byte[] buffer = new byte[1024];
readJar(file.getName(), zis, (entry, is) -> {
int len;
zos.putNextEntry(entry);
while ((len = is.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
zos.closeEntry();
}, new ArrayList<>());
zos.finish();
}
return dest;
}
private static void readJar(String jarName, JarInputStream zis, JarEntryWalker walker, List<String> checkedEntries) throws IOException {
ZipEntry entry;
if (!jarName.endsWith("/")) jarName += "/";
while ((entry = zis.getNextEntry()) != null) {
if (entry.getName().contains(".jar")) {
readJar(entry.getName(), new JarInputStream(zis), walker, checkedEntries);
} else {
String name = (entry.getName().contains("META-INF/") ? jarName : "") + entry.getName();
if (!checkedEntries.contains(name)) {
ZipEntry zipEntry = new ZipEntry(name);
zipEntry.setComment(entry.getComment());
zipEntry.setExtra(entry.getExtra());
zipEntry.setCreationTime(entry.getCreationTime());
zipEntry.setLastAccessTime(entry.getLastAccessTime());
zipEntry.setLastModifiedTime(entry.getLastModifiedTime());
walker.entry(zipEntry, zis);
checkedEntries.add(name);
}
}
}
}
}
interface JarEntryWalker {
void entry(ZipEntry zipEntry, InputStream is) throws IOException;
}

Related

Change file name in JarOutputStream

So I have been working with a Derby DB and can write to and work with the data. I'm now trying to pack it into a single archive file(jar). I can get it to pack and unpack with no issues. Well except one.
I can get it to pack into a jar but it has an extra folder depth. So if I pack up the directory that is named "December", and inside that archive I also get a "December" folder with the contents inside it. Is there a way I can remove that extra folder either in packing or unpacking?
I have tried to play with the file's name but if I mess with it I get errors saying it can't find the file after I renamed it. I tried this both before it's packed and while unpacking it. Closes I got was each file name in the main directory but all the files are 0kb. I even try to change the pack directory to "December*", but it didn't like that.
public class JarPack {
public static void pack(String name,String dir) throws FileNotFoundException, IOException{
System.out.println(dir);
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
JarOutputStream target = new JarOutputStream(new FileOutputStream(name), manifest);
add(new File(dir), target);
target.close();
}
private static void add(File source, JarOutputStream target) throws IOException{
BufferedInputStream in = null;
try
{
if (source.isDirectory())
{
String name = source.getPath().replace("\\", "/");
if (!name.isEmpty())
{
if (!name.endsWith("/"))
name += "/";
JarEntry entry = new JarEntry(name);
entry.setTime(source.lastModified());
target.putNextEntry(entry);
target.closeEntry();
}
for (File nestedFile: source.listFiles())
add(nestedFile, target);
return;
}
JarEntry entry = new JarEntry(source.getPath().replace("\\", "/"));
entry.setTime(source.lastModified());
target.putNextEntry(entry);
in = new BufferedInputStream(new FileInputStream(source));
byte[] buffer = new byte[1024];
while (true)
{
int count = in.read(buffer);
if (count == -1)
break;
target.write(buffer, 0, count);
}
target.closeEntry();
}
finally{
if (in != null)
in.close();
}
}
#SuppressWarnings("resource")
public static void unPack(String name,String dir) throws java.io.IOException {
java.util.jar.JarFile jarfile = new java.util.jar.JarFile(new java.io.File(name));
java.util.Enumeration<java.util.jar.JarEntry> enu= jarfile.entries();
while(enu.hasMoreElements()){
String destdir = dir;
java.util.jar.JarEntry je = enu.nextElement();
if (!je.getName().equals("META-INF/MANIFEST.MF")){
java.io.File fl = new java.io.File(destdir, je.getName());
if(!fl.exists()){
fl.getParentFile().mkdirs();
fl = new java.io.File(destdir, je.getName());
}
if(je.isDirectory()){
continue;
}
java.io.InputStream is = jarfile.getInputStream(je);
java.io.FileOutputStream fo = new java.io.FileOutputStream(fl);
while(is.available()>0){
fo.write(is.read());
}
fo.close();
is.close();
}
}
}
}

Delete directory in java is not deleting directory

I am deleting diretory after zipped the same. I used the following code to make zip and delete.
I am able to do zip and cant delete the folder.
Can anyone point me where I am doing mistake.
Here the code I m using
public class ZipDirectory {
public static void main(String[] a) throws Exception {
zipFolder("d:\\conf2", "d:\\conf2.zip");
}
static public void zipFolder(String srcFolder, String destZipFile) throws Exception {
ZipOutputStream zip = null;
FileOutputStream fileWriter = null;
fileWriter = new FileOutputStream(destZipFile);
zip = new ZipOutputStream(fileWriter);
addFolderToZip("", srcFolder, zip);
zip.flush();
zip.close();
delete(new File(srcFolder));
}
static private void addFileToZip(String path, String srcFile, ZipOutputStream zip)
throws Exception {
File folder = new File(srcFile);
if (folder.isDirectory()) {
addFolderToZip(path, srcFile, zip);
} else {
byte[] buf = new byte[1024];
int len;
FileInputStream in = new FileInputStream(srcFile);
zip.putNextEntry(new ZipEntry(path + "/" + folder.getName()));
while ((len = in.read(buf)) > 0) {
zip.write(buf, 0, len);
}
}
}
static private void addFolderToZip(String path, String srcFolder, ZipOutputStream zip)
throws Exception {
File folder = new File(srcFolder);
for (String fileName : folder.list()) {
addFileToZip(path + "/" + folder.getName(), srcFolder + "/" + fileName, zip);
}
}
static private void delete (File path){
if( path.exists() ) {
File[] files = path.listFiles();
for(int i=0; i<files.length; i++) {
files[i].delete();
}
}
path.delete();
}
}
Please close FileInputStream 's instance to make your deletion successful.
please add in.close() in addFileToZip() method.
On taking trace of the delete method it shows the below
07:58:12.734018754 0x2970500 mt.0 Entry >java/io/File.delete()Z Bytecode method, This = 0xfffc4810
07:58:12.734019108 0x2970500 mt.3 Entry >java/lang/System.getSecurityManager()Ljava/lang/SecurityManager; Bytecode static method
07:58:12.734019462 0x2970500 mt.9 Exit
07:58:12.734019815 0x2970500 mt.0 Entry >java/io/File.isInvalid()Z Bytecode method, This = 0xfffc4810
On deleting the file, security manager refuse to delete the file because of already existing file descriptor associated to that. Close the Fileinput stream to avoid this condition.
You are not colling the ZipOutputStream object that you have created. Try replacing following method in you code. Here I have created ZipOutputStrean object in try-with-resource and it worked
static public void zipFolder(String srcFolder, String destZipFile) throws Exception {
try(ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(destZipFile)))
{
addFolderToZip("", srcFolder, zip);
zip.flush();
zip.close();
delete(new File(srcFolder));
}
}

Copy directory and files from java project resource

I need to copy the directory "src" that is located in my java project, as a common resource. This "src" folder contains other subfolders and files, so I need them to be copied as well. How can I achieve something like this??
The main problem I'm facing is that I can't retrieve the absolute path of my "src" folder.
A solution would be to copy file by file but their are too much and I would like to find a better solution
Thank you
EDIT:
When the user click on "Generate" button, my app ask to the user a target location where to generate some files. This target location is where I want to copy my "src" folder with all its children. The "src" folder, as sad above, is located in my java project main folder.
If you want to extract the sources from a jar file you could start with this short example
public class UnZip {
public static void main(String argv[]) {
String destDir = "/tmp/";
String sourceJar = "your_src.jar";
try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(sourceJar)))) {
ZipEntry zipEntry;
while ((zipEntry = zis.getNextEntry()) != null) {
File newDestination = new File(destDir + zipEntry.getName());
if (zipEntry.isDirectory()) {
unzipDir(newDestination);
} else {
unzipFile(newDestination, zis);
}
}
} catch (IOException ex) {
System.err.println("input file coud not be read " + ex.getMessage());
}
}
private static void unzipFile(File file, final ZipInputStream zis) {
System.out.printf("extract to: %s - ", file.getAbsoluteFile());
if (file.exists()) {
System.out.println("already exist");
return;
}
int count;
try (BufferedOutputStream dest = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE)) {
while ((count = zis.read(BUFFER, 0, BUFFER_SIZE)) != -1) {
dest.write(BUFFER, 0, count);
}
dest.flush();
System.out.println("");
} catch (IOException ex) {
System.err.println("file could not be created " + ex.getMessage());
}
}
private static void unzipDir(File dir) {
System.out.printf("create directory: %s - ", dir);
if (dir.exists()) {
System.out.println("already exist");
} else if (dir.mkdirs()) {
System.out.println("successful");
} else {
System.out.println("failed");
}
}
static final int BUFFER_SIZE = 2048;
static byte[] BUFFER = new byte[BUFFER_SIZE];
}
I resolved the problem creating a zip archive of the app I need to generate. Then from my project I unzip this archive taken from resource folder and after that I open the stream of the java file I need to modify and I edit it.
This is the code, similar to SubOptimal's solution
UnzipUtility unzipper = new UnzipUtility();
InputStream inputStream = getClass()
.getResource("/template/template.zip").openConnection()
.getInputStream();
unzipper.unzip(inputStream, parentFolder.getCanonicalPath());
The UnzipUtility class is from this example: http://www.codejava.net/java-se/file-io/programmatically-extract-a-zip-file-using-java

How to copy file from directory to another Directory in Java

I am using JDK 6.
I have 2 folders names are Folder1 and Folder2.
Folder1 have the following files
TherMap.txt
TherMap1.txt
TherMap2.txt
every time Folder2 have only one file with name as TherMap.txt.
What I want,
copy any file from folder1 and pasted in Folder2 with name as TherMap.txt.If already TherMap.txt exists in Folder2, then delete and paste it.
for I wrote the following code.but it's not working
public void FileMoving(String sourceFilePath, String destinationPath, String fileName) throws IOException {
File destinationPathObject = new File(destinationPath);
File sourceFilePathObject = new File(sourceFilePath);
if ((destinationPathObject.isDirectory()) && (sourceFilePathObject.isFile()))
//both source and destination paths are available
{
//creating object for File class
File statusFileNameObject = new File(destinationPath + "/" + fileName);
if (statusFileNameObject.isFile())
//Already file is exists in Destination path
{
//deleted File
statusFileNameObject.delete();
//paste file from source to Destination path with fileName as value of fileName argument
FileUtils.copyFile(sourceFilePathObject, statusFileNameObject);
}
//File is not exists in Destination path.
{
//paste file from source to Destination path with fileName as value of fileName argument
FileUtils.copyFile(sourceFilePathObject, statusFileNameObject);
}
}
}
I call the above function in main()
//ExternalFileExecutionsObject is class object
ExternalFileExecutionsObject.FileMoving(
"C:/Documents and Settings/mahesh/Desktop/InputFiles/TMapInput1.txt",
"C:/Documents and Settings/mahesh/Desktop/Rods",
"TMapInput.txt");
While I am using FileUtils function, it showing error so I click on error, automatically new package was generated with the following code.
package org.apache.commons.io;
import java.io.File;
public class FileUtils {
public static void copyFile(File sourceFilePathObject,
File statusFileNameObject) {
// TODO Auto-generated method stub
}
}
my code not showing any errors,even it's not working.
How can I fix this.
Thanks
Use Apache Commons FileUtils
FileUtils.copyDirectory(source, desc);
Your code isn't working because in order to use the ApacheCommons solution you will have to download the ApacheCommons library found here:
http://commons.apache.org/
and add a reference to it.
Since you are using JRE 6 you can't use all the NIO file utilities, and despite everyone loving Apache Commons as a quick way to answer forum posts, you may not like the idea of having to add that utility on just to get one function. You can also use this code that uses a transferFrom method without using ApacheCommons.
public static void copyFile(File sourceFile, File destFile) throws IOException {
if (!destFile.exists()) {
destFile.createNewFile();
}
FileInputStream fIn = null;
FileOutputStream fOut = null;
FileChannel source = null;
FileChannel destination = null;
try {
fIn = new FileInputStream(sourceFile);
source = fIn.getChannel();
fOut = new FileOutputStream(destFile);
destination = fOut.getChannel();
long transfered = 0;
long bytes = source.size();
while (transfered < bytes) {
transfered += destination.transferFrom(source, 0, source.size());
destination.position(transfered);
}
} finally {
if (source != null) {
source.close();
} else if (fIn != null) {
fIn.close();
}
if (destination != null) {
destination.close();
} else if (fOut != null) {
fOut.close();
}
}
}
When you upgrade to 7, you will be able to do the following
public static void copyFile( File from, File to ) throws IOException {
Files.copy( from.toPath(), to.toPath() );
}
reference:
https://gist.github.com/mrenouf/889747
Standard concise way to copy a file in Java?

Copy and rename file on different location

I have one file example.tar.gz and I need to copy it to another location with different name
example _test.tar.gz. I have tried with
private void copyFile(File srcFile, File destFile) throws IOException {
InputStream oInStream = new FileInputStream(srcFile);
OutputStream oOutStream = new FileOutputStream(destFile);
// Transfer bytes from in to out
byte[] oBytes = new byte[1024];
int nLength;
BufferedInputStream oBuffInputStream = new BufferedInputStream(oInStream);
while((nLength = oBuffInputStream.read(oBytes)) > 0) {
oOutStream.write(oBytes, 0, nLength);
}
oInStream.close();
oOutStream.close();
}
where
String from_path = new File("example.tar.gz");
File source = new File(from_path);
File destination = new File("/temp/example_test.tar.gz");
if(!destination.exists())
destination.createNewFile();
and then
copyFile(source, destination);
It doesn't work. The path is correct. It prints that the file exists. Can anybody help me?
Why to reinvent the wheel, just use FileUtils.copyFile(File srcFile, File destFile) , this will handle many scenarios for you
I would suggest Apache commons FileUtils or NIO (direct OS calls)
or Just this
Credits to Josh - standard-concise-way-to-copy-a-file-in-java
File source=new File("example.tar.gz");
File destination=new File("/temp/example_test.tar.gz");
copyFile(source,destination);
Updates:
Changed to transferTo from #bestss
public static void copyFile(File sourceFile, File destFile) throws IOException {
if(!destFile.exists()) {
destFile.createNewFile();
}
FileChannel source = null;
FileChannel destination = null;
try {
source = new RandomAccessFile(sourceFile,"rw").getChannel();
destination = new RandomAccessFile(destFile,"rw").getChannel();
long position = 0;
long count = source.size();
source.transferTo(position, count, destination);
}
finally {
if(source != null) {
source.close();
}
if(destination != null) {
destination.close();
}
}
}
There is Files class in package java.nio.file. You can use the copy method.
Example: Files.copy(sourcePath, targetPath).
Create a targetPath object (which is an instance of Path) with the new name of your file.

Categories

Resources