Blog

Callbacks (part 3)

In the third part of our series on callbacks, we’ll discuss what to do if we have a .NET assembly that implements callbacks using a Java-style listener interface. (See part one: using callbacks in .NET-to-Java projects and part two: callbacks in Java-to-.NET projects.) Since JNBridgePro only supports the delegate/event callback style in Java-to-.NET projects, we need to have a way to convert between the two styles. Here’s one such way. Assume we have the following listener interface:

public interface MyCallbackInterface
{
    bool myCallbackMethod(int param1, string param2);  
}

and the following callback generator

public class CallbackGenerator
{
    public static void registerCallback(MyCallbackInterface theCallback) {…}  
    public static void fire() {…}
}

We can create .NET event-style wrappers that can be proxied as follows:

public delegate bool MyCallbackHandler(int param1, string param2);

public class NewCallbackGenerator
{
    // this is the wrapper to convert listeners to events 
    private class MyCallbackHandlerWrapper : MyCallbackInterface
    {
        MyCallbackHandler mch;
        public MyCallbackHandlerWrapper(MyCallbackHandler mch)
        {
            this.mch = mch;
        }
  
        public bool myCallbackMethod(int param1, string param2)
        {   
            return mch(param1, param2);
        }
    }
  
    // here is the event interface
    public static event MyCallbackHandler MyEvent
    {  
        add
        {
            MyCallbackHandlerWrapper mcw = new MyCallbackHandlerWrapper(value);
            CallbackGenerator.registerCallback(mcw);
        }
    }
  
    public static void fireEvents()
    {   
        CallbackGenerator.fire();
    }
}

You should proxy and use MyCallbackHandler and NewCallbackGenerator as you would any other proxied delegate/event-style callback.

Callbacks (part 2, at long last)

It’s been a while since I’ve blogged (hey, we’ve been busy :-)). I think the best place to pick up is with the long-promised second part of our article on callbacks. In the first part, I wrote about how to register .NET classes as listeners for Java events (in .NET-to-Java projects). In this post, I’ll talk about how to write Java classes that can be registered as listeners for .NET events (in Java-to-.NET projects).

Let’s start by describing the .NET event generator class:

public delegate void MyEventHandler(string message);

public class EventGenerator
{
   public event MyEventHandler myEvent;

   public void fireEvents(string message)
   {
      myEvent(message);
   }
}

The first thing to note is that our .NET event generator class uses events and delegates, rather than Java-style listener interfaces. This is the typical .NET style, and it can be confusing at first if you’re used to Java listeners. Because this is the encouraged .NET style for callbacks (and the way that the .NET Base Class Library does it), it’s the style we support, too. (In the next callback post, I’ll discuss how to create Java callbacks for .NET classes that use listener-style callback interfaces.) The above code includes a delegate type to represent the event handler, and an event (of that delegate type) as part of the event generator class. There’s also a method used to fire all the registered events. To register an event handler in .NET, we typically create a method:

public void sampleEventHandler(string message)
{
   Console.WriteLine(“sample event handler: message = “ + message);
}

then designate it as a delegate and add it to the event:

myEvent += new MyEventHandler(sampleEventHandler);

then, when fireEvents() is called, all such methods added to myEvent will be called. Since delegates and events don’t exist in Java, we need to handle them in a somewhat different way on the .NET side. Start by proxying EventGenerator and MyEventHandler. Include supporting classes. MyEventHandler is proxied as an interface. It has one method, Invoke(), whose signature is identical to that of the underlying delegate:

void Invoke(string);

Our Java callback class must implement MyEventHandler:

public class CallbackClass implements MyEventHandler
{
   // method implementation required by MyEventHandler
   public void Invoke(String message)
   {
      System.out.println("callback fired: message = " + message);
   }
}

The event myEvent in EventGenerator is proxied on the Java side as a pair of methods, add_myEvent() and remove_myEvent(), which are called to add or remove an event handler.

In our Java code, we instantiate EventGenerator (by instantiating its proxy), then instantiate CallbackClass and register it with myEvent. We can do this because CallbackClass implements the proxied delegate MyEventHandler.

// create the event generator
EventGenerator eg = new EventGenerator();</span>

// register an event handler
eg.add_myEvent(new CallbackClass());

Now, when we call eg.fireEvents(), all the registered event handlers will execute, including all Invoke() methods of registered Java-side event handlers.

Note that, just like in the .NET-to-Java situation, in Java-to-.NET projects the .NET side will suspend when it calls the Java-side callback and wait until it returns, in order to preserve the expected callback semantics. As in the .NET-to-Java situation, this can sometimes lead to deadlock, particularly in cases involving multi-threaded applications and GUIs. In the previous callback post, we discussed how this problem was addressed in .NET-to-Java projects through use of the [AsyncCallback] attribute. In Java-to-.NET projects, we do something similar: Java callback classes, in addition to implementing their proxied delegate interfaces, can also implement the marker interface com.jnbridge.jnbcore.AsyncCallback, which has no methods. When the .NET side calls a Java callback object implementing AsyncCallback, it doesn’t wait for the callback method to return, but rather continues on. This means that values returned by asynchronous callbacks, and exceptions thrown by them, are ignored.

In the third post on callbacks, we’ll discuss what to do if we have a .NET assembly that implements callbacks using a Java-style listener interface.

Callbacks (part 1)

Customers sometimes come to us saying they have a .NET-to-Java project, and asking how they can pass a real .NET object to a method in the proxied Java object. Often they’ll subclass a proxy or implement a proxied interface, but when they actually pass the .NET object to the proxy, they get an exception. I explain that what they really are trying to do is to implement a callback: the .NET code has called the Java side, and during that execution, the Java code “calls back” some .NET object that’s previously been registered as a “callback.” If you’re familiar with Java listener interfaces, you’ll know how this all works.

Callbacks are easy to implement. In this post, we’ll discuss how to implement a callback in a .NET-to-Java project. In the next post, we’ll discuss callbacks in Java-to-.NET projects.

Let’s start with a Java class that allows other classes to register themselves as listeners for particular events, then notifies those listeners that those events occur. All listeners must implement a particular listener interface, which must have at least one method that will be executed when the event occurs. The method can take parameters, and can return values or throw exceptions. Here’s a listener interface:

// this is Java
public interface MyListener
{
   public void fireEvent();
}

Now that we’ve got the listener, here’s the event generator class we’ll be working with:

// this is Java
public class MyEventGenerator
{
   private static java.util.ArrayList listeners = new java.util.ArrayList();

   public static void registerListener(MyListener aListener)
   {
      listeners.add(aListener);
   }

   public static void fireListeners()
   {
      for(int i = 0; i &lt; listeners.size(); i++)
      {
         MyListener aListener = (MyListener) listeners.get(i);
         aListener.fireEvent();
      }
   }
}

If we’ve proxied the MyListener and MyEventGenerator classes, we can implement .NET classes that can register themselves with MyEventGenerator as listeners:

// this is C#
[Callback("MyListener")]
public class DotNetListener : MyListener
{
   public void fireEvent()
   {
      Console.WriteLine("event fired!");
   }
}

Note that DotNetListener implements the proxied MyListener interface. Also note that it has the [Callback] attribute. These both are essential. The [Callback] attribute’s class is really com.jnbridge.jnbcore.CallbackAttribute, so you’ll need to make sure you’ve imported that namespace into your program (using the import (if VB.NET) or using (if C#) keywords). The [Callback] attribute needs the fully-qualified name of the interface, so if the interface is in a package, or if it’s nested inside another class, you’ll need to supply the entire name. Also note the method fireEvent() that any class implementing MyListener must implement.

Now we can use DotNetListener wherever a proxy expects a MyListener:

// this is C#
MyEventGenerator.registerListener(new DotNetListener());
MyEventGenerator.fireListener();

The console will now print "event fired!".

Callbacks can take parameters, return values, or throw exceptions, but the parameters, return values, and exceptions must be of classes that the Java side “understands,” which means they need to be proxies, primitives, strings, or arrays of those.

A callback class can implement multiple listener interfaces, but if it does, you’ll need to annotate the callback class with a [Callback] attribute for each listener interface it implements.

One subtlety to be aware of is that when the Java side calls the callback, the Java thread doing the calling suspends until the callback returns. This is done to preserve the expected callback semantics. Since the Java thread has suspended, it’s likely that the .NET thread that originally called the Java side has suspended, since it’s waiting for the Java side to return. Usually this is fine and expected, but sometimes, as when you’re using the callback inside a Windows Form, it can lead to deadlock. To get around this problem, use an asynchronous callback, which is designated using the [AsyncCallback] attribute rather than the [Callback] attribute. When an asynchronous callback is called, the Java-side thread doesn’t wait around until the callback returns, but goes on its way. This avoids the deadlock issue. The tradeoff is that since the Java side doesn’t wait around for the result of the callback, asynchronous callbacks can’t return values or throw exceptions.

That’s all there is to callbacks! In our next post, we’ll explain how callbacks work in the opposite direction: in Java-to-.NET projects.