The In-Process Integration Alternative

ESBs or Web services approaches to integration can work well in many enterprises. However, take a look at a middleware alternative that can successfully streamline integration better.

Integrating applications is time consuming, expensive, and fraught with risk. Before climbing aboard the enterprise service bus (ESB) or turning to Web services, many organizations are using "in-process" integration to tackle specific Java-to-C++ interoperability challenges. In-process integration is a middleware alternative that doesn't require the external processes or servers that alternative approaches require. This lean integration approach results in improved security, less communication overhead, and performance that is faster by orders of magnitude. In a report published earlier this year, Forrester Research cited in-process integration as a strategic alternative for some companies working in a tightly distributed system.

There are many use cases that require the developer to integrate components written in Java with software written in C++. Some of the problems that are encountered often are:

  • Publishing a C++ version of the developer's Java API
  • Integrating a C++ application with enterprise Java APIs—Enterprise JavaBeans (EJB), Java Message Service (JMS), and Java Naming and Directory Interface (JNDI)
  • Using a unique third-party Java library from a C++ application
  • Displaying existing Java GUIs in a C++ application

We can discuss Java/C++ integration by looking at the second problem category—using an enterprise Java API, in this case the JMS. While the in-process integration approach is not solely reliant on or limited to JMS, we chose JMS as our example for two reasons: it's a real-life use case that is immediately appealing to many people, and it's a beautiful example of mixing in-process and out-of-process integration technology.

In case you're not familiar with JMS it's a specification for performing asynchronous messaging between Java applications. Sun Microsystems maintains the API specification, which in turn is implemented by several dozens of independent messaging providers. Every J2EE application server comes bundled with a JMS implementation, but you can also use one of many free or commercial stand-alone messaging implementations if all you need is messaging. JMS is a very popular Java enterprise API, and there are many people who have to integrate with it or would like to leverage this technology from their C++ applications. Following the old adage of "one picture being worth more than a thousand words," we can illustrate the integration problem at the architectural level (see Figure 1).

Mix It Up
As mentioned previously, a modern developer's integration toolbox contains a variety of tools for integrating Java and C++ components. The most common are ESB, CORBA, Web services, and Java Native Interface (JNI).

JNI is significantly different from not only the first three options mentioned, but all other integration approaches. JNI allows you to mix Java and C or C++ within one process. Basically, you can call a JNI C function that results in an action being taken on the Java side within your C or C++ process. Contrast that process to all the other integration approaches that have the Java and C++ components running in different processes and have to use some kind of inter-process communications to achieve integration. Consequently, in-process integration through JNI has some very attractive technical characteristics when compared with all other integration technologies:

  • JNI is fast. The cost of a JNI call is comparable to the cost of an expensive virtual function call; interprocess calls are usually several orders of magnitude slower. There is also very little data marshaling involved.
  • JNI is secure. There are no interprocess communications; all calls are within one process, which is comparable to calling a function that is exported by a shared library. Expensive security measures (both in terms of performance and money) like authentication or encryption are not required.
  • JNI is reliable. You might cringe as you are reading this statement if you have written JNI code by hand. Your reaction is simply because of JNI's ease of use, or better, the lack thereof, not because of any technical shortcomings. When we look at the integration problem, JNI is the only approach that doesn't introduce additional failure modes into the integrated solution. An error-free, JNI-based application will either work (if it is configured correctly) or not (if it is configured incorrectly). The same cannot be said for solutions that rely on interprocess communications or the availability and reachability of server applications.

Observe the architecture when the C++ application uses JNI to bridge the language gap to Java (see Figure 2). The C++ application contains a dynamically loaded Java Virtual Machine (JVM) that executes all Java code on behalf of the containing process. This diagram also illustrates a mix of in-process and out-of-process technologies. JNI, an in-process integration technology, is used to bridge the language gap efficiently. JMS, our example use case and by itself an out-of-process integration technology, is used for asynchronous messaging between applications.

The one significant downside of JNI has already been mentioned; it's very hard to use. In fact, it is downright ugly! While it is a beautifully designed API, it really wasn't meant to be used by the casual software developer. In this snippet, we attempt to call a Java method that takes a String, a Date, and a bool argument:

jclass clsDate = env->FindClass( 
   "java/util/Date" );
jclass clsMyType = env->FindClass( 
   "com/mypack/MyType" );
if( clsMyType == NULL )
   exit( 1 ); 
   //handle error in real app
jmethodID ctor = env->GetMethodID( 
   clsDate, "", "()V" );
jmethodID mid = 
   clsMyType, "calculate",
   Date;Z)J" );
if( mid == NULL )
   exit( 1 ); 
   //handle error in real app
jstring arg1 = env->NewStringUTF( 
   "An input string" );
jobject arg2 = env->NewObject( 
   clsDate, ctor );
jboolean arg3 = bArg ? JNI_TRUE : 
jlong result = 
   clsMyType, mid,
   arg1, arg2, arg3 );
if( env->ExceptionOccurred() )
   exit( 1 ); 
   //handle error in real app

Extraordinary Exposure
We assume that the JVM has already been loaded and that the thread has already been attached to the JVM. If this example were production-quality code, this snippet should really contain much more—and much more complex—error handling. It is probably no surprise that production JNI code usually doesn't contain the required error handling, which is one of the reasons for JNI having a reputation of being unreliable.

Most people who have used JNI successfully have used it in only very limited scenarios, where they implemented a few integration points with relatively simple usage. Using JNI by hand to expose a significant enterprise Java API to C or C++ would be a truly Herculean task! Furthermore, JNI is a low-level C API. The creation of easy-to-use C++ proxy types for the Java types of interest is a problem that is not solved by using JNI. In summary, JNI is technically the best solution for integrating Java and C++, but it is too hard to use. What can be done to reap the benefits of JNI without the troubles stemming from its complexities?

How do the other integration technologies stack up? Many, including myself, share the opinion that they do not. Whether you're looking at CORBA, ESB, or Web services, you are looking at technology that would be used outside of its area of core competency. These technologies were invented to solve the problems associated with distributed systems, possibly distributed across the Internet, and that area is really where their use should be focused. Bridging a programming language gap is a mere side effect of these technologies trying to be useful to as many developers as possible. Using CORBA, ESB, or Web services to solve a mere language-integration problem is like using a sledgehammer to kill a mosquito. Indeed, you might succeed after a fashion, but you should really assess the collateral damage afterward.

There are two big drivers behind the use of these technologies for language integration: they might already be used in the project for other purposes, and they might be perceived as sexy and interesting (at least in the case of ESBs and Web services).

Don't fall into the trap of choosing an inappropriate tool for a job for the sole reason that it looks nice or that it is available. If you do, you're going to pay a price farther down the track.

There are several parts to the solution, but they revolve around a few key points:

  • The low-level integration must use JNI for performance, security, and reliability reasons.
  • JNI must be invisible yet accessible to the developer.
  • Developers must have access to all aspects of the underlying Java type through an easy-to-use and "unsurprising" C++ type.
  • Developers must be able to easily specify which Java types they want to use.

These key points lead us to a solution that includes a run-time library that abstracts JNI away as much as possible and to a code generator that generates C++ proxy types for user-specified Java types.

Teacher's Pet
There are a handful of companies who have spent years developing a solid run-time library and a code generator that takes Java bytecode as input and generates C++ source code. The generated code can be used in your application to call the corresponding Java API. The library and the code generator are the components that were identified earlier as the crucial pieces of a good Java/C++ integration technology: a JNI abstraction layer and a way for developers to specify the Java types that they want to use from C++. The combination of the components creates a technology that allows you to write C++ code that looks and behaves almost exactly like the corresponding Java code and requires very little expert knowledge about the integration layer.

Whether you own a Java API and your customers demand a C++ version, or whether you have a C++ application and need to integrate with a third-party Java API, Java/C++ cross-language integration problems can be solved cost effectively using in-process integration. This technology was designed from the ground up specifically for the purpose of integrating between the languages and is often one of the fastest methods to implement successfully. To further research this topic, try and contrast different integration approaches to better distinguish when in-process integration is appropriate and when using an ESB is a better approach for your Java integration problem.

You may find that the less popular student is really the star pupil.