How to use JarOutputStream to create a JAR file?
It turns out that JarOutputStream
has three undocumented quirks:
- Directory names must end with a '/' slash.
- Paths must use '/' slashes, not '\'
- 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
How to Execute System Commands (Linux/Bsd) Using Java
Best Practice for Setting Jframe Locations
Javafx 11: Illegalaccesserror When Creating Label
Load a Resource Contained in a Jar
Keylistener Not Working for JPAnel
Quickly Read the Last Line of a Text File
How to Tackle Daylight Savings Using Timezone in Java
Differencebetween Set and List
How to Respond with an Http 400 Error in a Spring MVC @Responsebody Method Returning String
Make Maven to Copy Dependencies into Target/Lib
Java List.Contains(Object with Field Value Equal to X)
How to Convert Milliseconds to "Hh:Mm:Ss" Format
Why Would One Mark Local Variables and Method Parameters as "Final" in Java
Using Mockito to Test Abstract Classes
Order of Xml Attributes After Dom Processing
Initial Size for the Arraylist
Getting Rsa Private Key from Pem Base64 Encoded Private Key File