Showing posts with label JNI. Show all posts
Showing posts with label JNI. Show all posts

Wednesday, September 25, 2013

Using POSIX MQ from Java - another part

In introduction we managed to create our message queue and to close it. Only thing which is unclear is where is it? Message queue is maintained by kernel and it is created in virtual file system. If we execute in terminal:

cat /proc/filesystems

we will see entry:

nodev    mqueue

If we want to take a closer look at our JNIMQ_1 we will have to mount mqueue. It is nicely described in man pages, just look for mq_overview entry. For lazy people here are commands for mounting message queue, you must do that as root:

# mkdir /dev/mqueue
# mount -t mqueue none /dev/mqueue


After this we should exit root level. Again using man pages we find out that second line is standard form for mount and looks like this mount -t type device dir. Once mounted we can use our standard set of tools for examining file content on Linux, for example:

ls -l /dev/mqueue
cat /dev/mqueue/JNIMQ_1


Now we can go on sending and receiving messages. If we do not specify time-out, those messages and queues will be persisted until system reboots.
Since we have functional message queue, we can nicely start chat. For that reason we will send some messages. We already have queue from last time and we will not create it:


That flags = 1 is open in write only mode. Boring part with javah we are skipping and here is implementation:


We compile it as described in previous blog entry and send some messages. If we cat /dev/mqueue/JNIMQ_1 we will see that it is growing.
Receiving is equally simple, here is the Java code:


We want queue opened as read only and we want messages with priority 0. After applying javac and javah we write implementation:


This 1024 should be retrieved from mq attributes, but I am lazy to do that and I still remember how it was created.
In this two examples I deviated from passing mq descriptor to Java, that is what jtux does. Not sure what is more expensive, crossing managed-native border or opening file, so do not ask.

Sunday, September 22, 2013

Using POSIX MQ from Java - Introduction

All this is taking place on Linux Mint but should work on any other Linux. JDK is Oracle JDK, I am doing Android programming and it requires Oracle JDK, OpenJDK should do fine but path will differ. Goal of this introduction is to create new message queue and to close it - nothing else.
As usually in JNI workflow we write Java code:


Idea about storing pointer to data structure into long is from jtux project, otherwise it should be message queue descriptor or (mqd_t)-1 in case of failure. Queue name must start with slash and 64 is O_CREAT | O_RDONLY we want queue to be created and to be read only for us. Now we compile PosixMQ.java and we run javah on class to get header for C implementation. On my box it looks like this:

/usr/lib/jvm/java-7-oracle/bin/javah -jni PosixMQ

Interesting part of PosixMQ.h looks like this:


Finally we write trivial C implementation, being careful to include all required imports:


I was too lazy to pass attributes around, it should be done at later stage. Permissions are also conveniently hardcoded, though passing octal through parameter list is not difficult. To compile this I used:

gcc -I/usr/lib/jvm/java-7-oracle/include -I/usr/lib/jvm/java-7-oracle/include/linux -o libPosixMQ.so -shared -fPIC PosixMQ.c -lrt

Switch -fPIC will not be required on 32 bit Linux and -lrt instruction to link against the real-time is required everywhere. To run it we do:

java -Djava.library.path=. PosixMQ

in directory where are binaries and we see:

We have MQ open.
Close returned 0


Not very impressive but again very simple. Maybe next time we send and receive some messages, who knows.

Thursday, February 21, 2013

More C++ and JNI on Linux Mint

In last JNI tutorial we learned how to compile Java code, generate header and implement and compile native code. JNI declares two main groups of commonly used types. Primitive types, which are int, boolean, float and so on, and they are user friendly, no need to do cast to native types. Other group are reference types, like String, arrays or proper Java classes, and they all inherit from jobject and require special handling to map them to native types. So here is the simple example to illustrate different parameter type handling:

public class MultiCall{
    public native int add(int a, int b);
    public native boolean isOdd(long a);
    public native int sum(int[] arr);
    public native String add(String a, String b);
    static
    {
        System.loadLibrary("MultiCall");
    }
    public static void main(String[] args)
    {
        MultiCall m = new MultiCall();
        System.out.println(m.add(1, 2));
        System.out.println(m.add("Hello", "World"));
        System.out.println(m.sum(new int[]{1,2,3,4}));
        System.out.println(m.isOdd(6L));
    }
}


After we compile it and run javah tool on resulting class, we have the following method definitions:


Implementation looks like this:


Reference types require significant work to use them from native code. Also if we want to return reference type it needs to be constructed. I am using std::string because it will do deep copy of character array, at least if you are using gcc/g++, and allows me to easily manipulate strings. Code is easy to understand and doesn’t deserve further comments.
The second example is illustration how to throw exception from native code and catch it in Java code.

public class NativeException{
    public native int divide(int a, int b) throws IllegalArgumentException;
    static
    {
        System.loadLibrary("NativeException");
    }
    public static void main(String[] args)
    {
        NativeException n = new NativeException();
        try{
            System.out.println(n.divide(12, 3));
            n.divide(12, 0);
        }catch(Exception e){
            System.out.println(e.getMessage());
        }
    }
}


Even if we marked native method with throws, javac will not complain if we try to call it out of try-catch block. One of reasons why pure Java is preferred to JNI. Also in header file, that throws is just ignored.


In implementation we do sanity check and if second parameter is zero we try to throw IllegalArgumentException.


If env fails to find IllegalArgumentException, we just return zero.

Thursday, February 14, 2013

JNI Hello World on Linux Mint

I wrote similar tutorial for www.linux.com about four years ago. The motivation is the same as then, in then Sun and today Oracle documentation only Solaris and Windows are treated, Linux is omitted. Also there are slight differences in compilation process on 32 bit Ubuntu 9.04 then and 64 bit Linux Mint 13 today. Otherwise there is increase in popularity of JNI thanks to Android NDK. To do native Android development one should be familiar with C, C++, JNI and Java. As in that old article I will use C++ to create shared library.
We start with Java class:

class YAHelloWorld
{
    public native void sayHi(String name);
    static
    {
        System.loadLibrary("YAHelloWorld");
    }
    public static void main(String[] args)
    {
        YAHelloWorld h = new YAHelloWorld();
        h.sayHi("World");
    }
}


Beside normal Java stuff we have request to load native library and forward declaration of native method. We save it as YAHelloWorld.java and compile it

javac YAHelloWorld.java

After that we invoke javah to create native header:

/usr/lib/jvm/java-7-oracle/bin/javah -jni YAHelloWorld

I was lazy to export path, so that is a reason for full path to javah. If you try without full path you may get following suggestion:


what is not really necessary.  Content of generated YAHelloWorld.h is:


The first parameter, JNIEnv * is pointer to array of pointers of JNI functions, jobject is sort of this pointer and jstring is Java String. Now when we know all that we can write C++ implementation:


Beside normal C++ stuff we convert Java String to C++ string at the beginning and at the end we release both of them. Compilation looks like this:

g++ -shared -fPIC -I/usr/lib/jvm/java-7-oracle/include -I/usr/lib/jvm/java-7-oracle/include/linux YAHelloWorld.cpp -o libYAHelloWorld.so

and finally we execute our Hello World example like this:

java -Djava.library.path=. YAHelloWorld

That should produce familiar Hello World output. Not too difficult.