: juggernet overview

JuggerNET Technology

On this page we're going to delve a bit deeper into the technologies that underpin JuggerNET and we're going to contrast JuggerNET's integration approach with a few other popular integration approaches.

Let's take a look at a contrived .NET code snippet that uses Java through JuggerNET-generated C# proxy types.

using Java.Util;
using Java.Text;

...

string  AddDaysToDate( Date base, int days )
{
    Calendar    cal = Calendar.GetInstance();

    cal.SetTime( base );
    cal.Add( Calendar.DAY_OF_MONTH, days );

    return DateFormat.GetInstance().Format( cal );
}
        

You could of course do the same thing in .NET without using Java, but we wanted to show you how similar to Java code the .NET code could look. Please note that Java strings are returned as .NET strings in this example.

There are many configurable transformation options that govern the naming of the .NET proxy types, fields, and methods. You can choose to keep the proxy code looking more like Java or you can choose to make it look more like native .NET code.

But before you delve into the transformation options we want to make sure to communicate how the integration between .NET and Java works at runtime. The diagram below illustrates what is going on when your .NET code tries to talk to the Java side.

The connection between Java and .NET

Your own application code is written in one of the .NET langauges, for example C#. In the snippet above, that would be the AddDaysToDate function. You previously generated .NET proxy types (Java.Util.Calendar, Java.Text.SimpleDateFormat) using the code generator—we'll leave aside for now how you did that—and you just use these proxy types like normal .NET types by creating instances and calling their methods or dereferencing their fields.

Internally, these calls get delegated to utility functions in the managed JuggerNET runtime assembly. This delegation allows us to keep the proxy types nice and thin. The runtime assembly delegates to the unmanaged runtime library which eventually issues one or more Java Native Interface (JNI) calls on behalf of your function call and returns the results to you. The decision to split the runtime into managed and unmanaged libraries has two reasons:

  1. The unmanaged JuggerNET runtime library is actually the JunC++ion runtime library! This allows several of our customers to combine JunC++ion- and JuggerNET-components in the same process without having duelling runtimes that both want to be in control.
  2. Leveraging the JunC++ion runtime for JuggerNET allows us to have the same codebase for the complex JNI management layer, This greatly improves both products' stability and quality.

Please note that the JVM is running within the user process. In other words, there is no inter-process communication involved.

Fastest Possible

All this happens blazingly fast when compared to alternative integration approaches. JNI has a poor reputation when it comes to reliability and performance. Yet the fact remains that it is the fastest way of crossing the language boundary by far. By using a proven and tested runtime library and generated code to invoke the Java Native Interface we also make sure to obey best practices and avoid many of the performance traps and all of the memory leaks and crashes that plague hand-written JNI code.

If your unit of work on the Java side is not totally trivial you will hardly see any performance degradation at all in the integrated application. Similarly, any Java code that works with databases or network resources won't be seeing a measurable performance hit from being used in .NET.

It goes without saying that you or a Java API vendor can implement a problem-specific, hand-optimized integration solution that is faster than JuggerNET or JNI. Nevertheless, we maintain that among generic integration approaches the JuggerNET approach is the best performing.

Most Secure

The in-process nature of the integration also means that you do not have to worry about new security vulnerabilities due to the integration. There are no ports to be opened, firewalls to be configured, or certificates to be installed just to secure the integration solution. As long as you take a little bit of care with your runtime configuration, your threat profile is essentially the superset of a pure .NET application's and a pure Java application's threat profiles.

Most Complete And Most Sparse

Would you ever try to expose 3,000 Java types to a .NET application via SOAP or a custom network protocol? Of course not, that would be crazy. You might use these approaches when you have a few high-level service APIs that need to be remotely accessible.

The code generator does not care whether it generates 9 or 9,000 proxy types. Well, that's not completely true: you might have to give it more heap to generate a large proxy type set. Other than that, its built-in understanding of Java makes sure you end up with what you need and only what you need.

You can prune down the proxy type set to just the types you wish to code to. The code generator will make sure that the generated types do not contain any element referencing types that you did not generate. It is very easy to define the set of types you want to generate once you understand how to best express your intent.

Mature Configuration Framework

The Java Runtime Environment has many options—classpath is just the best known one—and you might have to use many of them to configure your integrated application's JVM. The JuggerNET runtime offers a mature configuration API that allows you to hardcode settings in your application, use configuration files, pick up settings from the environment, or even from shared libraries when they get loaded.