Skip site navigation (1)Skip section navigation (2)
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>