How to Use Jaroutputstream to Create a Jar File

How to use JarOutputStream to create a JAR file?

It turns out that JarOutputStream has three undocumented quirks:

  1. Directory names must end with a '/' slash.
  2. Paths must use '/' slashes, not '\'
  3. Entries may not begin with a '/' slash.

Here is the correct way to create a Jar file:

public void run() throws IOException {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
JarOutputStream target = new JarOutputStream(new FileOutputStream("output.jar"), manifest);
add(new File("inputDirectory"), target);
target.close();
}

private void add(File source, JarOutputStream target) throws IOException {
String name = source.getPath().replace("\\", "/");
if (source.isDirectory()) {
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);
}
} else {
JarEntry entry = new JarEntry(name);
entry.setTime(source.lastModified());
target.putNextEntry(entry);
try (BufferedInputStream 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();
}
}
}

Using JarOutputStream to make runnable jar file

I managed to do this following this example:

https://stackoverflow.com/a/1281295/3474870

However it was only creating a simple output jar file.. What I needed was a runnable jar file.

I realised that the manifest set what the file did, so I took a look at the possible attributes and found this:

manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, new RunnableJarClass());

This enabled me to create the jar file, but use tell the manifest to look for the main method!

Hope this helps :)

Add JarOutputStream to ZipOutputStream

I'm not sure what you actually mean with "I have generated a JarOutputStream". But if you want to write content to a JAR file that is then written to a ZIP file, without the need to hold everything in memory, you could do this the following way:

public static class ExtZipOutputStream extends ZipOutputStream {

public ExtZipOutputStream(OutputStream out) {
super(out);
}

public JarOutputStream putJarFile(String name) throws IOException {
ZipEntry zipEntry = new ZipEntry(name);
putNextEntry(zipEntry);
return new JarOutputStream(this) {

@Override
public void close() throws IOException {
/* IMPORTANT: We finish writing the contents of the ZIP output stream but do
* NOT close the underlying ExtZipOutputStream
*/
super.finish();
ExtZipOutputStream.this.closeEntry();
}
};
}
}

public static void main(String[] args) throws FileNotFoundException, IOException {

try (ExtZipOutputStream zos = new ExtZipOutputStream(new FileOutputStream("target.zip"))) {

try (JarOutputStream jout = zos.putJarFile("embed.jar")) {
/*
* Add files to embedded JAR file here ...
*/
}

/*
* Add additional files to ZIP file here ...
*/

}
}

Creating a JAR file programmatically

I think you cannot add a new entry, because you cannot open jar package for "append". I think you must create a new jar file and copying entries from old, and add your entries.

Create jar from a folder in java

For complie time, you can use build tools like Apache Ant.

<jar destfile="${dist}/lib/app.jar">
<fileset dir="${build}/classes" excludes="**/Test.class" />
<fileset dir="${src}/resources"/>
</jar>

For runtime - Try this. It worked for me.
For others - this is my first attempt at it. Please post your comments because i can be wrong here:)

public class CreateJar {

public static void main(String[] args) throws IOException {
String filePath = "/src";
List<File> fileEntries = new ArrayList<>();
getAllFileNames(new File(filePath), fileEntries);
JarOutputStream jarStream = new JarOutputStream(new FileOutputStream(new File("a.jar")));
for(File file : fileEntries){
jarStream.putNextEntry(new ZipEntry(file.getAbsolutePath()));
jarStream.write(getBytes(file));
jarStream.closeEntry();
}
jarStream.close();
}

private static byte[] getBytes(File file){
byte[] buffer = new byte[(int) file.length()];
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(new FileInputStream(file));
//Read it completely
while((bis.read(buffer, 0, buffer.length))!=-1){
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return buffer;
}

private static void getAllFileNames(File file,List<File> list){
if(file.isFile()){
list.add(file);
}else{
for(File file1 : file.listFiles()){
getAllFileNames(file1, list);
}
}
}
}

How can I add the package directory structure to my jar

For anyone interested in the future, I solved this by using the following code:

out.putNextEntry(new ZipEntry("io/ironbytes/corkscrew/client/ClientInitiator.class"));

The new ZipEntry class from import java.util.zip.ZipEntry; will allow you to include your class and the directory structure in your jar at the same time.

Generate JAR file during RunTime

See the class java.util.jar.JarOutputStream (and many examples of usage on the web). For example:

FileOutputStream fout = new FileOutputStream("c:/tmp/foo.jar");
JarOutputStream jarOut = new JarOutputStream(fout);
jarOut.putNextEntry(new ZipEntry("com/foo/")); // Folders must end with "/".
jarOut.putNextEntry(new ZipEntry("com/foo/Foo.class"));
jarOut.write(getBytes("com/foo/Foo.class"));
jarOut.closeEntry();
jarOut.putNextEntry(new ZipEntry("com/foo/Bar.class"));
jarOut.write(getBytes("com/foo/Bar.class"));
jarOut.closeEntry();
jarOut.close();
fout.close();

Of course, your program will likely inspect the filesystem and traverse the directory and file structure to generate the entry names and contents but this example shows the key features. Don't forget to handle exceptions properly and close the streams in a finally block.

Also, remember that the manifest file is just the entry in "META-INF/MANIFEST.MF" which is just a text file with formatting rules per the manifest file specification.

Compiling your Java source files into Class files on the fly is a separate task, fraught with its own complications, but the class javax.tools.JavaCompiler is a good starting point. This question is also a good introduction: How do I programmatically compile and instantiate a Java class?

Create a file of a given size and type in java

Got it finally. The magic number for a jar file is (50 4b 03 04) Taken from here

File file = new File(basedir, filePath);
file.getParentFile().mkdirs();
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(sizeInBytes);
byte[] magicHeader = new byte[4];
magicHeader[0] = 0x50;
magicHeader[1] = 0x4b;
magicHeader[2] = 0x03;
magicHeader[3] = 0x04;
raf.write(magicHeader, 0, 4);
raf.close();


Related Topics



Leave a reply



Submit