Search code examples
javajava-native-interfacenativejna

Java JNA call will not run native code, causes application to stall


I'm using JNA (Java Native Access) to call a native method from Java to C. The native method (int funcServer(int param1)) is a basic unix socket listen. But regardless of whether I do the optimized JNA Direct Mapping or the Interface Mapping, JNA stalls out on me. The native code doesn't print anything even on the first line of the function call, yet no exception is thrown.

See my minimum viable Java example (run on NetBeans IDE Product Version: 8.0.2 Build 201411181905 with Ubuntu Linux):

package viableexamplejava;

import com.sun.jna.*; // link jna-4.1.0.jar, jna-platform-4.1.0.jar

/**
 * Test on Ubuntu Linux or with Cygwin on Windows 7.
 */
public class ViableExample {

    /**
     * This library depends on Unix Sockets.
     */
    public interface NativeLibary extends Library {

        public int funcServer(int param1);
    }
    //uncomment for Indirect Mapping
    //private static final NativeLibary myNativeLibrary_;

    public static native int funcServer(int param1);

    static {
        final String windowsLibraryName = "ViableExample.dll";
        final String linuxLibraryName = "libViableExampleNative.so"; // compiled with Netbeans

        if (Platform.isWindows()) {
            System.err.println("Loading Windows dll. 'cygwin1.dll' should be in same directory.");
            //myNativeLibrary_ = (NativeLibary) Native.loadLibrary(windowsLibraryName, NativeLibary.class);
            Native.register(windowsLibraryName);
        } else {
            System.err.println("Loading Linux .so");
            //myNativeLibrary_ = (NativeLibary) Native.loadLibrary(linuxLibraryName, NativeLibary.class);
            Native.register(linuxLibraryName);
        }
    }

    public static void main(String[] args) {
        System.err.println("Hello World from Java");
        //uncomment for Indirect Mapping
        //myNativeLibrary_.funcServer(7);
        funcServer(7); // This never prints.
    }

}

/**
 * Side notes: If the dynamic library cannot be found, this error occurs:
 *
 * Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load
 * library 'libViableExampleNative.so': Native library
 * (linux-x86-64/libViableExampleNative.so) not found in resource path
 * ([file:/home/user/Desktop/Dropbox/jna-4.1.0.jar,
 * file:/home/user/Desktop/Dropbox/jna-platform-4.1.0.jar,
 * file:/home/user/NetBeansProjects/ViableExampleJava/build/classes/])
 *
 * at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:271) at
 * com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:398) at
 * com.sun.jna.Library$Handler.<init>(Library.java:147) at
 * com.sun.jna.Native.loadLibrary(Native.java:412) at
 * com.sun.jna.Native.loadLibrary(Native.java:391) at
 * viableexamplejava.ViableExample.<clinit>(ViableExample.java:29) Java Result:
 * 1
 * 
 * If this happens, you have to put libViableExample.so into the resource path,
 * in my case into:
 * /home/user/NetBeansProjects/ViableExampleJava/build/classes/
 * 
 * Once it's in there, "Hello World from Java" prints, but nothing else gets
 * printed. "myNativeLibrary_.funcServer(7);" doesn't print anything.
 *
 * Output: 
 * Loading Linux .so 
 * Hello World from Java
 * (The application does not terminate or throw an exception).
 */

Here is the C code I used:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h> 

/**
 * Unix server that sends clients the date and time.
 * Compiled with -shared -std=c99. 
 * Produces "libViableExampleNative.so" when built with Netbeans on Linux.
 */
int funcServer(int param1) {
    printf("\n Hello World from C. This is a Socket Server Example Program \n");
    printf("\n param1: %d \n", param1);
    {
        int listenfd = 0, connfd = 0;
        struct sockaddr_in serv_addr;

        char sendBuff[1025];
        time_t ticks;
        // Just verifying that the C code is running.
        printf("I made it to line %d in file %s\n", __LINE__, __FILE__);
        listenfd = socket(AF_INET, SOCK_STREAM, 0);
        memset(&serv_addr, '0', sizeof (serv_addr));
        memset(sendBuff, '0', sizeof (sendBuff));

        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port = htons(5000);

        bind(listenfd, (struct sockaddr*) &serv_addr, sizeof (serv_addr));

        printf("I made it to line %d in file %s\n", __LINE__, __FILE__);
        listen(listenfd, 10);

        while (1) {
            connfd = accept(listenfd, (struct sockaddr*) NULL, NULL);

            ticks = time(NULL);
            snprintf(sendBuff, sizeof (sendBuff), "%.24s\r\n", ctime(&ticks));
            write(connfd, sendBuff, strlen(sendBuff));

            close(connfd);
            sleep(1);
        }
    }
    return 0;
}

Update 1

Tried putting the .so into the jar file and then running it from the terminal (UnsatisfiedLinkError: Unable to load library 'libViableExampleNative.so': Can't obtain InputStream for linux-x86-64/libViableExampleNative.so):

$ zip ./ViableExampleJava.jar ./libViableExampleNative.so 
  adding: libViableExampleNative.so (deflated 73%)

~/NetBeansProjects/ViableExampleJava/dist$ java -jar ./ViableExampleJava.jar 
Loading Linux .so

Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'libViableExampleNative.so': Can't obtain InputStream for linux-x86-64/libViableExampleNative.so
    at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:271)
    at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:398)
    at com.sun.jna.Native.register(Native.java:1396)
    at com.sun.jna.Native.register(Native.java:1156)
    at viableexamplejava.ViableExample.<clinit>(ViableExample.java:33)

* Update 2 *

Tried setting the system property "jna.library.path"

Correct output when run from the terminal (not Netbeans):

// Set jnaPath to: /home/user/NetBeansProjects/ViableExampleNative/dist/Debug/GNU-Linux-x86

$ java -jar ./ViableExampleJava.jar

Hello World from Java

 Hello World from C. This is a Socket Server Example Program 

 param1: 7 
I made it to line 26 in file ../../Desktop/Dropbox/ViableExample/native/ViableExample.c
I made it to line 37 in file ../../Desktop/Dropbox/ViableExample/native/ViableExample.c

* Update 3 *

Even though it works from the Ubuntu Linux terminal, my Windows version (with Cygwin) fails.


Solution

  • Try running it outside of Netbeans.

    When I ran it inside netbeans the last thing I saw was:

    Loading Linux .so
    Hello World from Java
    

    When I ran from the terminal with :

    java -jar target/mavenproject5-1.0-SNAPSHOT.jar
    Loading Linux .so
    Hello World from Java
    
     Hello World from C. This is a Socket Server Example Program 
    
     param1: 7 
    I made it to line 27 in file newfile.c
    I made it to line 38 in file newfile.c
    

    The program still seems to be running in Netbeans but the C printed output is not being displayed. I can tell because when not running at all telnet shows this:

    telnet localhost 5000
    Trying 127.0.0.1...
    telnet: Unable to connect to remote host: Connection refused
    

    but running telnet while the program is running in Netbeans produces this:

    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    Tue Aug  4 19:46:38 2015
    Connection closed by foreign host.