Architecture & Technology
There are two pieces to JMS Courier: a development piece and a runtime piece.
The development piece is easily described as a set of header files, libraries, or assemblies that you use in your application. If you're a C++ developer, you include the header files in your source files to gain access to the type declarations and you link with the libraries that contain the type implementations. If you're a .NET developer, you add the supplied assemblies to your project and start using the types residing in these assemblies. It almost couldn't be any easier.
The C++ or .NET types that you are using at development time were generated using Codemesh's JunC++ion and JuggerNET tools. You could download the tools and do this yourself, but Codemesh saved you the work of doing that by creating JMS Courier as a separate product.
The runtime piece is much more exciting because that's what you really care about. It's one thing to have the ability to write nice-looking, object-oriented code; it's another thing to have the code work correctly and with good performance. How does Codemesh expose the Java JMS functionality to C++ and .NET in a way that makes it both easy to use and highly performing?
The key lies in Codemesh's use of in-process integration. In-process integration keeps all integrated pieces together in one process, thereby offering great performance and security through the tightest possible coupling. In the case of JMS Courier, there's clearly an out-of-process piece involved; after all, the whole purpose of using JMS is to message between different processes. Just don't confuse this domain-inherent out-of-process functionality with the architecture of the integration layer! All messaging takes part between a Java JMS server/router and a Java JMS client. That's the out-of-process piece that your application relies on. All integration takes part between C++/.NET code written by you and the Java JMS client API. This part is totally in-process.
The following diagram illustrates this relationship.
All Java pieces are green. You can see two other-language processes, one C++, one .NET, each hosting a Java Virtual Machine. The hosted JVM handles all interactions between the client application and the JMS framework. The native process communicates with the hosted JVM via proxy types and the Codemesh runtime library.
The proxy types are highly usable C++ or C# types which represent the JMS API in the target language.
The runtime library is written in C, C++, and C# and does all the interesting work. It has two alternative "backend" implementations:
- The first implementation uses the Java Native Interface (JNI) to load a Java Virtual Machine (JVM) into the native process. In this mode (the default mode) there is no other process involved because all Java activities are executed in a JVM loaded into your native process.
This is the integration solution with the best possible performance.
- The second implementation uses a proprietary TCP/IP protocol to connect to a Shared JVM server. The Shared JVM server is a separate, pure Java process that is running somewhere on your network. It hosts your Java classes and every interaction between Java and native code involves an interprocess call. Think of the Shared JVM server as a lightweight application server that can make any plain old Java object (POJO) available as a remote object.
This integration solution has a severe performance penalty when compared to the JNI-based in-process integration and it is a little strange to use out-of-process integration to use another out-of-process integration API. In the JMS Courier use case, we would probably only recommend this mode if no JVM were available or allowed at deployment time.
Please note that both backend imlpementations use the same runtime library, the same generated proxy classes and the same user-written code. The only difference between the two modes lies in some runtime configuration settings.
Regardless of the mode that you're using, you can control when the JVM is loaded or a connection to a Shared JVM is made. If you take no explicit steps, the first use of a proxy type that requires delegation to Java will on-demand-load the JVM or connect to the server. Alternatively, you can explicitly load/connect to a JVM by using the configuration API in your application.
Once a JVM has been loaded/connected to, it will typically remain active until your process terminates. This is an important thing to remember: you cannot have multiple JVM load/unload cycles! This is necessitated by the JVM implementation which does not support this behavior and is not a limitation of the JunC++ion runtime.