javamavenjava-native-interfacenativemaven-nar-plugin

Using a native Maven artifact (nar) in a webapp


I have a native shared library that is built and packaged using the maven-nar plugin. This works great and builds on Linux/MacOSX/Windows. I've also defined a JNI library, also built using maven-nar, that wraps the shared library. Both of these are produced as NAR artifacts and require the maven-nar plugin in order to be used.

The problem arises when declaring a dependency on these NARs from a non-NAR-packaged project. The maven-nar plugin never seems to get invoked. Only when I change the project's packaging to NAR does the maven-nar plugin kick in. This makes it seem like NAR packaging needs to be infectious in order to work, if there's a NAR dependency then all upstream projects need to be NAR-packaged. Is this correct or am I missing something?

Can the native shared library and JNI artifacts produced using the maven-nar plugin be successfully used in web applications, i.e. WARs? If they can be used and deployed in a WAR, how is it done? Otherwise, is the only option to manually place the native libraries in some location in the java.library.path on the app server?

Here's a snippet of the POM for the project that depends on the NAR JNI artifact:

  <?xml version="1.0" encoding="UTF-8"?>
  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>thegroup</groupId>
    <artifactId>theparent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath>
  </parent>

  <artifactId>thedependant</artifactId>
  <packaging>jar</packaging>
  <name>A nice name</name>

  ...

  <properties>
    <skipTests>true</skipTests>
  </properties>

  ...

  <build>
    <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-nar-plugin</artifactId>
         <version>2.1-SNAPSHOT</version>
       </plugin>
    </plugins>
  </build>

  ...

  <dependencies>
    <dependency>
      <groupId>thegoup</groupId>
      <artifactId>theJNI</artifactId>
      <version>1.0-SNAPSHOT</version>
      <type>nar</type>
    </dependency>
  </dependencies>

  ...

</project>

Solution

  • No, or at least, not easily.

    If you have JNI, you need to mess with -Djava.library.path and perhaps LD_LIBRARY_PATH/DYLD_LIBRARY_PATH/PATH, and all that has to happen in the launch of the overall container. There's no mechanism for propagating all that from inside a war to the container.

    In full-blown Java EE, the JCA model was / is intended to be the way that native code gets integrated in web applications. But typical lightweight containers don't support it.

    If your native code has no dependencies on other shared libs, and if you aren't concerned about JVM native code conflicts (a given native class can only be in one class loader), then your problem is merely getting the shared objects into the war file at all.

    http://maven.apache.org/plugins/maven-war-plugin/examples/adding-filtering-webresources.html

    is one approach. Use the maven-dependency-plugin to drop the shared lib(s) into some directory under ${project.build.directory} and then pick them up as 'web resources'.