JPPF Issue Tracker
star_faded.png
Please log in to bookmark issues
feature_request_small.png
CLOSED  Feature request JPPF-298  -  .Net bridge for JPPF
Posted Jul 29, 2014 - updated Jan 07, 2015
action_vote_minus_faded.png
0
Votes
action_vote_plus_faded.png
icon_info.png This issue has been closed with status "Closed" and resolution "RESOLVED".
Issue details
  • Type of issue
    Feature request
  • Status
     
    Closed
  • Assigned to
     lolo4j
  • Type of bug
    Not triaged
  • Likelihood
    Not triaged
  • Effect
    Not triaged
  • Posted by
     lolo4j
  • Owned by
    Not owned by anyone
  • Category
    Client
  • Resolution
    RESOLVED
  • Priority
    Normal
  • Targetted for
    icon_milestones.png JPPF 5.0
Issue description
The idea is to implement and demonstrate a direct use of the JPPF client APIs from a .NET program. The mapping from .NET (e.g. C#) to Java would done via a JNI bridge. The jni4net framework looks like a promising candidate for this.

#2
Comment posted by
 lolo4j
Sep 14, 11:01
In fact, IKVM.NET looks even more promising, especially since it is still actively maintained (jni4net hasn't been updated in 3 years).
#3
Comment posted by
 lolo4j
Sep 15, 11:59
I managed a first working implemntation using IKVM. Basically, the process is I followed is:
  • compile all the jars needed on the client side into a .NET assembly, using ikmvc tool, along with the conifguration files (jppf and log4j)
  • in Visual Studio, create a project which references the assembly + the IKVM.OpenJDK.*.dll assemblies
  • create a main class which uses the JPPF APIs available via the generated jppf.dll assembly
  • run it
The big difficulty I had was with marshalling, because IKVM was using the stubs it generates. I had to create a custom class loader which references the actual jppf jar files to proivde the proper class files to send to the server.

#5
Comment posted by
 lolo4j
Nov 25, 21:46
Finally, I realized IKVM would not allow me to run arbitrary .NET code on remote Java nodes, so I went back to jni4net (what I can do is "just" submit Java tasks from C# code).

I first tried to use C# tasks that would inherit from the proxies generated by jni4net for the JPPF classes like Task and AbstractTask. This does not work, because jni4net does not handle cross-boundaries inhreritance. I can understand how very difficult this would be to implement and I should have realized earlier. So, after over two months of trials I finally implemented an approach that works:

1) First I defined a C# class named BaseDotnetTask from which all C# tasks inherit, so they all have a common Execute() method.

2) I defined a C# class name DotnetSerializer whose purpose is to serialize/deserialize C# tasks into/from binary format. The tasks are serialized as byte[], which jni4net makes directly interoperable with the Java byte[]. This class also has an Execute(byte[]) method which deserializes byte[] into a subclass of BaseDotnetTask, calls its Execute() method, then serializes its new state into a returned byte[].

3) On the Java side, I implemented a special Task named DonetTaskWrapper which has a byte[] attribute holding the serialized C# task, and whose run() method does the following:
  • create an instance of the Java proxy generated by jni4net for DotnetSerializer
  • call its Execute() method to run the actual C# task ==> no need to generate Java proxies for the C# tasks
  • set the new bytes returned by Execute() as the "bytes" attribute of the DonetTaskWrapper, so JPPF can transport it back to the remote C# client
4) On the C# client side, I'm doing something similar to this, using C# proxies for the JPPF client APIs:
public class MyDotnetTask : BaseDotnetTask {
  public override void Execute() { ... }
}
 
JPPFClient client = new JPPFClient();
JPPFJob job = new JPPFJob();
job.setName(".NET job");
 
DotnetSerializer ser = new DotnetSerializer();
MyDotnetTask mdt = new MyDotnetTask();
byte[] bytes = ser.Serialize(mdt);
DotnetTaskWrapper dtw = new DotnetTaskWrapper(bytes);
Task t = job.add(dtw);
 
java.util.List results = client.submitJob(job);
dtw = (DotnetTaskWrapper) results.get(0);
mdt = (MyDotnetTask) ser.Deserialize(dtw.getBytes());
#7
Comment posted by
 lolo4j
Dec 08, 01:51
So far I have made quite a few features available to the .Net bridge, which require special handling: task cancellation notifications, task timeout, job listeners.

I'm currently working on the management features available from a client. In particular, dynamic mbean proxies will not work with the .Net bridge, since jni4net doesn't handle them, nor is it supposed to. Consequently, I'm working on implementing static proxies for all the built-in MBeans. Since I don't want to spend any time maintaining these static proxies, I'm writing a code generator which uses reflection on the MBean interfaces to generate the code of the static proxies. The generator is working so far, at least it generates proper code, but I still need to test it at runtime and then integrate the code generation into the build. Once that is done we should be able to get jni4net to generate the bridge for these classes to make them available to the .Net side.