FileInputStream
to use that open file descriptor?
Java does have a FileDescriptor
class. At first glance,
that would seem to be the answer. The only trouble is, there is only one
constructor, and it gives you an 'invalid' one. Why did they bother?
The answer is that just because there is no apparent way to get a FileDescriptor to point to an arbitrary open file, that doesn't mean there is no way to do it.
Set up your native method in the usual manner, but make sure it returns a FileDescriptor. Something like this:
public class myDevice { private native static prepare_fd(String filename, FileDescriptor fdobj); private FileInputStream in, out; public myDevice() { FileDescriptor myDev = prepare_fd("/dev/tty.modem", myDev); in = new FileInputStream(myDev); out = new FileOutputStream(myDev); } }So far, so good. We now have the Java side set up to call our native code, which will return a
FileDescriptor
that
refers to the device we will be opening.
I'm not going to detail the vagueries of JNI and how you write it. The
trick in prepare_fd
is to just assume that the
FileDescriptor object has an integer field called fd
. If
you stuff the number you get from the open()
system call,
or from calling fileno()
on a FILE *
, it will
magically work.
Here's an example:
JNIEXPORT jobject JNICALL Java_Example_prepare_1fd(JNIEnv *env, jclass _ignore, jstring filename) { jfieldID field_fd; jmethodID const_fdesc; jclass class_fdesc, class_ioex; jobject ret; int fd; char *fname; class_ioex = (*env)->FindClass(env, "java/io/IOException"); if (class_ioex == NULL) return NULL; class_fdesc = (*env)->FindClass(env, "java/io/FileDescriptor"); if (class_fdesc == NULL) return NULL; fname = (*env)->GetStringUTFChars(env, filename, NULL); fd = open(fname, O_RDWR | O_NONBLOCK); (*env)->ReleaseStringUTFChars(env, filename, fname); if (fd < 0) { // open returned an error. Throw an IOException with the error string char buf[1024]; sprintf(buf, "open: %s", strerror(errno)); (*env)->ThrowNew(env, class_ioex, buf); return NULL; } // construct a new FileDescriptor const_fdesc = (*env)->GetMethodID(env, class_fdesc, "<init>", "()V"); if (const_fdesc == NULL) return NULL; ret = (*env)->NewObject(env, class_fdesc, const_fdesc); // poke the "fd" field with the file descriptor field_fd = (*env)->GetFieldID(env, class_fdesc, "fd", "I"); if (field_fd == NULL) return NULL; (*env)->SetIntField(env, ret, field_fd, fd); // and return it return ret; }