JNI++ Project: Aim and Objectives

Klaus Wiederänders
previous home  next

last revision: Tue Jun 13 17:32:26 2000

1. Overview

This paper describes in short the aim and objectives of the JNI++ open-source project. Since the JNI++ is, at present, not much more than an idea and vision, it takes the only thing already materialized: a simple but working example of couple of native C++ classes subclassed by a Java subclass. However, this example shows both sides of the business
  • calling methods of native (super)classes from a Java (sub)class
  • overriding virtual functions of native superclasses by a Java subclass
  • passing native C++ objects back to and accessing these from out of Java.
Later on the example will expand to
  • Java/native type conversion problems as well as
  • Java beans
  • Java servlets
As the name of the project already confesses, the native language in question is C++. However, the discussion, as long as the Java part is involved, is not limited to C++. On the other hand, the introduced principles of subclassing native classes hold not only for Java, but, e.g., for Perl as well. As a matter of fact, all these are proven to be applicable for subclassing C++ by Perl, too.

2. JNI Programmer's Dream

Suppose a valuable and mature C++ library full of usefull classes designed in an object-oriented way, i.e. prepared for being subclassed to fit the customers needs. Suppose, furthermore, the rather strange but not at all rare situation, that the source code of this library is not avail- or changable. Nethertheless this library is to be included into some nice Java project. And this is how the programmer might dream about how to do such job:

2.1. The Example's Native Class'es Definitions

These are the definitions of the classes contained in the sample C++ library:


class Bar

#ifndef Bar_h
#define Bar_h

/*
{group:Native}
Summary:
sample C++ superclass for demonstrating Java/C++ subclassing with JNI++
 
Purpose:
This simple C++ class is part of the JNI++ introductory example. 

Note: The main task of Bar in the JNI++ context is to act as superclass of C++ class Foo
which, in turn, is subclassed by the Java class SubFoo. For this purpose, 
it provides the virtual bar() method, which is overriden by the Java subclass SubFoo.
*/
class FOOBAREXPORT Bar
{
public:

/*
Summary: This constructs a Bar instance

Parameters:
  message - a text message

Purpose:
  Initializes the data member of Bar. Class Bar stores the message passed to its 
constructor in a private member variable.

Note: 
  In the JNI++ example this is used for demonstrating calls to the C++ superclass 
constructor through a class hierarchy during creation of a Java subclass including 
parameter passing.
*/
Bar( const char * message );

/*
Summary: Destroys the instance.

Purpose:
  The destructor frees the stored message
*/
virtual ~Bar();


/*
Summary:
Member function printing some messages

Parameters:
message - a text message (string passed from Java to C++ to C++)
  object  - a Bar instance (object passed from Java to C++ to C++)

Returns:
  the object passed as parameter.

Purpose:
  A call to this virtual function writes a line to the standard output 
containing the message passed and the messaged stored in the object passed. 
Then this object is returned as result value to bar's caller.
Since bar() is a virtual function, the subclass's override of bar() gets 
called, if any.

Note: In the JNI++ example this is the key member: a virtual function overridden
by some descent Java subclass.
*/
virtual Bar * bar( const char * message, Bar * object );

/*
Summary:
Member function printing some messages

Parameters:
message - a text message (string passed from Java to C++ to C++)
  object  - a Bar instance (object passed from Java to C++ to C++)

Returns:
  the object passed as parameter.

Purpose:
  A call to this function writes a line to the standard output containing 
the message passed and the messaged stored in the object passed. 
Then the virtual(!) function bar is called with message and object propagated 
as arguments. Finally, the reply of this call is returned to foo's caller.

Note: In the JNI++ example, this is the key member: the virtual function bar() 
is overridden by the descent Java subclass SubFoo. Thus, the call to bar() 
inside foo() leads to the call of the Java override bar() of SubFoo! This is
the real point of JNI++!
*/
Bar * foo( const char * message, Bar * object );

/*
Summary:
Get accessor for thisMessage

Returns:
  the value of the data member thisMessage

Purpose:
  This is the usual get accessor for the private data member thisMessage.

*/
const char * getMessage() const;

private:
/* {secret} */
char * thisMessage;
};

#endif
This is a very simple class defining a constructor, a virtual destructor, a non-virtual function foo(), and a virtual function bar(). The virtual function bar() is going to be overridden by a Java subclass. Most of the source is inline documentation, which can be viewed following this link.
Both functions will be called by the following C++ subclass also contained in the sample C++ library:


class Foo

#ifndef Foo_h
#define Foo_h

#include "Bar.h"

/*
{group:Native}
Summary:
sample C++ subclass for demonstrating Java/C++ subclassing with JNI++
 
Purpose:
This simple C++ class is part of the JNI++ introductory example. 

Note: The main task of Foo in the JNI++ context is to act as both, as subclass of the 
native C++ class Bar and as superclass of the  Java class SubBar. For this purpose, 
it hides the foo() method of its superclass. In order to demonstrate passing of 
values and objects, the class Foo furthermore provides a couple of members returning 
simple and object values, respectively.

Todo: include more accessors for different simple and component types
*/
class FOOBAREXPORT Foo
: public Bar
{
public:
/*
Summary: This constructs a Foo instance
Parameters:
    message - a text message

Purpose:
    Class Foo forwards the message passed to its constructor to its superclass's
constructor and initializes its member variable.

Note:
    In the JNI++ example this is used for demonstrating calls to the C++ superclass 
constructor during creation of a Java subclass including parameter passing.
*/
Foo( const char * message );

/*
Summary: Destroys the instance.

Purpose:
    The destructor frees the member variable.
*/
virtual ~Foo();


/*
Summary:
    Member function calling the superclass's pendant

Parameters:
    message - a text message (string passed from Java to C++)
    object  - a Bar instance (object passed from Java to C++)

Returns:
    the return value of the call to the superclass's foo() member.

Purpose:
    A call to this non-virtual writes a line to the standard output containing 
the message passed and the messaged stored in the object passed. Then a 
new object is created with message "moon!" and is stored in the member 
variable. After that, the superclass Bar's function foo() is called with 
the passed message and the newly created object as arguments. Finally the 
Bar::foo()'s reply is returned to the caller as result.

Note: 
    In the JNI++ example this member hides the superclass's function foo() and
is oberridden by the Java subclass SubJava. Furthemore, it shows how to pass an
object created in the C++ library through a pipeline of C++ and Java objects.
*/
Bar * foo( const char * message, Bar * object );

/*
Summary:
    Get accessor for thisBar

Returns:
    the value of the data member thisBar

Purpose:
    This is the usual get accessor for the private data member thisBar.

Note: In the JNI++ example this member demonstrates how to pass Objects 
from C++ to Java.
*/
Bar * getBar();

/*
Summary:
    Pass count to and from Java

Parameters:
    count - the integer value to pass

Returns:
    the value of the count parameter

Purpose:
    Pass the value of the input parameter back to the caller

Note: In the JNI++ example this member demonstrates passing of simple
type values as input parameter and return value.

*/
int passCount( int count );

private:

/* {secret} */
Bar * thisBar;
};

#endif
This native C++ class is derived from the above mentioned native class Bar(). It, too, has a constructor and a virtual destructor. Furthermore, the non-virtual functions foo() and getBar() are provided. These functions will be called by a Java subclass.

Note that the shown private member does not at all affect the mixed language subclassing job to be undertaken. It is a weakness of C++ and Java that private members are exhibited to clients in the class definition. That's why it is not included in the online documentation.

2.2. The Example's Java Subclass

And this is how a Java programmer might expect how a subclass of the C++ class Foo should look like and how the virtual functions of C++ class Foo and its superclass Bar are overridden:


class SubFoo

// Purpose:
//   package for testing the JNI++ FooBar example
package TestSuite.FooBar;

import FooBar.*;


// Summary: Java subclass of the native C++ class Foo
//
// Purpose: This class is an example of how to override
// virtual functions defined in native C++ superclasses
// by Java methods.  Note that from the Java programmer's 
// point of view there is nothing special about it.  
// No information about the native background is available 
// and/or necessary. All native stuff is done in Java proxies 
// like the Java superclass Foo using JNI.
public class SubFoo
extends Foo
{
    // Summary: This is a usual constructor of a Java subclass.
	// Parameters:
	//    message - a message string
    // Purpose:
    // Nothing special about it. Simply calls superclass constructor-
    SubFoo( String message )
    {
        super( message );
    }

    // Summary: Prints some messages.
	// Parameters:
	//    message - a message string
	//    object - a Bar instance
	// Returns: the reply of the superclass's foo()
    // Purpose:
    // This method appears courtesy of the native Foo::foo()
    // function which it nicely hides and calls.
    public Bar foo( String message, Bar object )
    {
        System.out.println(" +++ SubFoo.foo(" + message + "||" + getMessage() + 
            "||" + object.getMessage() + ")" );
        return super.foo( message, object );
    }



    // Summary: Prints some messages.
	// Parameters:
	//    message - a message string
	//    object - a Bar instance
	// Returns: the reply of the superclass's bar()
    // Purpose:
    // This method appears courtesy of the native Bar::bar()
    // function which it nicely overrides.
    // Note: In the JNI++ example this Java function gets called from
    // a native C++ function via the virtual function calling
    // mechanism.
    public Bar bar( String message, Bar object )
    {
        System.out.println(" +++ SubFoo.bar(" + message + "||" + getMessage() + 
            "||" + object.getMessage() + ")" );
        return super.bar( message, object );
    }
}

As you can see, the Java programmer don't even realize that the underlying superclass is implemented in C++. The fact, that SubFoo.foo() and SubFoo.bar() override superclass methods is determined by name and signature, as usual in Java as well as in C++.

It is the vision of JNI++ that the Java programmer needs not to do more hand-coding than the above! The JNI++ programmer should do a genuine Java job and must not be forced to care about JNI proxies and the like. To provide for this is the task of

2.3. The JNI++ Generator

All necessary code beside the above Java subclass should be generated by an envisioned tool: the JNI++ generator.

To confess, the above example is cheating a little. SubFoo is not directly subclassed from the native C++ class Foo. Instead it subclasses a Java proxy for Foo containing all the stuff necessary for calling C++ members from Java via native functions and JNI. And it prepares for callbacks from virtual C++ functions back to Java. But it is entirely generated, so that the Java programmer has the illusion to use the native C++ class Foo directly.

To generate such Java proxies for native C++ classes is the task of the JNI++ generator. This generator parses the definition and interface files of the superclasses to be subclassed by Java. Then it generates all necessary code for the seemless integration of the C++ library into the Java world, namely Java proxy classes, JNI interface code, Java/C++ type conversion code, and C++ adapter classes for calls and callbacks between Java and C++.

A detailed discussion of the code to be generated to make the example running will be provided in part 2 and part 3 of this paper.

2.4. The Example's Native Class'es Implementations

As in the supposed real-world situation the source code of the native class'es implementations will not be provided here. There will be only a functional desciption. Of course it is available: You can download it from the open-source links at the JNI++ home page.

Now for the description:

class Bar

  • Class Bar stores the message passed to its constructor.
  • The destructor frees the stored message.
  • A call to Bar * Bar::foo(const char * message, Bar * object ) writes a line to the standard output containing the message passed and the messaged stored in the object passed. Then the virtual(!) function bar is called with message and object propagated as arguments. Finally, the reply of this call is returned to foo's caller.
  • A call to the non-virtual Bar * Bar::bar(const char * message, Bar * object ) again writes a line to the standard output containing the message passed and the messaged stored in the object passed. Then this object is returned as result value to bar's caller.
class Foo
  • Class Foo forwards the message passed to its constructor to its superclass's constructor and initializes its member variable.
  • The destructor frees the member variable.
  • A call to the non-virtual Bar * Foo::foo(const char * message, Bar * object ), too, writes a line to the standard output containing the message passed and the messaged stored in the object passed. Then a new object is created with message "moon!" and is stored in the Foo's member variable. After that, the superclass Bar's function foo() is called with the passed message and the newly created object as arguments. Finally the Bar::foo()'s reply is returned to the caller as result.


As a matter of fact, the source code Foo() and Bar() in the C++ library is smaller than its description, but we simulate the strange world of closed software here. You can have a look at the documentation viewing the native group of the inline documentation.

2.5. The Example's Java Test Frame

Here the Java source of a frame application for testing it all together:


class main

package TestSuite.FooBar;

import FooBar.*;

public class Main 
{
    static 
    {  
        try 
        {
            System.loadLibrary("FooBarJNI"); 
            System.out.println(" !!! FooBarJNI lib found");
        } catch (Error e) 
        {
            System.out.println(" ??? FooBarJNI lib not found");
            System.out.println(e.toString());
            
        }
    }
    public static void main(String[] args) 
    {
        {
            Bar a = new Bar("universe!");
            SubFoo b = new SubFoo("world!");
            Bar c = b.foo("Hello", a);     
            a = null;
            b = null;
            c = null;
        }
        Runtime r = Runtime.getRuntime();
        r.gc();
        try
        {
            Thread.sleep(5000);
        }
        catch( Exception e )
        {
            System.out.println(" ??? something went wrong ");
        }
    } 
}

And this is the output it produces:

+++ example2 loaded
+++ SubFoo.foo(Hello||world!||universe!)
--- Foo::foo(Hello||world!||universe!)
--- Bar::foo(Hello||world!||moon!)
+++ SubFoo.bar(Hello||world!||moon!)
--- Bar::bar(Hello||world!||moon!)


The most interesting point is that the call to virtual bar() inside Bar::foo() results in a call to the Java member SubFoo.bar(): a Java member overrides a native C++ virtual function!

3. Some JNI++ Objectives

This is an unordered list of objectives (to be extended).
  • JNI++ does not replace JNI. JNI++ is build on top of JNI.
  • JNI++ should work with all C++ standard conform compilers.
  • JNI++ must not require special compilers.
  • JNI++ should be source-code compatible on as much plattforms as possible.
  • JNI++ should be as complete as possible.
  • JNI++ should support full Java/C++ type conversion.
  • JNI++ should make as less presuppositions on the underlying C++ lib as possible.
  • JNI++ is open-source software in terms of the "Artistic license"
previous home  next
Hosted by www.Geocities.ws

1