How to manually create the jar file for running your Java application

Advances in software engineering had shortened the time needed to build an application from scratch. For instance with Spring Boot, I can easily build my own web API backed by a web server of my choice into a single jar file. By running that jar file, I can start a process that responds to HTTP requests directed at my customized endpoints.

However, not knowing how that jar file is formed and read by the Java Virtual Machine can cloud our understanding of application development with Java. To help understand Java application development better, I described how to manually create a jar file for running a Java application.

Defining a sample scenario

As the topic of this post is to document the process of manually preparing an executable jar file, I shall define a simple scenario for my Java application.

Let's suppose that I want to build a Java application that downloads Techcoil's robots.txt and saves it in the same directory where my Java application resides in.

To fulfil this requirement, I refer to my previous post on how to send HTTP GET request with Java without using any external libraries to derive the following Java source file:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;

public class TechcoilRobotTxtDownloader {

	public static void main(String[] args) {
		
		ByteArrayOutputStream responseBodyBaos = null;
                Scanner httpResponseBodyScanner = null;
                try {
                    // Define server endpoint
                    URL robotsUrl = new URL("http://www.techcoil.com/robots.txt");
                    HttpURLConnection urlConnection = (HttpURLConnection) robotsUrl.openConnection(); 
 
                    httpResponseBodyScanner = new Scanner(urlConnection.getInputStream());
 
                    // Use a ByteArrayOutputStream to store the contents of the HTTP response body
                    responseBodyBaos = new ByteArrayOutputStream();
                    while(httpResponseBodyScanner.hasNextLine()) {
                        responseBodyBaos.write(httpResponseBodyScanner.nextLine().getBytes());
                    }
                    responseBodyBaos.close();
                    httpResponseBodyScanner.close();
 
                    // Verify contents of robots.txt
                    String robotsContent = responseBodyBaos.toString();
                    if (robotsContent.trim().equals("Sitemap: http://www.techcoil.com/sitemap-index.xml")) {
                        System.out.println("Able to retrieve robots.txt from server. Server is running fine.");
                
                        // Save the robots content to file in the same directory
                        PrintWriter printWriter = new PrintWriter("techcoil-robot.txt");
                        printWriter.println(robotsContent);
                        printWriter.flush();
                        printWriter.close();
                
                    }
                    else {
                        System.out.println("Not able to retrive robots.txt from server.");
                    }
 
                } catch(IOException ioException) {
                    System.out.println("IOException occurred while contacting server.");
                    ioException.printStackTrace();
                } finally {
                    if (responseBodyBaos != null) {
            	        try {
                                responseBodyBaos.close();
            	        } catch (IOException ioe) {
            		        System.out.println("Error while closing response body stream");
            	        }
                    }
                    if (httpResponseBodyScanner != null) {
                        httpResponseBodyScanner.close();
                    }
                }

	}

}

I added the block that utilized an instance of java.io.PrintWriter to write the contents of Techcoil's robots.txt. With that, I had created an Java application that can be run by the Java Virtual Machine.

Running your Java application that is not contained in a jar file

To get a runnable Java application, we will first need to get the Java Compiler to compile TechcoilRobotTxtDownloader.java:

javac TechcoilRobotTxtDownloader.java

Once the Java compiler completed its job, we will get TechcoilRobotTxtDownloader.class in the same directory where TechcoilRobotTxtDownloader.java is located at. TechcoilRobotTxtDownloader.class is our runnable Java application.

To run our Java application, we will then run the following command:

java TechcoilRobotTxtDownloader

Noticed that we do not specify the .class extension in the input parameter to our java executable. This will tell the Java Virtual Machine to look inside of TechcoilRobotTxtDownloader.class and run the static main method.

So in short, we will need to provide the Java Virtual Machine with a .class file that contains a static main method that contains the codes that will do what we wanted to do.

Understanding how the Java Virtual Machine run your Java application which is contained in a jar file

Before going into the command to prepare the jar file, it is beneficial to understand how the Java Virtual Machine runs your Java application.

By passing the name of the class (TechcoilRobotTxtDownloader) to the java executable, the java executable will be able to understand that there is a TechcoilRobotTxtDownloader.class that it can find in the current directory. That is straightforward.

In the case where our Java application is contained within a jar file, we have to make sure that our jar file contains a minimal file structure as follows:

nameOfJar.jar
|-- TechcoilRobotTxtDownloader.class
|-- META-INF
|   |-- MANIFEST.MF

With that, whenever the java executable receives a .jar file, it will look into the META-INF directory inside the .jar file for a MANIFEST.MF file. The MANIFEST.MF will need to contain a directive to tell java executable where to find the class that contains a static main method to run. The META-INF directory and MANIFEST.MF file have to named as such, they cannot be give other names.

Creating the manifest file that tells the java executable where to find the class that contains the static main method

To create a manifest file that tells the java executable where to find the class that contains the static main method, I run the following command in my shell, inside of the directory that contains TechcoilRobotTxtDownloader.class:

nano MANIFEST.MF

I then added the following line:

Main-Class: TechcoilRobotTxtDownloader

and saved the changes.

The Main-Class directive tells the java executable to find TechcoilRobotTxtDownloader.class, which contains the static main method, in the same directory as the META-INF directory. With this information, the java executable will then be able to know that I want to run TechcoilRobotTxtDownloader.class that is contained within the jar file.

Creating the executable jar file for running your Java application

After I had created MANIFEST.MF, I then continued on to create the jar file. The JDK contains the jar executable for us create jar files.

To create the jar file, I run the following command in my terminal:

jar cvfm TechcoilRobotTxtDownloader.jar MANIFEST.MF TechcoilRobotTxtDownloader.class

The first input parameter contains four instructions for the jar executable:

  • c for indicating that I want to create a jar file.
  • v for indicating that I want the jar executable to report its progress to me.
  • f for indicating that I wish to name my jar file myself.
  • m for indicating that I want to provide my own manifest file.

With that, the jar executable will read TechcoilRobotTxtDownloader.jar as the name for the jar file, MANIFEST.MF as the manifest file to include inside the META-INF directory inside of the jar file and anything that comes afterwards will be included in the root of the jar file.

With that command, I will get the following output:

added manifest
adding: TechcoilRobotTxtDownloader.class(in = 2539) (out= 1437)(deflated 43%)

This told me that the jar executable was able to find MANIFEST.MF and TechcoilRobotTxtDownloader.class, placing them inside the appropriate locations within the newly created jar file.

As an aside, in this case the manifest file that we provided for the jar executable can be give other names. This is because the jar executable will make sure that the manifest file is named as MANIFEST.MF before putting it into the META-INF directory.

Running the jar file that contains our Java application

Running the jar file from command line

To run the jar file that contains our Java application, I run the following command in my terminal:

java -jar TechcoilRobotTxtDownloader.jar

The -jar flags tells the java executable that we are providing it a jar file that contains the Java application with a static main method. When the command completes, I will see the output in my shell:

Able to retrieve robots.txt from server. Server is running fine.

and techcoil-robot.txt in the same directory where TechcoilRobotTxtDownloader.jar resides in.

Running the jar file from GUI based operating system

In a GUI based operating system, if the default application for opening .jar files is set as the Java Launcher, I will also be able to run TechcoilRobotTxtDownloader.jar by double-clicking on the TechcoilRobotTxtDownloader.jar icon.

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.