Bindings

From X4X

Jump to: navigation, search

Contents

Java-bindings for c-code

For this to work you can use swig for 95% of the code and about 50% of the work ;).

Wrapper(Shadow-)classes

Swig generates wrapperclasses for everything it doesn't know, this includes structs, pointers, references, ... Problem here is that there are also useless wrappers for pointers to primitives, e.g. int*. You need to know here what this pointer is. swig offers some interfaces which do the wrapping of input/output/inout values. To get a quick overview have a look at our interfacefile in swig/interfaces/xmmsclient.i (have a look into webCVS. It's really not that hard but it's nasty to search for

Callbackfunctions

Using callbackfunctions is even harder to implement. Thing here is that you cannot use java functions as is in the callbacks. You need to use c-functions to get this working and a identiefier to set this function as a paramter to the function which needs it. To get a quick overview have a look at our interfacefile in swig/interfaces/xmmsclient.i (have a look into webCVS. Next thing to think about is that you really don't want to do all the work needed in the c-function. In our case the c-callbackfunction calls a java-method. But the c-callbackfunction itself is not a jni function, so how to do that? it's not that easy but it's possible. we have implemented a simple function in misc.c which sets a special pointer locate in callbacks.h. This special pointer is a pointer to the running java virtual machine. In runMethod() located in callbacks.c we get the JNIEnv from the jvm, and a GlobalRef (java needs it this special way) to the calling object set in setEnv(). Then we search for a special method given as argument to runMethod(). If we found it we will invoke it. So we have taken the java reflection mechanism to pure c. Thing is that the callbackfunctions in java need to be static to have this working since we never pass a object to the functions. Then our javafunction is called with two jlong parameters, a pointer to a result and a void pointer. To get a resultobject we use swig again. get_result_from_pointer() gets a long value and returns a xmmsc_result_t, swig wrapps it the right way and we don't have to edit the files generated by swig, nice done ;)

Typemaps

Thing with swig is, that it cannot know what a functionparameter int* e.g. is later in the function. The definition has not enough information to find that out. Therefor you have to set up typemaps for input, output or in- and output.

%apply int *INOUT { int* };
%apply int *INOUT { void* };
%apply unsigned int *INOUT { unsigned int* };
%apply char **STRING_OUT { char** };

typedef int int32_t;
typedef int uint32_t;

The above typemaps set int* and void* to int[], unsigned int* to long[] and int32_t/uint32_t to int. To get these thing working you have to include

%include "typemaps.i"
%include "arrays_java.i"

into your interfacefile. They are located in $SWIG_HOME/java

Mainloop

The mainloop in our case is quite a simple thing, the hard thing was to get a java FileDescriptor object from xmmsc_io_fd_get() which returns a c fildescriptor. Therefor we have built a small function (seen in misc.c) which gets a FileDescriptor and a pointer to a connection and builds the FileDescriptor. With this object we can go into a loop until the fd is valid and call xmmsc_io_in_handle() if there's something available on the stdin and call xmmsc_io_out_handle() if xmmsc_io_want_out() > 0. Otherwise the callbacks won't work the right way.

Here the way we built it:

public void run(){
	FileDescriptor fd = new FileDescriptor();
	SpecialJNI.getFD(fd, Xmmsclient.getPointerToConnection(myConnection));
	FileInputStream in = new FileInputStream(fd);
	while (fd.valid() && running){
		try {
			Thread.sleep(40);
			if (Xmmsclient.xmmsc_io_want_out(myConnection) > 0)
				Xmmsclient.xmmsc_io_out_handle(myConnection);
			if (in.available() > 0)
				Xmmsclient.xmmsc_io_in_handle(myConnection);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (IOException io){
			io.printStackTrace();
		}
	}
}

Guess that a nicer one is also possible, but i bet not a shorter one ;). This mainloop is now placed with functions to set Callbacks in org.xmms2.MainloopJava

JNI

JNI is the java native interface. Without it java would be unable to work even if its dogma is "write once, run everywhere". One needs to be able to call functions from the OS it is running on. E.g. AWT and SWT wouldn't be possible without it.

Functions in JNI

JNI needs to access a library with appropriate jni functions, so you are not able to load e.g. twain.dll on windows or something alike. A jni function in java would look like

public static final int getOne();

whereas the matching c-function would look like

JNIEXPORT jint JNICALL Java_packagename_Classname_getOne(JNIEnv *jenv, jclass cls){
        return 1;
}

You need to include jni.h from $JAVA_HOME/include into the appropriate file to compile cleanly. Then, when your code is finished you need to compile a shared library to be loaded in java. You can do this using a compiler of your choice, here explained for the gcc

gcc -shared -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -o your_lib.so your_file.c

Then you can load this file using System.loadLibrary(), if the lib is in your LDPATH, or System.load() if it's somewhere else.

Calling java-methods from jni-functions

Calling java-methods from within jni-functions is quite an easy thing. You can call e.g. a static method of a class using a function like

JNIEXPORT jint JNICALL Java_packagename_Classname_getOne(JNIEnv *jenv, jclass cls){
       jmethodID mid = (*jenv)->GetStaticMethodID(jenv, cls, "methodname", "(I)V");
       if (mid == 0) {
               return;
       }
       (*jenv)->CallStaticVoidMethod(jenv, cls, mid, 1);
}

This would call a static method in the calling class which gets an integer as parameter and returns void. To do this for objectmethods you can search google, really simple too.

Calling java methods from non-jni functions

Calling java-methods from non-jni functions is even harder but sure not impossible. We will explain that for an objectmethod using a global and a local reference to the calling object. For this to work you need to set the object and the jvm first using a jni-method, otherwise this won't work.

JNIEXPORT void JNICALL Java_org_xmms2_SpecialJNI_setENV(JNIEnv *jenv, jclass cls, jobject myobject){
       globalObj = (*jenv)->NewGlobalRef(jenv, myobject);
       (*jenv)->GetJavaVM(jenv,&jvm);
}

Now you can call java methods from within normal c-functions using the things set within this function. For information: You cannot set the jnienv as a global variable, this changes on every call, and the object needs to be a globalRef too.

The calling c-function would then look like

void any_function(int val){
       JNIEnv *environment = (*jvm)->AttachCurrentThread(jvm, (void *)&env, NULL);
       jobject callbackObject = (*environment)->NewLocalRef(environment, globalObj);
       jclass clazz = (*environment)->GetObjectClass(environment, callbackObject);
       jmethodID mid = (*environment)->GetMethodID(environment, clazz, "methodname", "(I)V");
       if (mid == 0) {
               return;
       }
       (*environment)->CallObjectMethod(environment, callbackObject, mid, val);
}

This would call again a function that receives an integer and returns void but now on a given object and not as before static.

Further examples

xmmsc_io_fd_get() ==> FileDescriptor

Here we have a function which is placed in misc.c, gets a FileDescriptor object and a pointer to a connection and "fills" the FileDescriptor with something useful

JNIEXPORT void JNICALL Java_org_xmms2_SpecialJNI_getFD(JNIEnv *env, jclass _ignore, jobject fdobj, jlong jarg1) {
	jfieldID field_fd;
	jclass class_fdesc;
	int fd = 0;
	xmmsc_connection_t *conn_ptr = (xmmsc_connection_t *) 0;
	
	conn_ptr = *(xmmsc_connection_t **)(void *)&jarg1;

	fd = xmmsc_io_fd_get(conn_ptr);

	class_fdesc = (*env)->GetObjectClass(env, fdobj);
	field_fd = (*env)->GetFieldID(env, class_fdesc, "fd", "I");
	(*env)->SetIntField(env, fdobj, field_fd, fd);
}
Personal tools