Current JAR path

I wanted to write an executable JAR file, that would be able to install itself. It should copy itself to the correct location and also perform some configuration.

I was writing the installation guide and found myself saying something like "copy the JAR file to this location". Why not just tell the user to run the JAR file like this:

>java -jar component.jar

Now the question is, how do I know where my current JAR file is?

You can get the URL to any resource in your classpath by calling

URL java.lang.Class.getResource(String name)  

The URL will look something like this:

file:/C:/Users/palle/workspace/Component/Component.jar!/resources/image.png  

The first part of the URL before the exclamation mark (!) is the JAR file URL. We can get this part by splitting the path URL like this:

String urlPath = resource.getPath();  
String jarPath = urlPath.split("\\!", 2)[0];  

Now we have

file:/C:/Users/palle/workspace/Component/Component.jar  

To make this URL into a file path, we need to do the following:

String filePath = new File(new URI(url.toExternalForm())).getAbsolutePath();  

This seems a little weird, but it is to get rid of the leading slash

Now we have this (if run on Windows OS):

C:\Users\palle\workspace\Component\Component.jar  

Now let's say that we want to implement all of this in a utility class, so we can call it from anywhere in our code. Let's get the class name of the class that called our method:

StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();  
int index = stackTrace.length-1;  
StackTraceElement stackTraceElement = stackTrace[index];  
Class<?> c = Class.forName(stackTraceElement.getClassName());  

Notice how we are using the stack trace element before the last one. If we used the last one, we would be referencing the utility class, which might be in it's own JAR file.

We need to reference a resource inside the JAR file, but we don't know what resources the JAR file contains. Fortunately you can actually get a class in the JAR file as a resource, so lets see if we can get the calling class as a resource.

By replacing the dots (.) with slashes (/) and appending '.class' to the full name of the class, we reference it as a resource, so we need to do something like this:

String resourceName = "/"+c.getName().replace('.', '/').concat(".class");  

Now let's put it all together:

package com._4thex.installer;

import java.io.File;  
import java.net.MalformedURLException;  
import java.net.URI;  
import java.net.URL;

public class JarFileHelper {  
    public static String getPath() throws Exception {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        int index = stackTrace.length-1;
        StackTraceElement stackTraceElement = stackTrace[index];
        Class<?> c = Class.forName(stackTraceElement.getClassName());
        String resourceName = "/"+c.getName().replace('.', '/').concat(".class");
        URL resource = c.getResource(resourceName);
        String urlPath = resource.getPath();
        System.out.println(urlPath);
        String jarPath = urlPath.split("\\!", 2)[0];
        try {
            URL url = new URL(jarPath);
            String filePath = new File(new URI(url.toExternalForm())).getAbsolutePath();
            return filePath;
        } catch (MalformedURLException ex) {
            throw new Exception("Not a JAR file");
        }
    }
}

We can use our new utility class like this:

package com._4thex.installer;

public class Main {

    public static void main(String[] args) throws Exception {
        System.out.println(JarFileHelper.getPath());    
    }

}

Running this will give the following output:

C:\Users\palle\workspace\Component\Component.jar