Package Java JNI libraries in a JAR using Maven
Tags: java, native, jni, library
Published: 2023-08-20 18:59:15

---
title: Package Java JNI libraries in a JAR using Maven
published: true
date: 2023-05-02 11:51:43 UTC
tags: java, native, JNI, library
canonical_url: https://shubham.codes/blog/2023-05-02-package-java-jni-libraries/
---

## Introduction

If you are using a native library in Java, you can just load the `dll` or `dylib` or `so` files using `System.loadLibrary` method. But if you are packaging your Java program as a JAR file, the native library will not be available to the `System.loadLibrary` method. In this article, we'll see the correct way to package the native library in the JAR file using Maven so that it will be accessible in our Java program without any other user intervention.

## Project Structure

Maven follows a specific project structure where all the source code is placed in `src/main/java` and all the resources are placed in `src/main/resources`. The resources directory is added to the classpath when the project is compiled and packaged. This means you can access the contents of the `resources` directory directly in the Java program by specifying the path relative to the resources directory.

Let's say you have a native library `libhello.dylib` in the `resources/lib` directory. After packaging this will be available at the path `lib/libhello.dylib` in the JAR file.

## Use Native Utils

The default way to load a native library in Java is to use the `System.loadLibrary` method. The `maven package` command will generate the jar file of the project. The native library will be packaged in the `lib` directory inside the jar file which will not be available to the `System.loadLibrary` method.

Now running our app will require us to extract the library from the jar file and put it somewhere on the system. This can be done using [Native Utils](https://github.com/adamheinrich/native-utils). Internally this utility is extracting the native library from the jar file to a temporary directory and loads the library from there. This will keep our hands out of the dirty work of extracting the library and loading it. It provides a method `loadLibraryFromJar` which can be used to load the native library from the jar file.

## Configurations

As [Native Utils](https://github.com/adamheinrich/native-utils) is not available as a release package, we'll need to add it directly from the GitHub repository. For this, we can use the [JitPack](https://jitpack.io/) service which allows us to add GitHub repositories as Maven dependencies. You'll need to add [JitPack](https://jitpack.io/) as a repository in your `POM.XML` file.

```
  <repositories>
    <repository>
      <id>jitpack.io</id>
      <url>https://jitpack.io</url>
    </repository>
  </repositories>

```

Also, add the dependency for [Native Utils](https://github.com/adamheinrich/native-utils). For this, we need to give `groupId` as `com.github.<user-name>` and `artifactId` as `<repository-name>`. Since we are using the latest commit from the repository, we'll need to specify the commit hash in the version tag. Below is how the dependency will look like in `POM.XML`.

```
  <dependency>
    <groupId>com.github.adamheinrich</groupId>
    <artifactId>native-utils</artifactId>
    <version>e6a39489662846a77504634b6fafa4995ede3b1d</version>
  </dependency>

```

Now, you can confirm the dependency is added by running the `maven dependency:tree` command.

```
> mvn dependency:tree

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ javaunsafe ---
[INFO] com.gor.poc:javaunsafe:jar:1.0-SNAPSHOT
[INFO] +- log4j:log4j:jar:1.2.17:compile
[INFO] +- junit:junit:jar:3.8.1:test
[INFO] \- com.github.adamheinrich:native-utils:jar:e6a39489662846a77504634b6fafa4995ede3b1d:compile

```

You can see, my project is using 3 dependencies, log4j, junit, and native-utils with their version as specified.

## Load the native library

In your Java file, you can call the `loadLibraryFromJar` method to load the native library.

```
import cz.adamh.utils.NativeUtils;

public class NativeMemoryLoader {

  static {
    try {
      NativeUtils.loadLibraryFromJar("/lib/libhello.dylib");
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  public static native void sayHello();

  ...
}

```

## Load library based on your OS

The problem with loading native libraries like C/C++ is that your compiled file is dependent on the platform. This means you'll need to compile the library for each platform and then package it in the JAR file. So you'll have different versions of the library for different platforms. And you'll end up with `so`, `dylib`, and `dll` files in your `lib` directory. Let's modify our Java code to use the library based on the platform.

```
  // Get the file extension based on OS
  String osName = System.getProperty("os.name").toLowerCase();
  String libExtension = osName.contains("win") ? ".dll" :
                        osName.contains("mac") ? ".dylib" : ".so";
  String libPath = "/lib/libhello" + libExtension;
  NativeUtils.loadLibraryFromJar(libPath);

```

## Conclusion

In this article, we saw how to package native libraries in a JAR file using Maven and load them in our Java program. We also saw how to load the library based on the platform.