Interop Briefs: Out-of-proc interop using Intrinsyc's J-Integra

(This piece originally appeared on TheServerSide under the title "Interop Across the Wire" on 16 November 2006. I've fixed the--again--horrendous formatting problems and touched it up slightly. Changes are in italics.)

Welcome to the next installment of “As the Interop World Turns”. In this particular bit, we’re examining interop across the wire, but before we do, let’s acknowledge the major news in the interoperability arena, the announcement of the formation of the Interoperability Alliance, bringing together Microsoft, BEA, Sun, and another dozen or so vendors, all focused on making it easier to play nicely between the platforms.

Practically speaking, however, at this point the Interop Alliance hasn’t significantly changed the interop landscape, so while it’s important to note that they exist, there’s nothing more to report. Whether this will turn into Something Big, or just another meaningless consortium of vendors remains to be seen—for now, it remains as a “potential” industry-affecting move.

On to more practical matters.

In recent years, most focus about interoperability between Java and .NET has been directly on the WS-* stack, AKA “Web Services”. For almost a decade now, the various vendors involved in the various WS-* standardization efforts (and even those who don’t participate directly but graft on to the edges somehow) have promised that as soon as the standards are here, and the implementations all implement the standards, seamless and ubiquitous interoperability across all platforms will be ours.

We’re waiting...

In the meantime, however, it turns out–-according to those incredibly insightful people at Gartner and other “analysis agencies”--that most of the time, the only two platforms that principally draw interop interest are the JVM and the CLR. Hardly a surprise, for those of us who actually work for a living. And, as it turns out, if you’re looking to limit your interoperability to those two platforms, numerous toolkits abound already to make this happen.

While open-source toolkits also exist, in general they aren’t quite “up to speed” against the commercial toolkits, so in this entry we’ll focus on those, mainly the tools offered by J-Intrinsyc. Other commercial tools include JNBridgePro and Borland's Janeva. (In the time since this article's publication--which is a pretty short window, making me wonder if this wasn't the case before publication--attempting to download a trial of Janeva results in an error on Borland's site. More interestingly, Borland's latest release of VisiBroker claims .NET support, so it's possible that Janeva is being discontinued in favor of slipping .NET support into VisiBroker.) Each effectively provides a binary RPC-based interop approach, in which you follow a development process that’s (deliberately) similar to what’s done when working with the native ORPC stack (CORBA or RMI for Java, .NET Remoting for .NET). In several cases, the toolkits use the wire syntax and format of one of the two platforms (IIOP, JRMP or the .NET Remoting format), meaning that for one of the two platforms, the experience is seamless. (Which platform gets to be the seamless experience is up to you, of course, but practical considerations—and a desire to continue to do business with your clients—generally dictate that your clients have the better experience. Choose wisely.)

In the case of Janeva, (or any other CORBA tool, for that matter) the definitions are done in CORBA IDL, a language strikingly and deliberately similar to Java/C++/C# interface declarations. Developers familiar with CORBA will know what to do with these definitions on a .NET platform: simply run the ORB's code-gen tool over the IDL file, which will generate stubs (client-side proxies) or skeletons (server-side proxies) as necessary. For existing CORBA systems, this is likely to be the easiest thing for a .NET client to do to hook in, but remember that CORBA IDL is an entire language and type system in of itself, and CORBA itself represents a fairly sizable stack to get used to - easily dwarfing what’s in the .NET Remoting stack in both size and complexity.

For simpler scenarios, it’s generally easier to use something a little less intimidating (and, correspondingly, less powerful), such as the JaNET or JNBridge tools. Each is equally useful in my opinion, so I’m picking one at random here to use as a demo. JNBridge lost the toss (seriously!), so I’m going to use the J-Integra tool for this demo. This is actually taken from one of the demos shipping with their product, so if you feel like following along, grab the eval demo off their website, install, and look for the HelloWorld demo in the examples directory.

J-Integra takes a “.NET-friendly” perspective, meaning that the development experience is a bit easier on the .NET developer than the Java developer. (JNBridgePro take the opposite tack, for what that’s worth.) Thus, for the C# developer, developing an interoperable scenario is as simple as writing a typical .NET Remoting component—build a class that extends System.MarshalByRefObject:

// Copyright 2001-2003 Intrinsyc Software Inc.
// All rights reserved.
using System;
namespace HelloWorld
{
  public class HelloWorldClass: System.MarshalByRefObject
  {
    private String name;

    public HelloWorldClass(String name) {
      this.name = name;
    }

    public String getMessage() {
      return “Hello World, from ” + name;
    }
  }
}

From a .NET Remoting perspective, there’s absolutely nothing interesting about this class, which is exactly the point—any existing .NET Remoting servers can be flipped to be interoperable by doing exactly nothing. (In this particular demo, Intrinsyc has the HelloWorldClass instances being hosted by ASP.NET, but obviously we could just as easily self-host it if desired—see Ingo Rammer’s “Advanced .NET Remoting” from APress for details if you’re not “up” on your .NET Remoting.)

To get Java to call this guy, we need to do run J-Integra’s “GenJava” tool to create Java client proxies and compile them. Once those proxies are generated and compiled (and unfortunately I don’t see any custom Ant tasks to do this, so you’ll likely have to write an “exec” task to do it), drop them into your client .jar file, and call the proxies by name:

// Copyright 2001-2003 Intrinsyc Software Inc.
// All rights reserved.

import HelloWorld.*;

public class HelloWorldMain {
  public static void main(String[] args) throws Exception {
    HelloWorldClass helloWorldClass = new HelloWorldClass(”Fred”);
    System.out.println(helloWorldClass.getMessage());
  }
}

Again, nothing special, which is the point—the “magic” takes place inside the generated proxy, which (based on the settings in the GenJava tool) knows how to call over HTTP to the ASP.NET server hosting the HelloWorld instance, execute the call, and send back the returned String to the client.

(Before the JNBridgePro folks get peeved at me, let me quickly point out that the development experience there is going to be much the same: point their code-gen tool at the Java RMI server objects you want .NET to communicate to, and use those proxies as-is from C#.)

While tempting, there are some caveats to this approach. First, be careful when considering binary RPC-based approaches to interop, because the interface-based code-gen approach carries with it a nasty side effect: once published, a given interop endpoint can never be modified again without requiring all of its clients to also change with it. While this isn’t a major consideration during development of the project initially, it can be devastating when attempting to refactor code later, after the system has been initially released. This kind of tight coupling works against many agile projects, so choose your interfaces (whether Java or .NET based) with care. (And before the comments start flying, let’s be very clear about this: the tight coupling descends from the proxy-based code-generation approach, and not anything to do with the tools themselves. WSDL-based code-generated proxies fall into the same trap.)

Secondly, using either of these tools assumes that you will never need to branch beyond the Java and .NET platforms; should you have to incorporate Ruby or “legacy” C++ into the mix, for example, you’re out of luck. This is where the “open-ended” interoperability of the WS-* stack (or its conceptual predecessor, CORBA) holds its own, and if there’s any reason to suspect that you’ll need to reach beyond the JVM and CLR, you should consider an IIOP-based or WS-*-based solution. (Be careful, however, since as of this writing I’m not aware of any Ruby-CORBA packages, so even CORBA could be a dead end if you need to plug Ruby into the mix.)

Thirdly, remember that out of the box, these tools generally focus on cross-process communication, and so that means that each method call across the boundary is not only a platform shift, but also a network traversal. Loosely translated, that means “hideously expensive” in performance terms. Even those toolkits that offer support across shared memory channels still go through the marshaling/unmarshaling process, so it’s still not as cheap as an in-proc method call. As with most interoperability scenarios, try to minimize the amount of back-and-forth between the two platforms. (That having been said, however, the JNBridge blog shows how to embed Swing components inside of a WinForms form, which represents a powerful idea and one that shouldn’t be discarded out-of-hand. Just be sure to perf-test.)

The tight-coupling concern is a biggie, however, so in future installments we’ll look at ways to avoid it by using messaging tactics, instead of RPC-based ones. Until then, remember, Java and .NET are like your kids: you love them both… “the same”.

(Shortly after publication, Wayne Citrin of JNBridgePro posted a comment that I felt merited reprinting here. Unedited, it appears below.)

Ted –

Thanks for writing about JNBridgePro. I do have a couple of comments on the interop post.

- You mention that J-Integra is “.NET-friendly” and we (JNBridgePro) are the opposite. (Presumably, “Java-friendly”.) I don’t think that’s the case, since (1) our proxy tool is written in .NET, and J-Integra’s is written in Java, and (2) J-Integra exposes the low-level details of .NET Remoting to the user, while we hide those details (which is friendly no matter where your development experience lies). In any case, we try to make the product user-friendly whether your background is in Java or in .NET.

- While both JNBridgePro and J-Integra both use .NET Remoting, there are more differences between the two products than might be evident from your post. Enumerating them is beyond the scope of this comment, but one that jumps out from your J-Integra example is that for Java-calling-.NET scenarios, J-Integra requires that the accessed objects be .NET-Remotable components: either MarshalByRefObject (if they’re being accessed by reference) or ISerializable (if they’re being accessed by value).JNBridgePro allows the Java code to call _any_ .NET object (MarshalByRefObject, ISerializable, or anything else).

- Similarly, for .NET-to-Java directions, with JNBridgePro the .NET code can access _any_ Java object or class; it doesn’t need to be RMI-remotable.

- The “in-process” vs. “RPC-interop”/”across-the-wire” dichotomy doesn’t entirely hold up. (You do touch upon this at the end of your post, but I wanted to elaborate.) While JNBridgePro does support “across-the-wire” (what we call “socket-based”) interop, as you mention we also offer a shared-memory communications mechanism that allows the CLR and the JVM to run in process. This mechanism is very popular with our users, and is much faster than the socket-based approach. (You’re correct that it still needs to go through the marshalling/unmarshalling mechanism, so there’s overhead, but you avoid the overhead of traversing the socket stack and doing process switching.) Unlike IKVM, our shared-memory approach is still a bridging solution, and still uses .NET Remoting, which is evidence of the power and flexibility of the .NET Remoting mechanism.

- Thanks for pointing out the GUI-embedding blog entry. The support code mentioned in that blog entry has now been integrated into our new version 3.1, which makes this type of embedding a lot simpler. We’ve updated the blog entry to reflect that.

Wayne

(Readers are, of course, encouraged to download both and form their own opinions.)