Java Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

> CHECK OUT THE COURSE

1. Introduction

In this brief article, we'll go over the process of creating jar files programmatically. When writing software, eventually we need to deploy it into a production state. In some cases, it is okay to use a classpath with separate files. Usually, it is more convenient to handle a single file. In the case of Java, the standard way of doing this is with a JAR, WAR, or EAR file.

The basic process is to write the manifest, open the jar, add the contents, and finally, close the jar.

2. Anatomy of a Jar File

A jar file is an extension of the ZIP file format, with the inclusion of a manifest file. The manifest file is a special file specific to JAR files and may contain various settings. Some of these are the main class, optional data (ie, author, version, etc.), and code signing information.

We may use zip-compatible tools, such as WinRar, to view and extract some or all of an archive. We can also include a jars or libs subdirectory for containing dependency jars. Since a jar is an extension of zip files, we can include any file or directory.

3. Creating a JarTool Class

To simplify the process of creating a JAR file, we create a solitary, Plain Old Java Object (POJO) class that encapsulates our operations. We may include putting entries in a manifest file, creating a JAR file, adding files or directories.

We can also create methods to perform deletions from a JAR or even append entries to an existing JAR, although these operations require fully reading and re-writing the JAR.

3.1. JAR Manifest

In order to create a JAR file, we must first begin the manifest:

public class JarTool {    
    private Manifest manifest = new Manifest();

    public void startManifest() {
        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
    }
}

If we want the jar to be executable, we must set the main class:

public void setMainClass(String mainFQCN) {
    if (mainFQCN != null && !mainFQCN.equals("")) {
        manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, mainFQCN);
    }
}

Also, if we want to specify additional attributes, we can add them to the manifest, for example:

addToManifest("Can-Redefine-Classes", "true");

Here is that method:

public void addToManifest(String key, String value) {
     manifest.getMainAttributes().put(new Attributes.Name(key), value);
}

3.2. Opening the JAR For Writing

With the manifest completed, we may now write the entries to the JAR file. To do this, we must first open the jar:

public JarOutputStream openJar(String jarFile) throws IOException {        
    return new JarOutputStream(new FileOutputStream(jarFile), manifest);
}

3.3. Adding Files to the Jar

When adding files to the JAR, Java uses the Solaris style filenames using a forward slash as a separator (/). Note that we can add any file of any type, including other JAR files or empty directories. This is really handy for including dependencies.

Also, because the JAR file is a form of a classpath, we must specify what part of the absolute path we wish to use inside the JAR. For our purposes, the root path will be our project's classpath.

Understanding this, we can now finish our JarTool class with this method:

public void addFile(JarOutputStream target, String rootPath, String source) 
  throws FileNotFoundException, IOException {
    String remaining = "";
    if (rootPath.endsWith(File.separator)) {
        remaining = source.substring(rootPath.length());
    } else {
        remaining = source.substring(rootPath.length() + 1);
    }
    String name = remaining.replace("\\","/");
    JarEntry entry = new JarEntry(name);
    entry.setTime(new File(source).lastModified());
    target.putNextEntry(entry);
    
    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();
    in.close();
}

4. A Working Example

To demonstrate the minimum requirement for an executable jar, we'll write an application class and then see how it works:

public class Driver {
    public static void main(String[] args) throws IOException {
        JarTool tool = new JarTool();
        tool.startManifest();
        tool.addToManifest("Main-Class", "com.baeldung.createjar.HelloWorld");

        JarOutputStream target = tool.openJar("HelloWorld.jar");
        
        tool.addFile(target, System.getProperty("user.dir") + "\\src\\main\\java",
          System.getProperty("user.dir") + "\\src\\main\\java\\com\\baeldung\\createjar\\HelloWorld.class");
        target.close();
    }
}

The HelloWorld class is a very simple class with a single main() method that prints out the text:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

And to demonstrate that it works, we have this example:

$ javac -cp src/main/java src/main/java/com/baeldung/createjar/HelloWorld.java
$ javac -cp src/main/java src/main/java/com/baeldung/createjar/JarTool.java
$ javac -cp src/main/java src/main/java/com/baeldung/createjar/Driver.java
$ java -cp src/main/java com/baeldung/createjar/Driver
$ java -jar HelloWorld.jar
Hello World!

Here, we've compiled each class, then executed the Driver class, which will create the HelloWorld jar. Finally, we've executed the jar, which results in printing the “Hello World” message.

The commands above should be executed from the project location.

5. Conclusion

In this tutorial, we saw how to create a jar file programmatically, add files to it, and finally execute it.

And, of course, the code is available over on GitHub.

Java bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!