Bridging Between JVM-Based Languages and .NET

This article originally appeared in JAXenter Magazine.

Bridging Between JVM-Based Languages and .NET: Bridging is the answer to interoperability issues

By Wayne Citrin

April 5, 2016

As the go-between for Java byte code and a hardware platform, a Java Virtual Machine (JVM) is a critical link in computer processing. But despite its name, JVM is not just for Java. It’s a platform for plenty of other JVM-based languages, many of which — like Jython, Clojure, Groovy, Scala and JRuby — have a reasonably large developer community.

In general, these JVM-based languages allow a developer to write code that calls Java binaries — not surprising, since everything underneath is Java binaries running on the JVM, and it should all work together seamlessly. Using a technique called “bridging,” you can use this same ability to call Java binaries to also call .NET-based binaries — that is, libraries written in .NET languages.

Bridging Benefits Explained

Bridging solutions transparently manage the communications between .NET classes that run on a CLR and Java classes that run on a JVM. Bridging’s advantages are many. For one, they are typically a lower-cost choice, as they allow the developer to continue to use the best components for the job, whether Java- or .NET-based. There’s no need to invest in additional components for compatibility’s sake.

Bridging also eliminates an increasingly common problem among developers: “developer fatigue”. Recoding from Java to .NET or vice versa typically requires intensive time commitments — both in learning new languages and performing the actual recoding. Certainly, if you’re a .NET developer that must learn a new JVM-based language, the task may be overwhelming. Bridging solutions allow the developer to start slowly by learning the basic structures and then leveraging existing .NET skills.

Bridging and Proxies

The preferred bridging technique is to use “proxies”. In order to allow calls from .NET methods to Java methods, proxies are created on the .NET platform that mimic the interfaces of the corresponding Java classes. A .NET class can inherit from a Java class by inheriting from the Java class’s proxy, and vice versa.

JNBridgePro is an example of a bridging solution. After creating Java-based proxies for the .NET classes, JNBridgePro uses the JVM-based language to call Java classes to call the proxies, and (by extension) the underlying .NET classes.

A few examples

Let’s work through examples in showing how to access .NET code from JVM-based languages. We’ll provide brief excerpts here, with links to the full examples below.

In all the examples, we’ll call a simple .NET library written in C#:

namespace DotNetLibrary
{
     public class HelloWorldFromDotNet
     {
          private string theString = "";

          public HelloWorldFromDotNet()
          {
               theString = "Hello World from .NET!";
          }

          public HelloWorldFromDotNet(string s)
          {
               theString = s;
          }

          public string returnString()
          {
               return theString;
          }
     }
}

We’ll show how, in a variety of JVM-based languages, we can instantiate HelloWorldFromDotNet objects using each of the provided constructors, then call the returnString() method on each of the instantiated objects.

If a bridging solution employs proxies, the solution comes with a proxy generation tool that analyzes the target .NET binary and generates a Java binary consisting of proxies whose names and members mirror those of the underlying .NET code. The first thing we do is use the proxy generation tool to create a Java proxy class for DotNetLibrary.HelloWorldFromDotNet. As expected, the generated Java binary will offer an API identical to that of the underlying .NET code. We create a new project in whatever language we’re using, and add the Java proxies to the classpath, along with whatever other runtime components are required by the bridging solution.

Now, we can call the proxies the same way as we call any other Java class, and by doing so, we’re interacting with the underlying Java code. So, for example, if we’re programming in Jython, we can write code like this:

import DotNetLibrary

# bridge-mechanism-specific configuration code goes here
...
h = DotNetLibrary.HelloWorldFromDotNet()
print h.returnString()

h = DotNetLibrary.HelloWorldFromDotNet("Test String from DotNet")
print h.returnString()

In the code here, the constructors and the returnString() method in the proxy class are called just as if they were Java constructors and methods (because they are), but calls to the proxies result in executing the underlying .NET code, and we’ll get the output:

Hello World from .NET!
Test String from DotNet

Similarly, one can do the following in Groovy:

import DotNetLibrary.HelloWorldFromDotNet

class SimpleTest {

     static main(args) {

          // bridge-mechanism-specific configuration code goes here

          def helloWorldFromDotNet = new HelloWorldFromDotNet()
          println helloWorldFromDotNet.returnString()

          def helloWorldFromDotNet2 = new HelloWorldFromDotNet()
          println helloWorldFromDotNet.returnString("Test String from DotNet")
     }
}

Here’s how it’s done in Clojure:

;; bridge-specific configuration code goes here
(def h1 (new DotNetLibrary.HelloWorldFromDotNet))
(.returnString h1)
(def h2 (new DotNetLibrary.HelloWorldFromDotNet "Test String from DotNet"))
(.returnString h2)

In both the Groovy and the Clojure examples, the output is the same as it was when using Jython.

You can see what all three of these examples have in common. The proxies generated from the .NET code are just Java binaries, and are called from the JVM-based language in the same way as any other Java binaries might be called. In all cases, calls to the proxy constructors, methods and other members are automatically redirected to the .NET code, which is executed. Any return values are sent back to the original program in the JVM-based language, where they’re processed in the same way as any other return value. You can use the same strategies to call .NET code from any other JVM-based language, as long as the language allows calls to Java binary code.

It’s also possible to call code written in JVM-based language from .NET. Some JVM-based languages, like Groovy, Clojure and Jython, run inside their own Java-based runtime environment, which exposes a Java-based invocation API that’s ordinarily accessed from Java code to allow Java applications to call code written in one of these other languages. In such cases, the invocation APIs can be proxied into .NET, so similar calls can be made from .NET applications.

Other JVM-based languages compile their code into Java binaries. Bridging tools can generate .NET proxies from the Java binaries, and you can write .NET code that calls those proxies in the same way that the above examples do, except that in this case the calls are from .NET to JVM-based language, instead of in the other direction. In any case, details of how one can call from .NET code to code written in JVM-based languages are beyond the scope of this article.

Conclusion

Historically, developers have approached interoperability issues with dread, knowing that getting JVM and .NET components to work together seamlessly is an almost insurmountable task. But bridging has emerged as an effective solution to interoperability issues, with such technologies as JNBridgePro leading the way in innovation in the space. With bridging, language is not an obstacle, and developers can confidently move forward on their projects with no worry about cost or complexity.