Java Native Interface problems

Discussion in 'Java' started by Stephen Kellett, Jan 27, 2004.

  1. Hi Folks,

    History:
    I'm working on a Java product for blind and partially sighted people.
    The product uses a 3rd party, proprietary speech engine. We started this
    project in the 1997, so we had to use native libraries (which is why we
    aren't using the free Sun Speech engine). As a result, we are still
    using the native interface.

    Problem:
    In the past I've built this native library and used it with no problems,
    using VisualCafe 3.0 and JDK1.1.3. I've recently had to upgrade to
    JDK1.4
    (1.4.2_03 to be exact) to get access to some changes in Java
    functionality. So I'm using NetBeans 3.51 with JDK1.4.

    As a result I've had to rebuild my native libraries - no problem, had to
    change a few library linkages, but all went fine. However when I try to
    use
    the libraries, the correct native library loads, but then any of the
    methods
    gets an Unsatisfied link error. I've tried commenting out complex
    methods
    leaving only the trivial methods and they fail. I've recreated the
    headers
    using javah only to find there is no change between the previous headers
    I
    used and these new ones.

    Included below is the native class definition, the header file and the
    java
    error messages - anyone got any ideas what is going wrong. The correct
    DLL is being loaded, I know that much.

    I hope some of you can help. Replies by email or to the newsgroup will
    be most appreciated.

    Stephen

    Loading native library 'SpeechIO'...
    Native library 'SpeechIO' loaded.
    Jan 27, 2004 12:45:52 PM java.util.jar.Attributes read
    WARNING: Duplicate name in Manifest: Depends-On
    Jan 27, 2004 12:45:52 PM java.util.jar.Attributes read
    WARNING: Duplicate name in Manifest: Depends-On
    Jan 27, 2004 12:45:52 PM java.util.jar.Attributes read
    WARNING: Duplicate name in Manifest: Depends-On
    Jan 27, 2004 12:45:52 PM java.util.jar.Attributes read
    WARNING: Duplicate name in Manifest: Depends-On
    Jan 27, 2004 12:45:52 PM java.util.jar.Attributes read
    WARNING: Duplicate name in Manifest: Depends-On
    Jan 27, 2004 12:45:52 PM java.util.jar.Attributes read
    WARNING: Duplicate name in Manifest: Depends-On
    java.lang.UnsatisfiedLinkError: connect
    at cw.speech.SpeechIO.connect(Native Method)
    at cw.speech.SpeechIO.<init>(SpeechIO.java:76)
    at cw.speech.Speech.initSpeech(Speech.java:997)
    at cw.speech.Speech.<init>(Speech.java:724)
    at cw.wordAloud.CommandHandler.<init>(CommandHandler.java:582)
    at

    cw.wordAloud.LearningDisabledUI.setupCommandHandler(LearningDisabledUI.ja
    va:1621)
    at cw.wordAloud.Spoken.main(Spoken.java:535)

    package cw.speech;

    public class SpeechIO
    {
    public SpeechIO()
    {
    long id = 0;

    try
    {
    // tell the speechio.dll where the product is installed
    // note that we must convert semi colons to something else
    // so that speechIO.dll's parameter parser doesn't mangle
    the
    // values

    String str;
    String dir;

    str = "InstallationDirectory_";
    dir = cw.wordAloud.fileLocations.getInstallationDirectory();
    dir = dir.replace(':', '*');
    str += dir;

    // finally add a dummy value

    str += ":0";

    command(str, 0);

    // now connect to the speechio.dll, this starts the comms
    thread
    // in the speechio.dll so that windows messages get serviced

    id = connect();
    }
    catch (Exception e)
    {
    System.out.println("SpeechIO failed to connect to speech

    engine\n");
    }
    //System.out.println("SpeechIO ID is " + id + ". It should be
    non

    zero");
    }

    public void finalize()
    {
    try
    {
    close();
    }
    catch (Exception e)
    {
    }
    }

    public native long connect();

    public native void close();

    public native void speak(String data,
    int token);

    public native void command(String data,
    int token);

    public native void configure(String data,
    int token);

    public native void convertDocToHTML(String converter,
    String fileToConvert,
    String outputFile,
    String title);

    static
    {
    System.out.println("Loading native library
    'SpeechIO'...");
    try
    {
    System.loadLibrary("speechio");

    System.out.println("Native library 'SpeechIO'

    loaded.");
    }
    catch(UnsatisfiedLinkError e)
    {
    System.out.println("Failed to load SpeechIO

    library");
    System.out.println(e);
    }
    catch(SecurityException e)
    {
    System.out.println("Failed to load SpeechIO

    library");
    System.out.println(e);
    }
    }
    }

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class SpeechIO */

    #ifndef _Included_SpeechIO
    #define _Included_SpeechIO
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
    * Class: SpeechIO
    * Method: connect
    * Signature: ()J
    */
    JNIEXPORT jlong JNICALL Java_SpeechIO_connect
    (JNIEnv *, jobject);

    /*
    * Class: SpeechIO
    * Method: close
    * Signature: ()V
    */
    JNIEXPORT void JNICALL Java_SpeechIO_close
    (JNIEnv *, jobject);

    /*
    * Class: SpeechIO
    * Method: speak
    * Signature: (Ljava/lang/String;I)V
    */
    JNIEXPORT void JNICALL Java_SpeechIO_speak
    (JNIEnv *, jobject, jstring, jint);

    /*
    * Class: SpeechIO
    * Method: command
    * Signature: (Ljava/lang/String;I)V
    */
    JNIEXPORT void JNICALL Java_SpeechIO_command
    (JNIEnv *, jobject, jstring, jint);

    /*
    * Class: SpeechIO
    * Method: configure
    * Signature: (Ljava/lang/String;I)V
    */
    JNIEXPORT void JNICALL Java_SpeechIO_configure
    (JNIEnv *, jobject, jstring, jint);

    // this function added by hand...

    JNIEXPORT void JNICALL Java_SpeechIO_convertDocToHTML(JNIEnv *, jobject,


    jstring, jstring, jstring, jstring);
    #ifdef __cplusplus
    }
    #endif
    #endif
    --
    Stephen Kellett
    Object Media Limited http://www.objmedia.demon.co.uk
    RSI Information: http://www.objmedia.demon.co.uk/rsi.html
    Stephen Kellett, Jan 27, 2004
    #1
    1. Advertising

  2. Hi Stephen,

    your Java code and the javah generated header file do not match:

    > package cw.speech;
    >
    > public class SpeechIO
    > {
    > ...
    > public native long connect();
    > ...
    > }
    >
    > /* DO NOT EDIT THIS FILE - it is machine generated */
    > #include <jni.h>
    > /* Header for class SpeechIO */
    >
    > #ifndef _Included_SpeechIO
    > #define _Included_SpeechIO
    > #ifdef __cplusplus
    > extern "C" {
    > #endif
    > /*
    > * Class: SpeechIO
    > * Method: connect
    > * Signature: ()J
    > */
    > JNIEXPORT jlong JNICALL Java_SpeechIO_connect
    > (JNIEnv *, jobject);
    > ...
    > #ifdef __cplusplus
    > }
    > #endif
    > #endif


    The class SpeechIO is located in the package cw.speech
    The package name is part of the javah generated prototype in the header
    file. This part is missing in your example.

    IMHO you added the package name while changing from JDK 1.1.x to 1.4.x, because
    JDK 1.4 needs packages. If this is true, you must run javah again and adapt
    the name of the native routines to the new names, generated by javah.

    Best,
    Manfred
    Manfred Rosenboom, Jan 27, 2004
    #2
    1. Advertising

  3. In message <bv5pse$9t2$-siemens.com>, Manfred Rosenboom
    <> writes

    >The class SpeechIO is located in the package cw.speech
    >The package name is part of the javah generated prototype in the header
    >file. This part is missing in your example.
    >
    >IMHO you added the package name while changing from JDK 1.1.x to 1.4.x, because
    >JDK 1.4 needs packages.


    Yup. Caused me a lot of confusion the class compiling but other classes
    failing to import it. So I added it to a package just to please the
    compiler.

    >If this is true, you must run javah again and adapt
    >the name of the native routines to the new names, generated by javah.


    Thank you. Thats exactly the response I expected, and its what I looked
    from in the header files. I ran javah again and got the header I posted.
    Anyway thanks for the reply, at least I now know that my instincts were
    right and that something has gone wrong generating the header etc.

    Appreciated.

    Stephen
    --
    Stephen Kellett
    Object Media Limited http://www.objmedia.demon.co.uk
    RSI Information: http://www.objmedia.demon.co.uk/rsi.html
    Stephen Kellett, Jan 27, 2004
    #3
  4. In message <bv5pse$9t2$-siemens.com>, Manfred Rosenboom
    <> writes
    >your Java code and the javah generated header file do not match:


    >The class SpeechIO is located in the package cw.speech
    >The package name is part of the javah generated prototype in the header
    >file. This part is missing in your example.


    I've re-worked everything based on your feedback - I've even examined
    the byte code and sure enough it is stating the class is
    cw/speech/SpeechIO - so the correct source file is compiled. I've made
    javah verbose and it is picking up the correct SpeechIO.class.

    d:\j2sdk1.4.203\bin\javah.exe is not generating any package specific
    header files. As a result I get prototypes of the following form.

    /*
    * Class: SpeechIO
    * Method: command
    * Signature: (Ljava/lang/String;I)V
    */
    JNIEXPORT void JNICALL Java_SpeechIO_command
    (JNIEnv *, jobject, jstring, jint);

    I've no idea what is going wrong. I'll edit the header files myself. My
    question is, given that it is refusing to put in the correct package
    name hints, what form do they take? The package is cw.speech.

    Would it be:
    JNIEXPORT void JNICALL Java_cw_speech_SpeechIO_command
    (JNIEnv *, jobject, jstring, jint);

    or is it something else?

    Regards

    Stephen
    --
    Stephen Kellett
    Object Media Limited http://www.objmedia.demon.co.uk
    RSI Information: http://www.objmedia.demon.co.uk/rsi.html
    Stephen Kellett, Jan 27, 2004
    #4
  5. On Tue, 27 Jan 2004 15:05:14 +0000, Stephen Kellett wrote:
    > d:\j2sdk1.4.203\bin\javah.exe is not generating any package specific
    > header files.


    You need to specify a fully qualified classname when you run javah, if
    you want it to generate the package information.

    /gordon

    --
    [ do not email me copies of your followups ]
    g o r d o n + n e w s @ b a l d e r 1 3 . s e
    Gordon Beaton, Jan 27, 2004
    #5
  6. In message <4016882c$>, Gordon Beaton <>
    writes
    >On Tue, 27 Jan 2004 15:05:14 +0000, Stephen Kellett wrote:
    >> d:\j2sdk1.4.203\bin\javah.exe is not generating any package specific
    >> header files.

    >
    >You need to specify a fully qualified classname when you run javah, if
    >you want it to generate the package information.


    Thanks - that was certainly not obvious.

    The class file and the java file both contain information that indicate
    the package. If I'm sitting in directory cw/speech and run javah on
    SpeechIO and it picks up ./SpeechIO.class then it should be clever
    enough to think 'Aha! The class name is "cw/speech/SpeechIO"' as that is
    what is encoded in the byte code (I checked).

    Once again, thanks, I've changed things and now have it working.

    Stephen
    --
    Stephen Kellett
    Object Media Limited http://www.objmedia.demon.co.uk
    RSI Information: http://www.objmedia.demon.co.uk/rsi.html
    Stephen Kellett, Jan 27, 2004
    #6
  7. Stephen Kellett

    Chris Smith Guest

    Stephen Kellett wrote:
    > Thanks - that was certainly not obvious.
    >
    > The class file and the java file both contain information that indicate
    > the package. If I'm sitting in directory cw/speech and run javah on
    > SpeechIO and it picks up ./SpeechIO.class then it should be clever
    > enough to think 'Aha! The class name is "cw/speech/SpeechIO"' as that is
    > what is encoded in the byte code (I checked).


    I agree that it should check the bytecode of the class file against the
    class name you've given, and fail with a helpful error message if they
    don't match. I certainly don't agree that it should continue to run
    even though you've given it the wrong class name!

    --
    www.designacourse.com
    The Easiest Way to Train Anyone... Anywhere.

    Chris Smith - Lead Software Developer/Technical Trainer
    MindIQ Corporation
    Chris Smith, Jan 27, 2004
    #7
  8. In message <4.net>, Chris Smith
    <> writes
    >Stephen Kellett wrote:
    >> Thanks - that was certainly not obvious.
    >>
    >> The class file and the java file both contain information that indicate
    >> the package. If I'm sitting in directory cw/speech and run javah on
    >> SpeechIO and it picks up ./SpeechIO.class then it should be clever
    >> enough to think 'Aha! The class name is "cw/speech/SpeechIO"' as that is
    >> what is encoded in the byte code (I checked).

    >
    >I agree that it should check the bytecode of the class file against the
    >class name you've given, and fail with a helpful error message if they
    >don't match. I certainly don't agree that it should continue to run
    >even though you've given it the wrong class name!


    If I'm in cw/speech, that implies I am in package cw.speech, therefore
    specifying SpeechIO is correct as that is specify cw.speech implicitly.
    The byte code agrees with this position, as it correctly identifies the
    class as cw.speech.SpeechIO. I have to disagree with you.

    If I specify cw.speech.SpeechIO whilst in cw/speech it fails with an
    error message. I need to do:
    cd ..
    cd ..
    then
    specify cw.speech.SpeechIO
    which produces the header file, in the wrong directory of course.
    "Wrong" being "not the same directory as the class". Yes there is an
    option for this, I just think the program does not work in a logical
    manner.

    Either way, I've got the program working again and I'm thankful for the
    help provided.
    --
    Stephen Kellett
    Object Media Limited http://www.objmedia.demon.co.uk
    RSI Information: http://www.objmedia.demon.co.uk/rsi.html
    Stephen Kellett, Jan 27, 2004
    #8
  9. On Tue, 27 Jan 2004 16:31:03 +0000, Stephen Kellett wrote:
    > Thanks - that was certainly not obvious.


    One way to avoid the symbol naming issue is to use RegisterNatives()
    from the JNI_OnLoad() function in your library. There doesn't need to
    be any correlation between the names of your native methods and the
    ones generated by javah, package or no package.

    Define a set of methods and call them what you like, but obey the
    signatures described by javah. Then define an array mapping the method
    names you declared in the java source to the names you used in the C
    source:

    static JNINativeMethod all_methods[] = {
    { "foo", "()V", do_foo },
    { "baz", "()V", do_baz },
    };

    static int n_methods = sizeof(all_methods)/sizeof(*all_methods);

    One caveat if you're using C++, note that your native methods must be
    declared "extern C". That happens automatically if you use the exact
    same names as in the header file, but if you break the naming
    convention you need to declare them that way yourself.

    JNI_OnLoad() will be invoked when the library is loaded. At that time,
    you can register your native functions "manually" with the following
    code. You need to provide the (fully qualified!) classname in just one
    place:

    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
    {
    JNIEnv *env;
    jclass cls;

    if ((*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_2) == JNI_OK) {

    if ((cls = (*env)->FindClass(env,"my/package/MyClass"))) {
    if (!((*env)->RegisterNatives(env,cls,all_methods,n_methods))) {
    return JNI_VERSION_1_2;
    }
    }

    if ((*env)->ExceptionOccurred(env)) {
    (*env)->ExceptionDescribe(env);
    }
    }

    fprintf(stderr,"%s:%d: JNI_OnLoad failed\n",__FILE__,__LINE__);
    return -1;
    }

    I find this helps make my native code more reader friendly, and I
    don't need to cut and paste a whole set of method declarations from
    the generated header file to the source if I change the package
    declaration of a class. Just one line (usually a constant macro at the
    start of the file) needs to be changed.

    /gordon

    --
    [ do not email me copies of your followups ]
    g o r d o n + n e w s @ b a l d e r 1 3 . s e
    Gordon Beaton, Jan 27, 2004
    #9
  10. In message <>, Gordon Beaton <>
    writes
    >On Tue, 27 Jan 2004 16:31:03 +0000, Stephen Kellett wrote:
    >> Thanks - that was certainly not obvious.

    >
    >One way to avoid the symbol naming issue is to use RegisterNatives()
    >from the JNI_OnLoad() function in your library. There doesn't need to
    >be any correlation between the names of your native methods and the
    >ones generated by javah, package or no package.


    Nice, I didn't know about that - been doing a lot of C++ for while,
    hence out of the loop on Java a bit (although very in the loop with
    JVMDI and JVMPI).

    Stephen
    --
    Stephen Kellett
    Object Media Limited http://www.objmedia.demon.co.uk
    RSI Information: http://www.objmedia.demon.co.uk/rsi.html
    Stephen Kellett, Jan 27, 2004
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Kristian Karl

    Java native interface problem

    Kristian Karl, Sep 18, 2004, in forum: Java
    Replies:
    1
    Views:
    504
    Tor Iver Wilhelmsen
    Sep 19, 2004
  2. Clemens Arth
    Replies:
    3
    Views:
    8,397
    Alf P. Steinbach
    Oct 11, 2004
  3. Replies:
    2
    Views:
    519
  4. Replies:
    13
    Views:
    6,076
  5. Replies:
    3
    Views:
    398
    Lawrence Kirby
    Feb 28, 2005
Loading...

Share This Page