0

I'm trying to get the basic HelloWorld JNA example working while using Maven to build a .jar. I'm starting with this directory structure:

.
+-- pom.xml
+-- src
    +-- main
        +-- java
            +-- HelloWorld.java

HelloWorld.java contains the code from the JNA getting started page:

package com.sun.jna.examples;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

/** Simple example of JNA interface mapping and usage. */
public class HelloWorld {

    // This is the standard, stable way of mapping, which supports extensive
    // customization and mapping of Java to native types.

    public interface CLibrary extends Library {
        CLibrary INSTANCE = (CLibrary)
            Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
                               CLibrary.class);

        void printf(String format, Object... args);
    }

    public static void main(String[] args) {
        CLibrary.INSTANCE.printf("Hello, World\n");
        for (int i=0;i < args.length;i++) {
            CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
        }
    }
}

My pom.xml file contains the following:

<project>
  <groupId>edu.berkeley</groupId>
  <artifactId>hello-world</artifactId>
  <modelVersion>4.0.0</modelVersion>
  <name>Hello World</name>
  <packaging>jar</packaging>
  <version>1.0</version>
  <properties>
    <jna.version>4.5.0</jna.version>
  </properties>
  <dependencies>
    <dependency> <!-- JNA dependency -->
      <groupId>net.java.dev.jna</groupId>
      <artifactId>jna</artifactId>
      <version>${jna.version}</version>
    </dependency>
    <dependency> <!-- JNA platform dependency -->
      <groupId>net.java.dev.jna</groupId>
      <artifactId>jna-platform</artifactId>
      <version>${jna.version}</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.0.2</version>
        <configuration>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
              <mainClass>com.sun.jna.examples.HelloWorld</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

I can build the target .jar without problem using mvn package, but when I try to run it I get the error mentioned in the title:

$ java -jar target/hello-world-1.0.jar
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/jna/Library
...

I can see the JNA and JNA-platform .jar files in the maven local repository. The manifest file for the built .jar has the jna related files in its class path:

$ unzip -p target/hello-world-1.0.jar META-INF/MANIFEST.MF
Manifest-Version: 1.0
Built-By: root
Class-Path: jna-4.5.0.jar jna-platform-4.5.0.jar
Created-By: Apache Maven 3.0.4
Build-Jdk: 1.8.0_144
Main-Class: com.sun.jna.examples.HelloWorld

If I manually copy the JNA .jars to the target folder, the application runs, but these .jars get removed when I do mvn clean. I assume copying those .jars to the target directory is not correct since they are removed with a clean. What should I be doing instead to get this application to run?

EDIT:

From the link shared by Roman, I found that I need to use either the maven-assembly plugin or the maven-shade plugin. I ended up going with the maven-shade plugin and added the following to my pom.xml file:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
      <execution>
        <phase>package</phase>
        <goals>
          <goal>shade</goal>
        </goals>
        <configuration>
          <minimizeJar>true</minimizeJar>
          <artifactSet>
            <includes>
               <include>net.java.dev.jna:jna</include>
               <include>net.java.dev.jna:jna-platform</include>
            </includes>
          </artifactSet>            
        </configuration>
      </execution>
    </executions>
  </plugin>

I'm using the include option so that only the white-listed dependencies (JNA) are included in order to keep the build time and .jar file size are to a minimum.

Chris
  • 402
  • 1
  • 5
  • 18
  • 2
    The issue here is, that the jar file your maven project creates does not include the JNA libraries by default hence the `java -jar` invocation fails with the NCDF exception. You might consider creating a [fat jar](https://www.mkyong.com/maven/create-a-fat-jar-file-maven-assembly-plugin/) which includes the missing JNA libraries – Roman Vottner Nov 02 '17 at 21:46
  • Thanks for that link. I'm still new to using maven and am trying to learn all the available plugins. That works for getting this basic example running, but for my full project this increases the build time and .jar size significantly (not unexpected) due to other dependencies being pulled in. I didn't have any issues with those other dependencies not being in the .jar so I'd rather not have them pulled in as well as the JNA files. It looks like that plugin supports some exclude option though so I'll have to spend some time playing around with that. – Chris Nov 02 '17 at 22:20
  • A question why you use `root` to build the jar – Blanket Fox Jul 13 '21 at 13:14

0 Answers0