Thinkpad Accelerometer with Java JNI

Man I have a lot of things to do. But I can't help thinking about my aunt tilting the laptop for Crayon physics. It turns out that the accelerometer on the Thinkpad can actually be accessed:
  1. C# code [] and a very nice version from bsuter.[]
  2. Perl code []
  3. More C# code with a writeup.
Shocking that IBM doesn't have a Java library for it! Well, I got a bit excited that I might be the first to write the code...of course, I've never written JNI code before in my life. I've heard its tricky, and I know it complicates deployment (I've had to package and deploy JNI code before). So it seems that Sun's JNI programmers guide is the place to go. I skimmed it and came to this section on wrapping existing native libraries. It's pretty hairy, especially compared to the perl code, but hey this is Java right?

Undaunted I downloaded the example code from the book, thinking I could just compile the CFuntion, etc classes and be on my way. I fire up Cygwin, discover I don't have gcc or make installed - which was easily corrected. (And I noticed that an ocaml compiler was available, so I installed it too for giggles.)

I run into my first roadblock - make goes into infinite recursion and I have to kill the process. I read the makefile. It appears that the win32 makefiles provided with the distro are written for nmake - Microsoft's rewrite of gnu make. I'd never heard of it before. (Good heavens, how many make clones does the world really need?)

Probably the easiest thing to do would be to just install Visual Studio, if I had the disk space (I have a bigger drive on order, actually). If I knew make better I could edit the makefile for compatibility. But I suspect there may be other things in the codebase that require VS. Or I could scrap my original plan and write custom "one-to-one mapping" code, and hope that gcc will cut the mustard. That might be worth a shot (and more educational) while I wait for my new hard drive.

So I gave HelloWorld a shot. Alas, it didn't work (using the command line "gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/win32 HelloWorld.c") - the parser didn't like the header files. What I really need is a version of this book for Cygwin, as I don't have the expertise to figure it out myself. The internet to the rescue! (Hmm and he mentions SWIG. Interesting.)

Well I was damn close. "gcc -mno-cygwin -I$JAVA_HOME/include -I$JAVA_HOME/include/win32 -Wl,--add-stdcall-alias -shared -o HelloWorld.dll HelloWorld.c" did the trick.

(Note that when I first tried to run I got a "java.lang.UnsupportedClassVersionError: Bad version number in .class file". A "which java" in cygwin showed that I was running a JRE in "/cygdrive/c/WINDOWS/system32/java" while compiling with a different one. So I just ran it with the right JRE and it worked! "$JAVA_HOME/bin/java HelloWorld". There are lots of ways to fix cygwin systematically - I'll probably make the change globally to the PATH in /etc/.bashrc)

I don't hold JNI itself responsible for this learning curve of mine - I don't do C/C++ development in Cygwin (or out of it), and that's caused the majority of the trouble. It's true that I think it *should* be possible to drag a DLL into a tool and have it's functions exposed in whatever language you like, automatically.

After reading the mindprod JNI article (which I should have done first - Roedy Green really gets into this stuff and is generous with his knowledge) I think the biggest thing holding me back is not having the source to sensor.dll source code. There are several "SharedStub" pre-compiled libraries out there (JNative, JNIWrapper - commercial) which claim to avoid writing and compiling any C (at least under Windows and Linux). SWIG claims to avoid writing any C - but you have to compile it (and it's not clear how you compile for Windows vs. Linux). The super cool thing about SWIG is once you write the ".i" file you can generate bindings for 13 different languages, not just Java. I might even get inspiration from how Eclipse generates JNI for SWT (something called JNIGeneratorAppUI). I discovered that the Shared Stub C code from the Sun book is on the site repackaged and distrubuted with a normal make file! Perhaps that's the code that JNative is based on?

One problem I cannot solve is figuring out which functions "sensors.dll" exposes. Static or dynamic, all of these JNI methods basically require a header file to work with. Seems to me a library would be pretty up-front about the functions it exposes. A tool to generate SWIG .i files from a dll would be pretty cool - if overkill. If that's not available, then a tool that generates .h files from a .dll.

If only this data was available via a port or a file handle! Oddly, the file handle method is the one used in C# and Perl. I don't really understand it though. Ironically, Mark Smith at IBM started a project to make accelerometer data available in Linux and is now open source. (This is ironic for two reasons: first, that the Linux form would be easier to use with Java, and second that Mark had to reverse-engineer his own companies software - but come to think of it that happens all the time.)

[Update: According to MSDN, that strange path-like structure is a "communication resource handle" on which there are several possible operations. These operations are fully documented (for example the CreateFile function).

Oddly, I noticed similar path strings in Windows Services. Take this executable path: "C:\bin\Tomcat 5.5\bin\tomcat5.exe" //RS//Tomcat5. My guess is this is an internal attempt to avoid running tomcat twice, similar to using a PID file. But that's probably not what it is since tomcat won't start again because of port conflicts - a sort of port PID.

Here's another - "C:\bin\Tomcat 5.5\bin\tomcat5w.exe" //MS//Tomcat5 (for monitoring tomcat)


Anonymous said...

You can do an almost direct port from .NET (P/Invoke) to J/Invoke for Java. Check out for a pure Java example instead of using JNI

josh said...

Thanks for the tip.

It seems like there's a lot of these libraries floating around! xFunction comes to mind. And then there's the open source one JNative.

What isn't clear to me though is how these differ from the Sun CFunction code provided with their book on JNI.