days
0
4
hours
1
5
minutes
5
1
seconds
4
3
Bridging between JVM-based languages and .NET

Bridging is the answer to interoperability issues

Wayne Citrin
.NET
Suspension bridge image via Shutterstock

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.

JVM is a platform for plenty of other JVM-based languages, many of which – such as 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 a surprising news, since everything underneath is Java binaries running on the JVM, and it should all work together seamlessly. By using a technique called “bridging,” you can use the 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 which run on a JVM. Bridging’s advantages are countless. First, 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 massive time commitments – both in learning new languages and performing the actual recoding. Certainly, if you’re a .NET developer who 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 that mimic the interfaces of the corresponding Java classes are created on the .NET platform. A .NET class can derive from a Java class by inheriting from the latter’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 go through some examples which show how to access .NET code from JVM-based languages. We’ll provide brief excerpts here, with links to the full examples below. In all these 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 to instantiate HelloWorldFromDotNet objects in a variety of JVM-based languages 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
 
    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 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, thus exposing a Java-based invocation API that’s ordinarily accessed from Java code. The logic behind it is 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. The difference is that in this case the calls are from .NET to JVM-based language, instead of the other way round. 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 with their projects without any worries about cost or complexity.

Author

Wayne Citrin

Dr. Wayne Citrin is CTO and cofounder of JNBridge, LLC, the leading provider of interoperability tools to connect Java and .NET frameworks.


Comments
comments powered by Disqus