Looking for a convenient way to call Java from C++
Asked Answered
F

9

31

It seems most documentation or helper libraries relating to JNI (Java Native Interface) are concerned with calling native code from Java. This seems to be the main use of it, even though it is capable of more.

I want to mostly work in the opposite direction: modify an existing (fairly large) portable C++ program by adding some Java libraries to it. For example, I want to make it call databases via JDBC, or message queue systems via JMS, or send emails, or call my own Java classes, etc. But with raw JNI this is pretty unpleasant and error-prone.

So I would ideally like to write C++ code that can call Java classes as easily as C++/CLI can call CLR classes. Something like:

using namespace java::util::regex; // namespaces mapped

Pattern p = Pattern.compile("[,\\s]+");

array<java::lang::String> result = 
    p.split("one,two, three   four ,  five");

for (int i=0; i < result.length(); i++)
    std::cout << result[i] << std::endl;

This way, I wouldn't have to manually do the work of getting the method ID by passing the name and the weird signature strings, and would be protected from programming errors caused by the unchecked APIs for calling methods. In fact it would look a lot like the equivalent Java.

NB. I AM STILL TALKING ABOUT USING JNI! As an underlying technology it is perfect for my needs. It is "in process" and highly efficient. I don't want to run Java in a separate process and make RPC calls to it. JNI itself is fine. I just want a pleasant interface to it.

There would have to be a code generation tool to make equivalent C++ classes, namespaces, methods, etc. to exactly match what is exposed by a set of Java classes I specify. The generated C++ classes would:

  • Have member functions that accept similarly-wrapped versions of their parameters and then do the necessary JNI voodoo to make the call.
  • Wrap the return values in the same way so I can chain calls in a natural way.
  • Maintain a per-class static cache of method IDs to avoid looking up them every time.
  • Be totally thread-safe, portable, open source.
  • Automatically check for exceptions after every method call and produce a std C++ exception.
  • Also work for when I'm writing native methods in the usual JNI way but I need to call on to other Java code.
  • The array should work totally consistently between primitive types and classes.
  • Will no doubt need something like global to wrap references in when they need to survive outside of a local reference frame - again, should work the same for all array/object references.

Does such a free, open-source, portable library/tool exist or am I dreaming?

Note: I found this existing question but the OP in that case wasn't nearly as demanding of perfection as I am being...

Update: a comment about SWIG led me to this previous question, which seems to indicate that it is mostly about the opposite direction and so wouldn't do what I want.

IMPORTANT

  • This is about being able to write C++ code that manipulates Java classes and objects, not the other way round (see the title!)
  • I already know that JNI exists (see the question!) But hand-written code to the JNI APIs is unnecessarily verbose, repetitious, error-prone, not type-checked at compile time, etc. If you want to cache method IDs and class objects it's even more verbose. I want to automatically generate C++ wrapper classes that take care of all that for me.

Update: I've started working on my own solution:

https://github.com/danielearwicker/cppjvm

If this already exists, please let me know!

NB. If you're considering using this in your own project, feel free, but bear in mind that right now the code is a few hours old, and I only wrote three very unstrenuous tests so far.

French answered 24/9, 2011 at 8:6 Comment(8)
There is SWIG although I only used it for C and Python ... a long time ago.Originate
See update: SWIG apparently doesn't do this.French
in which compiler/interpreter you want to execute your program, C or Java? If Java, reverse is not possible using JNI.Assign
I've added swig to the tags, in case someone can clarify.French
@Assign - I don't understand your question: I want to execute the C++ parts in natively compiled C++ (as usual) and the Java parts in the JVM, and to connect the two with JNI/invocation. I just don't want to manually do all the JNI nonsense.French
gcc.gnu.org/onlinedocs/gcj/About-CNI.html ?Vries
@Vries - CNI is a feature of GCJ, which is a compiler for a subset of Java, supporting a subset of Java libraries. I want to use any 3rd party Java libraries running on a real supported Java implementation.French
@Daniel Earwicker - Yes, this already exists -- see my answer below.Lacour
L
17

Yes, there are existing tools that do exactly this -- generate C++ wrappers for Java classes. This makes use of Java APIs in C++ more transparent and enjoyable, with lower cost and risk.

The one that I've used the most is JunC++ion. It's mature, powerful and stable. The primary author is very nice, and very responsive. Unfortunately, it's a commercial product, and pricey.

Jace is a free, open-source tool with a BSD license. It's been years since I last played with jace. Looks like there's still some active development. (I still remember the USENET post by the original author, over a decade ago, asking basically the same question you're asking.)

If you need to support callbacks from Java to C++, it's helpful to define C++ classes that implement Java interfaces. At least JunC++ion allows you to pass such C++ classes to Java methods that take callbacks. The last time I tried jace, it did not support this -- but that was seven years ago.

Lacour answered 27/9, 2011 at 21:3 Comment(4)
Thanks. I see from the Jace source that it turns every single reference into a global! This isn't a great idea. The JNI deliberately makes that an option: globals being more heavyweight than locals, and the vast majority of variables in a real program are locals. There's no need for locals to be "released" in a destructor as the JVM takes care of it. You just need a RAII class to encapsulate PushLocalFrame/PopLocalFrame. What approach does JunC++ion take for that?French
BTW with my Google search for the terms C++ Java JNI Wrapper, I see now that JunC++ion is on page 22 of the results! Haven't spotted Jace yet...French
JunC++ion would be on page 1, if the search were based on quality. I don't have access to JunC++ion right now. I would guess global references are necessary in some cases and sufficient for others, but that's just a guess. I'll see if I can get the author to respond.Lacour
Alex Krapf's answer below addresses global/local references.Lacour
B
8

I'm one of the prinicpal architects for Codemesh's language integration products, including JunC++ion. We have been doing this kind of integration since 1999 and it works really well. The biggest problem is not the JNI part. JNI is tedious and hard to debug, but once you get it right, it mostly just keeps working. Every now and then, you get broken by a JVM or an OS update, and then you have to fine-tune your product, but in general it's stable.

The biggest problem is the type system mapping and the trade-offs between general usability and targeted solution. You state for example that you don't like the fact that JACE treats all object references as globals. We do the same thing (with some escape hatches) because it turns out that this is the behavior that works best for 95% of customers, even if it hurts performance. If you're going to publish an API or a product, you have to pick the defaults that make things work for most people. Picking local references as the default option would be wrong because more and more people are writing multithreaded applications, and a lot of Java APIs that people want to use from other languages are intrinsically multithreaded with asynchronous callbacks and the like.

We also found out that you really want to give people a GUI-based code generator to create the integration specification. Once they've specified it, you use the CLI version to integrate it into the nightly build.

Good luck with your project. It's a lot of work to get right. We spent several years on it and we're still making it better regularly.

Banal answered 28/9, 2011 at 14:25 Comment(1)
Thanks. Re: the sensible choice of defaults, I totally agree - it's the most important thing about design IMO. For my present project, having the choice to use locals will be important, and shared data will be very much the minority, so I'm going for java::lang::String and global<java::lang::String> as the syntax for locals and globals respectively.French
W
4

I had pretty much the same problems, ended up doing it on my own, maybe this helps someone.

https://github.com/mo22/jnipp

It has a small runtime footprint (<30kb), manages references, and supports generating Java class interfaces. I.e. LocalRef> stringArray; and then using stringArray[1]->getBytes() or something.

Watchman answered 5/5, 2015 at 21:3 Comment(0)
T
2

Re calling Java from C++.

You can do what you wish but you must let Java be in control. What I mean by this is that you create Java threads that call into Native code and from there they block, kind of waiting for your native code to give it something to do. You create as many Java threads as you need to get enough work / throuhput done.

So your C++ application starts up, it creates a JVM/JavaVM (as per the documented way, example exists in qtjambi codebase see below), this in turn perform the usual JNI initialization and System.loadLibrary() and provides JARs with "native" linkage. You then initialize a bunch of threads and call some JNI code (that you created) where they can block in wait for your C++ code to give them some work to do.

Your C++ code (presumabily from another thread) then sets up and passes all the information needed to one of the blocked and waiting Java Thread workers, it is then given the order to run, it may then go back into pure-Java code to do work and return with a result.

...

It is possible to setup and create and contain a JavaVM instance from C++ code. This can be force fed your own CLASSPATH/JARs to setup the contained environment you need encapsulated inside your C++ program.

Outline of that as I'm sure you have found already at http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html

...

There is a kind of C++ => Java JNI generator in the QtJambi project (that I work on and help maintain). This is quite bespoke for the Qt toolkit but essentially it translates a bunch of C++ header files into a collection of C++ .cpp/.h files and *.java file to provide linkage and shell containment of the object so that the competing memory allocation schemes play well together. Maybe there is something to be taken from this.

This is certainly a proof in cencept for what you are asking the generator just happens to be contained in the qtjambi project (but could be made independant with some work) and this is LGPL licensed (open-source). The Qt toolkit is not a small API yet it can generated 100s of classes to cover high % of API (>85% and almost 100% of Core/GUI parts).

HTH

Tal answered 26/9, 2011 at 19:21 Comment(5)
The part of your answer about threads makes no sense to me. I can already do something far simpler than that using pure JNI. I just init the JVM, ask it load me a class, call the constructor on it, call its methods. That's all there is to it.French
The QtJambi library is a project to allow Java code to use the Qt library, i.e. Java calling C++. Does it read existing Java classes and produce corresponding C++ wrapper classes so that the Java classes can be easily called from C++?French
The thread issue was about how to setup a process so that C++ can at anytime call into Java safely. Parts of the Qt library are complex, it is a C++ library that also needs to call back into Java. You can register callback/eventHandlers/threads that run in C++ but then occasionally invoke things in Java. Java has APIs in pure-Java and also C/C++. But C/C++ has no APIs for doing things in Java. It makes no sense to make a C++ wrapper of Java things when Java already has a C++ API (i.e. JNI).Tal
Maybe I worded my question really badly or something.French
This is a very interesting idea, it seems to only require some refactoring on the c++ part, but the rest is mostly based on pure JNI which is very stable. It would be nice to see some real examples of this, maybe somebody else have tried it?Persnickety
B
1

I also had many difficulties getting JNI to work on different operating systems, coping with 32/64-bit architectures and making sure the correct shared libraries were found and loaded. I found CORBA (MICO and JacORB) difficult to use too.

I found no effective way to call from C/C++ into Java and my preferred solutions in this situation are to run my Java code as either:

  1. a stand-alone program that I can easily run from C/C++ programs with java -cp myjar.jar org.foo.MyClass. I guess this is too simplistic for your situation.

  2. As a mini-server, accepting requests from C/C++ programs on a TCP/IP socket and returning results through this socket too. This requires writing networking and serializing functions but decouples the C/C++ and Java processes and you can clearly identify any problems as being in the C++ side or Java side.

  3. As a Servlet in Tomcat and make HTTP requests from my C/C++ program (other servlet containers would also work too). This also requires writing networking and serializing functions. This is more like SOA.

Burgundy answered 26/9, 2011 at 23:40 Comment(3)
Thanks. I've got JNI working on Windows and Linux so far without problems - it seems basically identical. CORBA is way off what I need. Marshalling all calls out-of-process seems like a hand-written version of the stuff you'd get from using SOA (or CORBA), and all those have a big extra overhead, and more complexity.French
Meanwhile, JNI would be the perfect lightweight solution, if only the incantation for each method call (pass the method name and signature, cached the ID, determine the correct flavour of Call[Static]MethodT, check for exceptions) could be cleanly automated. That's all I want.French
Another commercial product: javain.com/javain/oojni.jsp?cat=oojni&sub=whatIsFrench
L
1

What about using Thrift or Protocol Buffers to facilitate your Java to C++ calls?

Lemming answered 27/9, 2011 at 16:43 Comment(1)
I should update my question to rule out using RPC to out-of-process servers.French
I
0

Maybe a bit of a too large hammer for this nail, but isn't that what CORBA was built for?

Ivar answered 26/9, 2011 at 21:1 Comment(1)
From that link: "The C++ mapping is notoriously difficult; the mapping requires the programmer to learn complex and confusing datatypes that predate the C++ Standard Template Library" Sounds great! :)French
I
0

Since CORBA doesn't seem to be what you want, here are links describing how to start JVM from C/C++ and calling JAVA-methods.

http://java.sun.com/docs/books/jni/html/invoke.html and http://java.sys-con.com/node/45840

PS: When interfacing Java with C++ you should also have a look at JNA or bridj.

Ivar answered 26/9, 2011 at 22:54 Comment(1)
It's getting late. Both links just seem to provide the same information as already given by others... Good night :)Ivar
F
0

Answering my own question:

http://java4cpp.kapott.org/

Doesn't appear to be an active project. The author recommends it not be used with JDK 1.5 or later.

It appears to have a serious problem: it passes around naked pointers to its wrapper objects:

java::lang::Integer* i = new java::lang::Integer("10");

delete i; // don't forget to do this!

It also causes a more subtle problem that in order to represent assignment compatibility (e.g. a class as a subtype of the interface it implements) the wrappers have to inherit from each other.

French answered 28/9, 2011 at 8:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.