Create JAR of the program itself - java

I have a program that needs to be able to create an executable JAR-file of itself, but I'm unfortunately having some trouble making it work. This is the method I'm currently using to create the JAR:
public static void createJar() throws IOException {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, "JarTest");
JarOutputStream jos = null;
try {
String jarPath = System.getProperty("user.dir") + "/Test.jar";
jos = new JarOutputStream(new FileOutputStream(jarPath), manifest);
}
catch (IOException e) {e.printStackTrace();}
ArrayList<String> fileList = new ArrayList<String>();
String codeDir = System.getProperty("user.dir") + "/bin/jartest/";
Files.list(Paths.get(codeDir)).forEach(entry -> {
fileList.add(((Path)entry).toString());
});
int len = 0;
byte[] buffer = new byte[1024];
for(String file : fileList ) {
//create JarEntry
JarEntry je = new JarEntry(file);
je.setComment("Creating Jar");
je.setTime(Calendar.getInstance().getTimeInMillis());
System.out.println(je);
jos.putNextEntry(je);
//write the bytes of file into jar
InputStream is = new BufferedInputStream(new FileInputStream(file));
while((len = is.read(buffer, 0, buffer.length)) != -1)
jos.write(buffer, 0, len);
is.close();
jos.closeEntry();
System.out.println("Done");
}
jos.close();
}
When I execute this no errors happen and I do get a JAR file called "Test.jar" generated in my Eclipse project folder. But when I open the JAR the .class files are not there, instead I see this:
I know that it's finding the .class files since the line System.out.println(je); successfully prints out the absolute path of each of them, so why aren't they getting put into the JAR?

It solved itself when I changed these two lines:
String jarPath = System.getProperty("user.dir") + "/Test.jar";
String codeDir = System.getProperty("user.dir") + "/bin/jartest/";
To these:
String jarPath = "Test.jar";
String codeDir = "bin/jartest/";
I can now see the .class files inside the JAR.

Related

Getting strange structure file when zipping a directory using Java

I wanted to zip a directory with files and subdirectories in it. I did this and worked fine but I am getting and unusual and curious file structure (At least I see it that way).
This is the created file: When I click on it, I see an "empty" directory like this: but when I unzip this I see this file structure (Not all the names are exacly as they are showed in the image below):
|mantenimiento
|Carpeta_A
|File1.txt
|File2.txt
|Carpeta_B
|Sub_carpetaB
|SubfileB.txt
|Subfile1B.txt
|Subfile2B.txt
|File12.txt
My problem somehow is that the folder "mantenimiento" is where I am zippping from (the directory which I want to zip) and I dont want it to be there, so when I unzip the just created .zip file I want it with this file structure (which are the files and directories inside "mantenimiento" directory): and the other thing is when I click on the .zip file I want to see the files and directories just like the image showed above.
I dont know what's wrong with my code, I have searched but haven't found a reference to what my problem might be.
Here's my code:
private void zipFiles( List<File> files, String directory) throws IOException
{
ZipOutputStream zos = null;
ZipEntry zipEntry = null;
FileInputStream fin = null;
FileOutputStream fos = null;
BufferedInputStream in = null;
String zipFileName = getZipFileName();
try
{
fos = new FileOutputStream( File.separatorChar + zipFileName + EXTENSION );
zos = new ZipOutputStream(fos);
byte[] buf = new byte[1024];
int len;
for(File file : files)
{
zipEntry = new ZipEntry(file.toString());
fin = new FileInputStream(file);
in = new BufferedInputStream(fin);
zos.putNextEntry(zipEntry);
while ((len = in.read(buf)) >= 0)
{
zos.write(buf, 0, len);
}
}
}
catch(Exception e)
{
System.err.println("No fue posible zipear los archivos");
e.printStackTrace();
}
finally
{
in.close();
zos.closeEntry();
zos.close();
}
}
Hope you guys can give me a hint about what I am doing wrong or what I am missing.
Thanks a lot.
Btw, the directory i am giving to the method is never used. The other parameter i am giving is a list of files which contains all the files and directories from the C:\mantenimiento directory.
I once had a problem with windows and zip files, where the created zip did not contain the entries for the folders (i.e. /, /Carpeta_A etc) only the file entries. Try adding ZipEntries for the folders without streaming content.
But as alternative to the somewhat bulky Zip API of Java you could use Filesystem (since Java7) instead. The following example is for Java8 (lambda):
//Path pathToZip = Paths.get("path/to/your/folder");
//Path zipFile = Paths.get("file.zip");
public Path zipPath(Path pathToZip, Path zipFile) {
Map<String, String> env = new HashMap<String, String>() {{
put("create", "true");
}};
try (FileSystem zipFs = FileSystems.newFileSystem(URI.create("jar:" + zipFile.toUri()), env)) {
Path root = zipFs.getPath("/");
Files.walk(pathToZip).forEach(path -> zip(root, path));
}
}
private static void zip(final Path zipRoot, final Path currentPath) {
Path entryPath = zipRoot.resolve(currentPath.toString());
try {
Files.createDirectories(entryPath.getParent());
Files.copy(currentPath, entryPath);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

Cannot extract jar file in java application packaged for windows

My program was created in Netbeans 8.0.2. The program is supposed to create a (database) folder after installation and extract the contents of a (database) jar file from its library. The folder gets created quite okay, but the contents of the jar file do not get extracted.
How can I get the extraction of the jar file to work?
NB: When I run the program in Netbeans, everything goes well.
Sample Code:
String appHomeDir = new java.io.File(".").getCanonicalPath();
String destDir = appHomeDir + "/database";
File folder = new File(destDir);
if (!folder.exists()) {
folder.mkdir();
String current = new java.io.File(".").getCanonicalPath();
String jarFile = current + "\\app\\lib\\database.jar";
java.util.jar.JarFile jar = new java.util.jar.JarFile(jarFile);
java.util.Enumeration enumEntries = jar.entries();
while (enumEntries.hasMoreElements()) {
java.util.jar.JarEntry file = (java.util.jar.JarEntry) enumEntries.nextElement();
java.io.File f = new java.io.File(destDir + java.io.File.separator + file.getName());
if (file.isDirectory()) { // if its a directory, create it
f.mkdir();
continue;
}
java.io.InputStream is = jar.getInputStream(file); // get the input stream
java.io.FileOutputStream fos = new java.io.FileOutputStream(f);
while (is.available() > 0) { // write contents of 'is' to 'fos'
fos.write(is.read());
}
fos.close();
is.close();
}
}
So the "database" directory gets created but the contents of "database.jar" do not get extracted.
Problem solved: I replaced "/app/lib/database.jar" with "/lib/database.jar"

How to copy files out of the currently running jar

I have a .jar that has two .dll files that it is dependent on. I would like to know if there is any way for me to copy these files from within the .jar to a users temp folder at runtime. here is the current code that I have (edited to just one .dll load to reduce question size):
public String tempDir = System.getProperty("java.io.tmpdir");
public String workingDir = dllInstall.class.getProtectionDomain().getCodeSource().getLocation().getPath();
public boolean installDLL() throws UnsupportedEncodingException {
try {
String decodedPath = URLDecoder.decode(workingDir, "UTF-8");
InputStream fileInStream = null;
OutputStream fileOutStream = null;
File fileIn = new File(decodedPath + "\\loadAtRuntime.dll");
File fileOut = new File(tempDir + "loadAtRuntime.dll");
fileInStream = new FileInputStream(fileIn);
fileOutStream = new FileOutputStream(fileOut);
byte[] bufferJNI = new byte[8192000013370000];
int lengthFileIn;
while ((lengthFileIn = fileInStream.read(bufferJNI)) > 0) {
fileOutStream.write(bufferJNI, 0, lengthFileIn);
}
//close all steams
} catch (IOException e) {
e.printStackTrace();
return false;
} catch (UnsupportedEncodingException e) {
System.out.println(e);
return false;
}
My main problem is getting the .dll files out of the jar at runtime. Any way to retrieve the path from within the .jar would be helpful.
Thanks in advance.
Since your dlls are bundeled inside your jar file you could just try to acasses them as resources using ClassLoader#getResourceAsStream and write them as binary files any where you want on the hard drive.
Here is some sample code:
InputStream ddlStream = <SomeClassInsideTheSameJar>.class
.getClassLoader().getResourceAsStream("some/pack/age/somelib.dll");
try (FileOutputStream fos = new FileOutputStream("somelib.dll");){
byte[] buf = new byte[2048];
int r;
while(-1 != (r = ddlStream.read(buf))) {
fos.write(buf, 0, r);
}
}
The code above will extract the dll located in the package some.pack.age to the current working directory.
Use a class loader that is able to locate resources in this JAR file. Either you can use the class loader of a class as Peter Lawrey suggested, or you can also create a URLClassLoader with the URL to that JAR.
Once you have that class loader you can retrieve a byte input stream with ClassLoader.getResourceAsStream. On the other hand you just create a FileOutputStream for the file you want to create.
The last step then is to copy all bytes from the input stream to the output stream, as you already did in your code example.
Use myClass.getClassLoader().getResourceAsStream("loadAtRuntime.dll"); and you will be able to find and copy DLLs in the JAR. You should pick a class which will also be in the same JAR.

Transform zip directory function using java.util.zip to use LZMA

I currently have a function makeBackup() which zips an entire directory into a zip file, the files are however too big so we decided to switch to LZMA.
We found a library which does this (lzma-java) however it seems to compress only a single file, while the zip function we used permits to add files and directories to a zip file.
How can we implement the same with LZMA by changing our function? I added our current function below:
private static void makeBackup()
{
String backupPathString = "/home/backups";
/* zip remote file */
try
{
//name of zip file to create
String zipFilename = "backup.zip";
//create ZipOutputStream object
ZipOutputStream zipOutStream = new ZipOutputStream(new FileOutputStream(zipFilename));
//path to the currentFile to be zipped
File zipFolder = new File(backupPathString);
//get path prefix so that the zip file does not contain the whole path
// eg. if currentFile to be zipped is /home/lalit/test
// the zip file when opened will have test currentFile and not home/lalit/test currentFile
int len = zipFolder.getAbsolutePath().lastIndexOf(File.separator);
String baseName = zipFolder.getAbsolutePath().substring(0, len + 1) + File.separator + "todaybackups";
zipFilesInPath(zipOutStream, backupPathString, baseName);
zipOutStream.flush();
zipOutStream.close();
}
catch (IOException e)
{
}
}
private static void zipFilesInPath(ZipOutputStream zipOutputStream, String filePath, String baseName) throws IOException
{
File currentFile = new File(filePath);
ArrayList<File> filesArrayList = new ArrayList<File>(Arrays.asList(currentFile.listFiles()));
if (filesArrayList.isEmpty())
{
String name = currentFile.getAbsolutePath().substring(baseName.length());
ZipEntry zipEntry = new ZipEntry(name + "/" + ".");
zipOutputStream.putNextEntry(zipEntry);
}
for (File file : filesArrayList)
{
if (file.isDirectory())
{
zipFilesInPath(zipOutputStream, file.getAbsolutePath(), baseName);
}
else
{
String name = file.getAbsolutePath().substring(baseName.length());
ZipEntry zipEntry = new ZipEntry(name);
zipOutputStream.putNextEntry(zipEntry);
IOUtils.copy(new FileInputStream(file), zipOutputStream);
zipOutputStream.closeEntry();
}
}
}
private static void unzipFilesToPath(ZipInputStream zipInputStream, String fileExtractPath) throws IOException
{
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null)
{
int count;
byte[] data = new byte[2048];
/*let's make the directory structure needed*/
File destFile = new File(fileExtractPath, entry.getName());
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
if (!entry.isDirectory() && !entry.getName().substring(entry.getName().length() - 1).equals("."))
{
final FileOutputStream fos = new FileOutputStream(fileExtractPath + File.separator + entry.getName());
final BufferedOutputStream dest = new BufferedOutputStream(fos, 2048);
while ((count = zipInputStream.read(data, 0, 2048)) != -1)
{
dest.write(data, 0, count);
}
dest.flush();
dest.close();
}
}
}
apparently is not possible, and what you have to do is package all your files into a tar/zip and then apply the lzma. take a look at this How to use LZMA SDK to compress/decompress in Java

Can't Retrieve Resources from External Jar File

NOTE: This is a followup to my question here.
I have a program that takes the contents of a directory and bundles everything into a JAR file. The code I use to do this is here:
try
{
FileOutputStream stream = new FileOutputStream(target);
JarOutputStream jOS = new JarOutputStream(stream);
LinkedList<File> fileList = new LinkedList<File>();
buildList(directory, fileList);
JarEntry jarAdd;
String basePath = directory.getAbsolutePath();
byte[] buffer = new byte[4096];
for(File file : fileList)
{
String path = file.getPath().substring(basePath.length() + 1);
path.replaceAll("\\\\", "/");
jarAdd = new JarEntry(path);
jarAdd.setTime(file.lastModified());
jOS.putNextEntry(jarAdd);
FileInputStream in = new FileInputStream(file);
while(true)
{
int nRead = in.read(buffer, 0, buffer.length);
if(nRead <= 0)
break;
jOS.write(buffer, 0, nRead);
}
in.close();
}
jOS.close();
stream.close();
So, all is well and good and the jar gets created, and when I explore its contents with 7-zip it has all the files I need. However, when I try to access the contents of the Jar via a URLClassLoader (the jar is not on the classpath and I don't intend it to be), I get null pointer exceptions.
The odd thing is, when I use a Jar that I've exported from Eclipse, I can access the contents of it in the way I want. This leads me to believe that I'm somehow not creating the Jar correctly, and am leaving something out. Is there anything missing from the method up above?
I figured it out based on this question - the problem was me not properly handling backslashes.
Fixed code is here:
FileOutputStream stream = new FileOutputStream(target);
JarOutputStream jOS = new JarOutputStream(stream);
LinkedList<File> fileList = new LinkedList<File>();
buildList(directory, fileList);
JarEntry entry;
String basePath = directory.getAbsolutePath();
byte[] buffer = new byte[4096];
for(File file : fileList)
{
String path = file.getPath().substring(basePath.length() + 1);
path = path.replace("\\", "/");
entry = new JarEntry(path);
entry.setTime(file.lastModified());
jOS.putNextEntry(entry);
FileInputStream in = new FileInputStream(file);
while(true)
{
int nRead = in.read(buffer, 0, buffer.length);
if(nRead <= 0)
break;
jOS.write(buffer, 0, nRead);
}
in.close();
jOS.closeEntry();
}
jOS.close();
stream.close();

Categories

Resources