Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Integrate Java and C++ with Jace

The open source Jace toolkit helps simplify JNI programming

  • Print
  • Feedback

Page 3 of 4

  • Reference types may be null. You can detect if a C++ proxy class refers to a null Java object by calling JObject::isNull():

      /**
       * @param jstring - A java.lang.String
       */
      JNIEXPORT void JNICALL Java_foo_Bar_someMethod( JNIEnv *env, jstring
    javaString ) {
        String str( javaString );
        if ( str.isNull() ) {
          cout << "Error - The argument, javaString, must not be
    null." << endl;
        }
      }
    


  • Throwable and String

    Both the Throwable and String C++ proxy classes derive from JObject (as do all reference-type proxy classes). These classes are included (along with a few others) as a core part of the Jace library to provide users with a tighter integration between the C++ and Java languages. (I discuss this in more detail in the "C++ Integration" section.)

    Jace features

    Jace sports several features, including thread management, exception management, and automatic type conversions, among others. Let's examine those features and more.

    Thread management

    In JNI, a few thread issues exist:

    • JNIEnv pointers are only valid on the thread from which they are obtained
    • Similarly, most JNI types are only valid on the thread from which they are obtained
    • C++ threads must be attached to the JVM before calling JNI functions


    Jace addresses all these issues. First, every function in the Jace library automatically retrieves a JNIEnv pointer that is valid for the current thread.

    Second, Jace creates global references to the necessary JNI types. For example, upon construction, a JClass creates a global reference to its jclass member, and a JObject creates a global reference to its jobject member. Unlike local references, which are only valid for the current thread, global references are valid across all threads.

    Finally, every function in Jace ensures that the current thread is attached to the JVM before calling any JNI functions.

    Exception management

    Exception handling is one bane of JNI programming. Jace has two policies for exception handling:

    1. Jace checks the return code of every JNI function it executes. If an error occurs, Jace clears the JNI exception and then throws Jace's JNIException.
    2. If Jace determines that a Java method's invocation has failed because the method has thrown an exception, Jace clears the JNI exception, examines the thrown exception, creates a C++ proxy instance for that exception, and throws the C++ proxy:
        using namespace jace::java::net;
        void readGoogle() {
          try {
            /* When Jace internally executes NewObject, it checks to 
             * see if an exception is thrown. If the JNI function 
             * ExceptionOccurred, returns an exception, Jace clears the 
             * exception, creates a corresponding C++ proxy, and throws it.
             */
            URL url( "http://www.google.com" );
          }
          /* Here, you catch the Jace thrown C++ proxy exception.
           */
          catch ( MalformedURLException& e ) {
            cout << e;
          }
        }
      


    Automatic type conversion

    Jace provides automatic type conversions between C++ and Java primitive types. You may use a C++ std::string or char* anywhere a C++ proxy requires a java::lang::String. You may also use C++ types, like bool, int, and char, where C++ proxy methods require primitive JNI types, like JBoolean, JInt, and JChar:

      using jace::javax::swing::JFrame;
      JFrame createFrame( const std::string& title, int x, int y ) {
        /* The prototype for JFrame is JFrame( java::lang::String str );.
         * Jace automatically converts between std::string and java::lang::String.
         */
        JFrame frame( title );
        /* The prototype for setLocation is setLocation( JInt x, JInt y );.
         * Jace automatically converts between int and JInt.
         */
        frame.setLocation( x , y ); 
        return frame;
      }
    


    C++ integration

    Jace includes a C++ proxy-generating tool, BatchGen. The Jace community used BatchGen on the Java Runtime Environment (JRE)'s rt.jar to generate the C++ proxy classes, which are included in the Jace C++ runtime library. However, the Jace community has modified a few of those generated proxy classes for tighter integration with the C++ language and standard library.

    java.lang.Object, for example, has an added operator<<( ostream& out, Object& object );, which lets you write any Object as you would using System.out.println().

    java.lang.String has several added methods to integrate itself with std::strings and char*s, including operator+(), operator=(), and operator==().

    The community has also modified java.lang.Throwable to derive from std::exception.

    Type-safe field and method access

    C++ proxy generation is the basis of type-safe access to Java objects. For any given Java classfile, Jace can generate a C++ proxy class that has all the same methods and fields. You call the C++ proxy methods in the same way you would call their Java counterparts. Fields are accessed through methods of the same name:

      /* A Java class
       */
      public class Foo {
        public int aField;
        public String aMethod( URL aURL );
      }
      /* You access the C++ proxy from C++
       */
      Foo foo;
      foo.aField() = 14;
      String result = foo.aMethod( URL( "http://www.google.com" ) );
    


    Jace provides two tools—ProxyGen and BatchGen—that you can use to generate C++ proxy classes from Java classfiles. I describe these tools in this article's "Jace Tools" section.

    Type-safe arrays

    You can use Jace's template JArray class to access Java arrays type-safely. Behind the scenes, Jace calls the appropriate Get<Type>ArrayElement() and Set<Type>ArrayElement() JNI functions as dictated by the array type:

      JArray<JInt> intArray( 10 ); // Results in a call to NewIntArray
      int i = intArray[ 2 ]; // Results in a call to GetIntArrayElements
      JArray<String> stringArray( 5 ); // Results in a call to NewObjectArray
      std::string str = stringArray[ 2 ]; // Results in a call to GetObjectArrayElement
    


    Jace tools

    As mentioned previously, ProxyGen and BatchGen generate C++ proxy classes. ProxyGen generates the header and footer for a classfile. BatchGen generates the headers and footers for all classes in a jar file.

    ProxyGen

    ProxyGen dumps the header file or source file for a Java classfile to standard output. ProxyGen always includes public methods and fields in the generated C++ proxy class. Depending upon the access level specified, it will also include protected, package, or private fields and methods:

      Usage: ProxyGenerator <class file> <header | source> [ options ]
      Where options can be:
        -protected : Generate protected fields and members
        -package : Generate package fields and members
        -private : Generate private fields and members
    


    BatchGen

    BatchGen is similar to ProxyGen in that it generates header and source files for C++ proxy classes. However, instead of reading from one Java classfile, BatchGen reads from a jar or zip file of multiple classfiles. Also, instead of writing the header and source files to standard output, BatchGen writes them to the specified destination directories:

    • Print
    • Feedback

    Resources