I'm the father of ItsNat AJAX Java web framework, JNIEasy, LAMEOnJ, JEPLayer and JEPLDroid libraries (and very old stuff like XPDOM), supporter of the Single Page Interface (SPI) paradigm, writer of the SPI manifesto and currently Android native guy. Jose Maria has posted 28 posts at DZone. You can read more from them at their website. View Full User Profile

How to Access C++ from Java

04.03.2008
| 42263 views |
  • submit to reddit

JNIEasy is a development Java library focused on JNI access to C/C++ native methods. Here Jose, its creator, tells you all about it via an example scenario. And he asks: "What native libraries do you know of that could be interesting to access from Java?" -- Geertjan Wielenga, JavaLobby Zone Leader

JNIEasy goes the extra mile, using transparent Java-native synchronization. Any Java POJO can represent a C++ object, including instance methods and fields. A field modified in Java modifies the native field too and vice versa. Optionally, Java methods can be called from native C/C++ code, using pointers to functions with no JNI code. This “transparent native programming” is a translation to the Java-native world of techniques used in transparent persistence such as JPA or JDO.

Below follows a brief tutorial on how you can mix your C/C++ code with Java. The two worlds can be mixed in a way so transparent that it is difficult to distinguish when our task is executed in C++ and when in Java, although making the distinction is of course possible, should we want to do so.

The following Java class:

    public class MyCPPClassOnDLL

{

protected int virtualTable; // the C++ class has a virtual method

protected double value;

public MyCPPClassOnDLL() { // mandatory (is not native)

}

public MyCPPClassOnDLL(int a, int b) {

throw new RuntimeException("Not enhanced");

}

public native static void destroy(MyCPPClassOnDLL obj);

public native static long addStatic(int a,int b);

public double getValue() {

return value;

}

public native double sub(int a,int b);

public native static void varargsEx(byte[] buffer,Object[] args);

}
...is designed as a wrapper for this C++ class:
#include "JNIEasy.h"

// MyCPPClassOnDLL.h
class DLLEXPORT MyCPPClassOnDLL
{
protected:
double m_value;

public:
MyCPPClassOnDLL(int a,int b);
virtual ~MyCPPClassOnDLL();

static MyCPPClassOnDLL* __stdcall create(int a,int b);

static void __stdcall destroy(MyCPPClassOnDLL* obj);

static __int64 __stdcall addStatic(int a,int b);

double __stdcall getValue();

virtual double __stdcall sub(int a,int b);

static void __cdecl varargsEx(char* buffer,...);

};


#include "MyCPPClassOnDLL.h"
#include <stdio.h>
#include <stdarg.h>

MyCPPClassOnDLL::MyCPPClassOnDLL(int a,int b)
{
m_value = a + b;
}

MyCPPClassOnDLL::~MyCPPClassOnDLL()
{
}

MyCPPClassOnDLL* __stdcall MyCPPClassOnDLL::create(int a,int b)
{
return new MyCPPClassOnDLL(a , b); // may be an inherited class
}

void __stdcall MyCPPClassOnDLL::destroy(MyCPPClassOnDLL* obj)
{
delete obj;
}

__int64 __stdcall MyCPPClassOnDLL::addStatic(int a,int b)
{
return (__int64)a + (__int64)b;
}

double __stdcall MyCPPClassOnDLL::getValue()
{
return m_value;
}

double __stdcall MyCPPClassOnDLL::sub(int a,int b)
{
m_value = m_value - (a + b);
return m_value;
}

void __cdecl MyCPPClassOnDLL::varargsEx(char* buffer,...)
{
va_list marker;
va_start( marker, buffer );
const char* name = va_arg( marker, const char*);
int age = va_arg( marker, int);
int brothers = va_arg( marker, int);
va_end( marker );

sprintf(buffer,"%s is %d years old and has %d brothers",name,age,brothers);
}

...where DLLEXPORT is __declspec(dllexport) on Windows (unixes by default export all methods) and __int64 is a macro defined as long long in unixes. These macros are defined by JNIEasy.h

Consider the C++ class is linked in a dynamic library called MyLibrary.dll on Windows, libMyLibrary.so on Linux/Solaris ,and libMyLibrary.dylib on Mac OS X. These names are the typical conventions of these operating systems and JNIEasy tries to follow these conventions by default. However, they are not mandatory. In JNIEasy, any custom dynamic library name can be used.

The Java class must be declared as "native" with JNIEasy. It can be "enhanced" to bind this class to the C++. To do this, we need an XML descriptor. The XML declares our Java class as JNIEasy-native and explains how to bind the desired methods to native methods:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Archive MyCPPClassOnDLL.jnieasy.enh.xml -->
<jniEasyEnhancer version="1.1">

<package name="examples.manual">
<imports/>
<class name="MyCPPClassOnDLL" type="class" libraryPath="MyLibrary" >

<constructor params="int, int" onLibrary="true" nativeName="MSC:?create@MyCPPClassOnDLL@@SGPAV1@HH@Z;gcc:_ZN15MyCPPClassOnDLL6createEii"> </constructor>

<method name="destroy" params="MyCPPClassOnDLL" onLibrary="true" nativeName="MSC:?destroy@MyCPPClassOnDLL@@SGXPAV1@@Z;gcc:_ZN15MyCPPClassOnDLL7destroyEPS_"> </method>

<method name="addStatic" params="int,int" onLibrary="true" nativeName="MSC:?addStatic@MyCPPClassOnDLL@@SG_JHH@Z;gcc:_ZN15MyCPPClassOnDLL9addStaticEii">
</method>

<method name="sub" params="int, int" onLibrary="true" nativeName="MSC:?sub@MyCPPClassOnDLL@@UAGNHH@Z;gcc:_ZN15MyCPPClassOnDLL3subEii">
</method>

<method name="varargsEx" onLibrary="true" nativeName="MSC:?varargsEx@MyCPPClassOnDLL@@SAXPADZZ;gcc:_ZN15MyCPPClassOnDLL9varargsExEPcz" callConv="c_call">
<return />
<params>
<param class="byte[]" />
<param class="Object[]" varargs="true" />
</params>
</method>
</class>
</package>
</jniEasyEnhancer>

The following attribute:

nativeName="MSC:?create@MyCPPClassOnDLL@@SGPAV1@HH@Z;gcc:_ZN15MyCPPClassOnDLL6createEii"

...explains to JNIEasy to use "?create@MyCPPClassOnDLL@@SGPAV1@HH@Z" as the exported method name, if the native library was compiled with a Microsoft C/C++ compiler. Otherwise it uses "_ZN15MyCPPClassOnDLL6createEii", if compiled with gcc.

Both "macros" are user-defined, calling NativeTypeManager.defineMacro(String,Object). Using this "C-inspired" macro system allows JNIEasy, at runtime, to resolve the appropiated library name, method name, memory size, and so on. This way, Java "native" code can be portable between platforms, compilers, and so on.

Note that the getValue() method is not declared in the XML enhancer file. JNIEasy keeps this method "untouched". By default, any Java field in the class is declared native and bound to the C++ field by the enhancer. Correspondence between Java and C++ is according to the native layout of the class. This is the reason for the Java field "virtualTable", because the C++ class has a hidden member pointer to resolve virtual method addressing. And our C++ class has a virtual method, this implies a virtual method table.

This Java code shows an example of how we can access our C++ code and create a C++ object from Java:

long res = MyCPPClassOnDLL.addStatic(1,2);
System.out.println("Must be 3: " + res);

MyCPPClassOnDLL obj = new MyCPPClassOnDLL(1,2); // Calls create() C++ method.
// New object is already native and maps the C++ object
System.out.println("Must be 3: " + obj.getValue());
// Java method getValue() is not native (but field "value" is native)

double res2 = obj.sub(1,2);
System.out.println("Must be 0: " + res2);

MyCPPClassOnDLL.destroy(obj); // Calls the C++ method destroy(), the memory is freed.

This code is basically the same when we do the same in C++.

The call obj.getValue() is interesting, in spite of getValue() is not a “native” method (is a normal Java method) it returns the value of the C++ field “value”. The Java field “value” is a proxy of the C++ field, if the Java field is modified from Java (inside the Java class) the native field is automatically modified too (the Java class is enhanced to provide this transparency).

This example shows a C++ class ready to be used with JNIEasy (methods are exported with standard or C call convention, this is not the C/C++ default). Methods of legacy C++ classes can be exported and “wrapped” using false C++ methods. A chapter in manual (“MAPPING NATIVE LEGACY CLASSES”) shows how to do this.

Furthermore from C++ we can bind and call Java methods using normal pointer to functions:

double (__stdcall * MyCPPClassOnJava::_sub)(void*,int,int) = 0;
_sub = (double (__stdcall *)(void*,int,int)) JNIEasyHelper::findExportedMethodAddress("examples.manual.MyCPPClassOnJava.sub(int,int)");

This way we can define C++ classes as wrappers of Java POJOs.

The following call:

MyCPPClassOnJava* obj = …
_sub(obj, a, b);

Where “obj” points to a C++ MyCPPClassOnJava object (a C++ wrapper of the Java class) invokes the Java instance method sub(int,int) of the Java class MyCPPClassOnJava (this class must be previously declared as native with JNIEasy and loaded from Java).

An example of a Java class MyCPPClassOnJava:

public class MyCPPClassOnJava
{
protected double value;

public MyCPPClassOnJava() // mandatory
{
}

public MyCPPClassOnJava(int a,int b)
{
this.value = a + b;

JNIEasy.get().getLockedRegistry().lock(this); // To avoid GC if called from native
}

public static void destroy(MyCPPClassOnJava obj)
{
JNIEasy.get().getLockedRegistry().unlock(obj); // To enable GC again
JNIEasy.get().getNativeManager().free(obj);
}

public static long addStatic(int a,int b)
{
return (long)a + (long)b;
}

public double sub(int a,int b)
{
this.value = this.value - (a + b);
return this.value;
}
}

MyCPPClassOnJava methods can be exported as callbacks to the native world with the following XML enhancer descriptor:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Archive MyCPPClassOnJava.jnieasy.enh.xml -->
<jniEasyEnhancer version="1.1">

<package name="examples.manual">
<imports />
<class name="MyCPPClassOnJava" type="class">
<constructor exportMethod="true" params="int,int">
</constructor>
<method name="destroy" exportMethod="true"
params="MyCPPClassOnJava">
</method>
<method name="addStatic" exportMethod="true" params="int,int">
</method>
<method name="sub" exportMethod="true" params="int,int">
</method>
<fieldMethod name="value" exportMethod="true" />
</class>
</package>
</jniEasyEnhancer>

These examples show the "transparent" level. JNIEasy provides many utility classes too to attain fine-grained control in the Java-native world.

The current JNIEasy version is 1.2.1. This version runs on the following x86 platforms: Windows, Linux, MacOSX (including Leopard) and Solaris. JNIEasy is commercial software, but is free for non-profit usage via temporary licenses, which can be renewed without user data. Also note that LAMEOnJ, an open source Java based wrapper of the LAME API, using JNIEasy, has been updated to JNIEasy 1.2.1, adding Solaris x86 support.

You are welcome to download the library, also for the temporary license, and you can also visit the related Home page. So... maybe the time has come to bring your venerable native programs/libraries to Java, on a path into the future? And a question to readers: what native libraries do you know of that could be interesting to access from Java?

 

Published at DZone with permission of its author, Jose Maria Arranz.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Daniel Spiewak replied on Sat, 2008/04/05 - 2:33pm

Er...  How is this better than JNA?  To me, this looks like a whole mess of XML, a ton of boiler-plate Java and a depedance on bytecode instrumentation for no significant benefit.

Kevin Conaway replied on Sun, 2008/04/06 - 10:09am

Two useful methods that we use are the Microsoft DPAPI methods CryptProtectData and CryptUnprotectData

 

We ended up releasing a wrapper library for them: http://jdpapi.sourceforge.net/

Jose Maria Arranz replied on Mon, 2008/04/07 - 3:23am

Daniel Spiewak: Er... How is this better than JNA? To me, this looks like a whole mess of XML, a ton of boiler-plate Java and a depedance on bytecode instrumentation for no significant benefit.

Do you allow me the following analogy?

JNA = JDBC

JNIEasy = JPA/JDO

Now rewitting your phrase:

Er... How is JPA/JDO better than JDBC? To me, this looks like a whole mess of XML/annotations, a ton of boiler-plate Java and a depedance on bytecode instrumentation for no significant benefit.

Where is the whole mess of XML? XML code showed before can be understood by a child.

Where is the ton of boiler-plate?

First example shows how to access a C++ object with no line of C/C++ code and using a POJO. Where is the boiler plate?. Try to do this with JNA.

Second example shows how you can bind a normal Java POJO to a C++ class working as a proxy dynamically binding Java methods with no JNI dependency. Where is the boiler plate? Try to do this with JNA.

Disclaimer: JNA is fine, is the JDBC API of Java-native programming, JNIEasy has similar features as JNA but JNIEasy goes the extra mille providing transparent native programming. JNIEasy has the typical tools to call native methods with no bytecode instrumentation.

JNIEasy doesn't use annotations (it reduces the XML code, most of the code is to identify the method is being converted to native) because is Java SE 1.4 compatible.

XML configuration is necessary because JNIEasy provides a fine control of how Java is converted to native, for instance a Java char can be seen in C/C++ as a char (1 byte) or wchar_t (2 or 4 bytes), JNIEasy offers alignment customization per field and so on, extracting this information of the Java type is impossible and introduce innecessary dependencies.

 

Daniel Spiewak replied on Mon, 2008/04/07 - 12:11pm

<package name="examples.manual">  
<imports/>
<class name="MyCPPClassOnDLL" type="class" libraryPath="MyLibrary" >

<constructor params="int, int" onLibrary="true" nativeName="MSC:?create@MyCPPClassOnDLL@@SGPAV1@HH@Z;gcc:_ZN15MyCPPClassOnDLL6createEii"> </constructor>

You must know some interesting kids.  :-)

 I personally find JDBC quite a bit easier than JPA.  That's just a matter of preference of course, the higher-level  abstractions can be quite nice.  I think your analogy is a bit flawed though.  I would liken JNI to JDBC, and JNA to more of a thin abstraction such as ActiveObjects.  Both JNA and JNIEasy do a lot of the same things, but JNIEasy seems to have a) thicker abstractions, and b) much more configuration work.  I respect the job you've done with the project, but I honestly can't envision a use-case where I would go with JNIEasy over JNA.

Jose Maria Arranz replied on Tue, 2008/04/08 - 2:41am in response to: Daniel Spiewak

Daniel Spiewak: You must know some interesting kids. :-)

LOL

Is not difficult:

name="MyCPPClassOnDLL"  => the Java class to be enhanced/made native
type="class" => as a C++ class (this includes inheritance)
libraryPath="MyLibrary" => C++ methods to bind are located in MyLibrary.dll or libMyLibrary.so or libMyLibrary.dylib
constructor params="int, int" => declares the Java constructor MyCPPClassOnDLL(int,int) as native
onLibrary="true" => as a proxy of a C++ method 
nativeName="MSC:?create...;gcc:_ZN15..." identifies the C++ method  
MyCPPClassOnDLL* MyCPPClassOnDLL::create(int,int) , this method is exported in MyLibrary DLL, different C++ compiler
	use different name mangling MSC and gcc are declared on runtime to pick the correct name. 
	pedump.exe, objdump, elfdump can be helpful to identify exported names with methods in source code.
 
Daniel Spiewak: I honestly can't envision a use-case where I would go with JNIEasy over JNA 

I must to say JNA is fine to call native methods but it uses reflection/proxies to set native structures in Java, you only can query the memory state of a native structure calling a method. In JNIEasy you can read native fields from Java when you want, the Java field value is ever the native field value because the native side is ever queried. You can walk through your Java (native) data model much like you do in C++, Java object identity is the native identity, a Java reference as a field in a Java native class is identified as a native pointer, if this field has changed in the native side the Java field will point to the correct Java native instance (or a new one if the native object is new or null), and vice versa, if you change this reference to point another Java native object (if not native is made native automatically by "reachability") the C++ field is changed too, this is the magic of transparent native programming and this is the MAIN reason of bytecode enhancement).

The JDBC-JPA/JDO analogy is not bad: if you want to fill a Java POJO using JDBC you usually set all fields at once, this is how JNA works. The state of this Java POJO only represents the database immediately after the query, you cannot traverse an object model (course you can if queried at once). In JPA/JDO any Java object is ever bound to the database and you can traverse the object model.

 

Jose Maria Arranz replied on Tue, 2008/04/08 - 2:42am

 

 Uh I'm sorry copy/paste copied the HTML markup too.

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.