Date: Sat, 1 Jan 2000 12:17:53 -0500 (EST) From: Brian Clapper <bmc@WillsCreek.COM> To: freebsd-java@FreeBSD.ORG Subject: Re: java JNI compile problems Message-ID: <200001011717.MAA47567@footbridge.willscreek.com> In-Reply-To: <14446.5474.172635.556466@vega.sudell.org> References: <386D454B.694B5093@1connect.com> <14446.5474.172635.556466@vega.sudell.org>
next in thread | previous in thread | raw e-mail | index | archive | help
Perhaps this will help. I've enclosed a shell archive containing a simple JNI test that works fine for me on FreeBSD 3.1-RELEASE, with the latest JDK. It worked fine with the 1.1.6 JDK, too, which was the latest one available when I built this test more than a year ago. I used a shar file, because I'm not sure what the list server will do to a MIME attachment; seemed safer to use shar. The archive contains four files: Makefile SystemCommandOutputReader.java SystemCommandOutputReader.c CommandTest.java SystemCommandOutputReader.{c,java} implements a command-runner class that uses native methods to permit a Java program to issue a command and read its output. CommandTest.java is a simple test driver. To run the test: 1. Unpack the shell archive. Make sure you have all four files. 2. Make sure you have GNU make installed, or be prepared to hack the Makefile a bit more than I've described here. 3. Edit the appropriate variables at the top of the Makefile--specifically, JAVA_HOME. If you don't have the jikes port installed, you'll also need to modify the setting of JAVAC. 4. Type "gmake" 5. Make sure that the current directory is in your LD_LIBRARY_PATH, and type something like: java CommandTest ls -CF /var If it works, you should see output similar to this: Command output follows: ----------------------- account/ cron/ mail/ rwho/ at/ db/ msgs/ spool/ backups/ games/ preserve/ tmp/ crash/ log/ run/ yp/ I apologize for the almost complete lack of documentation in the code. It's not used in production; I hacked it together just to provide a template for doing JNI. It's not that difficult to adapt to a Windows environment, BTW; I've done that, too, in the past. Hope this helps. Regards, -Brian Brian Clapper, bmc@WillsCreek.COM, http://WWW.WillsCreek.COM/ The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore all progress depends on the unreasonable man. -- George Bernard Shaw #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: CommandTest.java Makefile SystemCommandOutputReader.c # SystemCommandOutputReader.java # Wrapped by bmc@current.willscreek.com on Sat Jan 1 12:14:20 2000 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'CommandTest.java' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'CommandTest.java'\" else echo shar: Extracting \"'CommandTest.java'\" \(1824 characters\) sed "s/^X//" >'CommandTest.java' <<'END_OF_FILE' Ximport java.io.*; Ximport java.lang.*; X X// SystemCommandOutputReader class is currently in the default package. X Xpublic class CommandTest X{ X // Main program, for testing the SystemCommandOutputReader class. X X public static void main(String[] args) X { X int exitCode = 0; X X if (args.length == 0) X { X System.err.println ("Usage: java Command command [args] ..."); X exitCode = 1; X } X X else X { X String cmdString = args[0]; X int i; X X for (i = 1; i < args.length; i++) X cmdString = cmdString + " " + args[i]; X X System.out.println ("Command = \"" + cmdString + "\""); X exitCode = runCommand (cmdString); X } X } X X private static int runCommand (String cmdString) X { X int exitCode = 0; X SystemCommandOutputReader cmd = new SystemCommandOutputReader(); X X // Start the command. X X try X { X cmd.start (cmdString); X } X X catch (IOException e) X { X System.err.println ("Can't run \"" + cmdString + "\": " X + e.toString()); X exitCode = 1; X cmd = null; X } X X // Now, read its output. X X try X { X int c; X X System.out.println ("Command output follows:"); X System.out.println ("-----------------------"); X X while ( (c = cmd.read()) > 0 ) X { X System.out.write (c); X } X } X X catch (Exception e) X { X System.err.println ("Error reading from command: " + e.toString()); X exitCode = 1; X } X X if (cmd != null) X cmd.convenientClose(); X X return exitCode; X } X} END_OF_FILE if test 1824 -ne `wc -c <'CommandTest.java'`; then echo shar: \"'CommandTest.java'\" unpacked with wrong size! fi # end of 'CommandTest.java' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(869 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' XJAVA_HOME = /usr/local/java/jdk1.1.8 XJAVA_BIN = $(JAVA_HOME)/bin X#JAVAC = $(JAVA_BIN)/javac XJAVAC = /usr/local/bin/jikes XJAVAH = $(JAVA_BIN)/javah XJAR = $(JAVA_BIN)/jar XC_INCLUDES = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/freebsd -I. X X.SUFFIXES: .class .java X X%.class: %.java X $(JAVAC) $< X X%.h: %.class X $(JAVAH) -jni $* X X%.o: %.c X $(CC) -Wall -c $< $(C_INCLUDES) X X.PHONY: Command all X Xall: Command Xclean: X rm -f *.class *.o *.h *.so X XHelloWorld: HelloWorld.class libhello.so XHelloWorld.o: HelloWorld.c HelloWorld.h Xlibhello.so: HelloWorld.o X $(CC) -o libhello.so -shared HelloWorld.o X XCommand: SystemCommandOutputReader.class CommandTest.class libCommand.so X XSystemCommandOutputReader.o: SystemCommandOutputReader.h \ X SystemCommandOutputReader.c X XlibCommand.so: SystemCommandOutputReader.o X $(CC) -o libCommand.so -shared SystemCommandOutputReader.o END_OF_FILE if test 869 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'SystemCommandOutputReader.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'SystemCommandOutputReader.c'\" else echo shar: Extracting \"'SystemCommandOutputReader.c'\" \(4059 characters\) sed "s/^X//" >'SystemCommandOutputReader.c' <<'END_OF_FILE' X/*---------------------------------------------------------------------------*\ X $Id$ X X Native implemenation of SystemCommandOutputReader X\*---------------------------------------------------------------------------*/ X X/*---------------------------------------------------------------------------*\ X Includes X\*---------------------------------------------------------------------------*/ X X#include <stdio.h> X#include <unistd.h> X#include "SystemCommandOutputReader.h" X X/*---------------------------------------------------------------------------*\ X Local Routines X\*---------------------------------------------------------------------------*/ X Xstatic FILE *getFP (JNIEnv *env, jobject obj) X{ X jclass cls = (*env)->GetObjectClass (env, obj); X jfieldID fid = (*env)->GetFieldID (env, cls, "nativeData", "[B"); X jbyteArray array = (*env)->GetObjectField (env, obj, fid); X FILE *fp; X X /* X The file pointer (address) is stored in the nativeData instance variable X array. Copy it back somewhere useful. X */ X (*env)->GetByteArrayRegion (env, array, 0, sizeof (fp), (jbyte *) &fp); X return fp; X} X Xstatic void setFP (JNIEnv *env, jobject obj, FILE *fp) X{ X jclass cls = (*env)->GetObjectClass (env, obj); X jfieldID fid = (*env)->GetFieldID (env, cls, "nativeData", "[B"); X jbyteArray array = (*env)->GetObjectField (env, obj, fid); X X /* X Copy the contents of the file pointer into the nativeData instance X variable. getFP() will restore the pointer from the same array. X */ X (*env)->SetByteArrayRegion (env, array, 0, sizeof (fp), (jbyte *) &fp); X return; X} X X Xstatic void throwException (JNIEnv *env, jobject obj, X const char *exceptionName, X const char *reason) X{ X jclass exc; X X /* X Note: This code fragment was adapted from code in the Java JNI tutorial. X */ X (*env)->ExceptionDescribe (env); X (*env)->ExceptionClear (env); X X exc = (*env)->FindClass (env, exceptionName); X if (exc != 0) X (*env)->ThrowNew (env, exc, reason); X X return; X} X Xstatic void doClose (JNIEnv *env, jobject obj, FILE *fp) X{ X if (fp != NULL) X { X fclose (fp); X setFP (env, obj, NULL); X } X X return; X} X X/*---------------------------------------------------------------------------*\ X Native Methods X\*---------------------------------------------------------------------------*/ X XJNIEXPORT jint JNICALL XJava_SystemCommandOutputReader_available (JNIEnv *env, jobject obj) X{ X return 0; X} X XJNIEXPORT void JNICALL XJava_SystemCommandOutputReader_close (JNIEnv *env, jobject obj) X{ X doClose (env, obj, getFP (env, obj)); X return; X} X XJNIEXPORT jint JNICALL XJava_SystemCommandOutputReader_nativeDataSize (JNIEnv *env, jobject obj) X{ X return sizeof (FILE *); X} X XJNIEXPORT jint JNICALL XJava_SystemCommandOutputReader_read (JNIEnv *env, jobject obj) X{ X char c; X FILE *fp; X int count; X jint result = -1; X X if ( (fp = getFP (env, obj)) == NULL ) X { X throwException (env, obj, "java/io/FileNotFoundException", X "command not started"); X } X X else if ( (count = fread (&c, sizeof (c), 1, fp)) == 0 ) X { X result = 0; X doClose (env, obj, fp); X } X X else if (count < 0) X { X throwException (env, obj, "java/io/IOException", "read error on pipe"); X } X X else X { X result = (jint) c; X } X X return result; X} X XJNIEXPORT void JNICALL XJava_SystemCommandOutputReader_start (JNIEnv *env, jobject obj, X jstring commandString) X{ X FILE *p; X const char *cmd = (*env)->GetStringUTFChars (env, commandString, 0); X X p = popen (cmd, "r"); X (*env)->ReleaseStringUTFChars (env, commandString, cmd); X X if (p == NULL) X throwException (env, obj, "java/io/IOException", "can't run command"); X X else X setFP (env, obj, p); X X return; X} END_OF_FILE if test 4059 -ne `wc -c <'SystemCommandOutputReader.c'`; then echo shar: \"'SystemCommandOutputReader.c'\" unpacked with wrong size! fi # end of 'SystemCommandOutputReader.c' fi if test -f 'SystemCommandOutputReader.java' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'SystemCommandOutputReader.java'\" else echo shar: Extracting \"'SystemCommandOutputReader.java'\" \(2632 characters\) sed "s/^X//" >'SystemCommandOutputReader.java' <<'END_OF_FILE' Ximport java.io.*; Ximport java.lang.*; X Xpublic class SystemCommandOutputReader extends InputStream X{ X /*----------------------------------------------------------------------*\ X Private Data Elements X \*----------------------------------------------------------------------*/ X X // nativeData[] is basically just a buffer into which the native X // methods can store data that can't easily be represented as Java X // types (e.g., pointers). The native method nativeDataSize() X // determines how many bytes to allocate. X X private byte nativeData[]; X X /*----------------------------------------------------------------------*\ X Static Initializer X \*----------------------------------------------------------------------*/ X X static X { X // Load the shared library containing the native methods as soon as X // this file is read. X X System.loadLibrary ("Command"); X } X X /*----------------------------------------------------------------------*\ X Constructor X \*----------------------------------------------------------------------*/ X X public SystemCommandOutputReader() X { X super(); X nativeData = new byte [nativeDataSize()]; X } X X /*----------------------------------------------------------------------*\ X Public Methods X \*----------------------------------------------------------------------*/ X X public native int available() throws IOException; X X public native void close() throws IOException; X X public void convenientClose() X { X try X { X close(); X } X X catch (IOException e) X { X } X X return; X } X X public boolean markSupported() X { X return false; X } X X public synchronized void mark (int readlimit) X { X // no-op X X return; X } X X public native int read() throws IOException; X X public synchronized void reset() throws IOException X { X throw new EOFException ("reset() unavailable for class Command"); X } X X public long skip (long n) throws IOException X { X long skipped = 0; X X while ( (read() > 0) && (skipped < n) ) X skipped++; X X return skipped; X } X X public native void start (String command) throws IOException; X X /*----------------------------------------------------------------------*\ X Private Methods X \*----------------------------------------------------------------------*/ X X private native int nativeDataSize(); X} X END_OF_FILE if test 2632 -ne `wc -c <'SystemCommandOutputReader.java'`; then echo shar: \"'SystemCommandOutputReader.java'\" unpacked with wrong size! fi # end of 'SystemCommandOutputReader.java' fi echo shar: End of shell archive. exit 0 To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-java" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200001011717.MAA47567>