Search This Blog

Sunday, June 29, 2014

Remote Procedure Calls between .NET and Java using TCP

Simple example showing RPC communication between .NET and Java.

The source code for this example can be downloaded from:
http://www.eneter.net/Downloads/Examples/RpcCalculatorJavaService.zip

To use the example please download Eneter for .NET and Java from http://www.eneter.net/ProductDownload.htm and add the Eneter library into the project.


Introduction

This article is a free continuation of Remote Procedure Calls between Android and .NET where Android is a client application and .NET is a service application.

The example bellow demonstrates how to use RPC between .NET and Java where .NET is the client application and Java is the service application.


RPC between .NET and Java

As already described in the previous article RPC is a request-response communication where one application (typically client) requests to invoke a method in another application (typically service).

Therefore the key aspect in RPC is exposing methods which can be remotely invoked. The Eneter framework allows to specify these methods via the 'interface' keyword which is supported by both Java and C# languages.


For declaring the interface that shall work across .NET and Java it is important declarations in both environments are identical.
  • Methods must have same names (names are case sensitive).
  • Input parameters must be of same types.
  • Input parameters must be in same order.
Due to language differences there are also following restrictions:
  • Generics are not supported.
  • Overloading of methods is not allowed.

In general to declare, expose and use the interface between .NET and Java you need to do following steps:
On the service side:
  1. Declare API interface that shall be exposed (see ICalculator interface in Java).
  2. Provide implementation for the interface (see Calculator class).
  3. Use Eneter to expose the interface (see using RpcFactory instantiating IRpcService).
  4. Use Eneter to start listening via TCP, WebSocket or UDP (see attaching the input channel to IRpcService).
On the client side:
  1. Declare identical API interface as in the service (see ICalculator interface in C#).
  2. Use Eneter to provide the proxy implementation for the interface (see using RpcFactory instantiating IRpcClient).
  3. Use Eneter to connect the proxy implementation with the service (see attaching the output channel to IRpcClient).
  4. Use the 'Proxy' property to call methods from the interface.

Java Service Application

The service is a simple console application that calculates numbers. It exposes ICalculation interface which declares methods that can be called from a .NET application.

Declaration of the interface:

/**
 * Declares calculator service methods.
 */
public interface ICalculator
{
    double sum(double a, double b);
    double subtract(double a, double b);
    double multiplay(double a, double b);
    double divide(double a, double b);
}
Implementation of the interface:

/**
 * Implements service methods.
 */
public class Calculator implements ICalculator
{
    @Override
    public double sum(double a, double b)
    {
        return a + b;
    }

    @Override
    public double subtract(double a, double b)
    {
        return a - b;
    }

    @Override
    public double multiplay(double a, double b)
    {
        return a * b;
    }

    @Override
    public double divide(double a, double b)
    {
        return a / b;
    }
}
Listening console application exposing the interface:
package calculator;

import java.io.*;

import eneter.messaging.endpoints.rpc.*;
import eneter.messaging.messagingsystems.messagingsystembase.*;
import eneter.messaging.messagingsystems.tcpmessagingsystem.TcpMessagingSystemFactory;

public class Program
{
    public static void main(String[] args) throws Exception
    {
        // Instantiate the calculator.
        Calculator aCalculator = new Calculator();
        
        // Expose the calculator as a service.
        RpcFactory anRpcFactory = new RpcFactory();
        IRpcService<ICalculator> aService =
            anRpcFactory.createSingleInstanceService(aCalculator, ICalculator.class);
        
        // Use TCP.
        // Note: You can use other protocols e.g. WebSockets too.
        IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
        IDuplexInputChannel anInputChannel =
            aMessaging.createDuplexInputChannel("tcp://127.0.0.1:8045/");
        
        // Attach input channel and start listening.
        aService.attachDuplexInputChannel(anInputChannel);
        
        System.out.println("Calculator service is running. Press ENTER to stop.");
        new BufferedReader(new InputStreamReader(System.in)).readLine();
        
        // Detach input channel and stop listening.
        aService.detachDuplexInputChannel();
    }
}


.NET Client Application

The client is a simple WinForm application using the Java service to calculate numbers. It declares the same interface like the Java service and uses RPC from Eneter framework to communicate with the Java service.

The whole implementation is very simple:

using System;
using System.Windows.Forms;
using Eneter.Messaging.EndPoints.Rpc;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;

namespace CalculatorClient2
{
    public partial class Form1 : Form
    {
        // Declare same interface as in Java service application.
        // Note: do not forget case-sensitiveness. If Java methods start with
        //       small letters then also these methods must start with small letters.
        public interface ICalculator
        {
            double sum(double a, double b);
            double subtract(double a, double b);
            double multiplay(double a, double b);
            double divide(double a, double b);
        }

        private IRpcClient<ICalculator> myRpcClient;

        public Form1()
        {
            InitializeComponent();

            // Get RPC client for declared service interface.
            IRpcFactory anRpcFactory = new RpcFactory();
            myRpcClient = anRpcFactory.CreateClient<ICalculator>();

            // Attach output channel and be able to communicate.
            TcpMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
            IDuplexOutputChannel anOutputChannel = 
                aMessaging.CreateDuplexOutputChannel("tcp://127.0.0.1:8045/");
            myRpcClient.AttachDuplexOutputChannel(anOutputChannel);
        }


        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Stop listening by detaching the input channel.
            myRpcClient.DetachDuplexOutputChannel();
        }

        private void SumButton_Click(object sender, EventArgs e)
        {
            double a = double.Parse(Number1TextBox.Text);
            double b = double.Parse(Number2TextBox.Text);
            ResultLabel.Text = myRpcClient.Proxy.sum(a, b).ToString();
        }
         
        private void SubtractButton_Click(object sender, EventArgs e)
        {
            double a = double.Parse(Number1TextBox.Text);
            double b = double.Parse(Number2TextBox.Text);
            ResultLabel.Text = myRpcClient.Proxy.subtract(a, b).ToString();
        }

        private void MultiplyButton_Click(object sender, EventArgs e)
        {
            double a = double.Parse(Number1TextBox.Text);
            double b = double.Parse(Number2TextBox.Text);
            ResultLabel.Text = myRpcClient.Proxy.multiplay(a, b).ToString();
        }

        private void DivideButton_Click(object sender, EventArgs e)
        {
            double a = double.Parse(Number1TextBox.Text);
            double b = double.Parse(Number2TextBox.Text);
            ResultLabel.Text = myRpcClient.Proxy.divide(a, b).ToString();
        }
    }
}

8 comments:

  1. i'm using eneter messaging framework , i have a .NET service wich is working good and i can send message to a javascript client with success , i want do the same to a java client by sockets but it looks like i am receiving nothing.

    // Websocket factory client.
    IMessagingSystemFactory aWebScketMessagingFactory = new WebSocketMessagingSystemFactory();

    // Create duplex output channel to send and receive messages.
    IDuplexOutputChannel myOutputChannel = aWebScketMessagingFactory.createDuplexOutputChannel("ws://127.0.0.1:8843/Test/");


    // Create the broker client
    IDuplexBrokerFactory aDuplexBrokerFactory = new DuplexBrokerFactory();
    IDuplexBrokerClient myBrokerClient = aDuplexBrokerFactory.createBrokerClient();

    // Handler to process notification messages.
    /*myBrokerClient.brokerMessageReceived().subscribe(myNotifyMessageReceived);*/
    myBrokerClient.brokerMessageReceived().subscribe(new EventHandler() {

    @Override
    public void onEvent(Object sender, BrokerMessageReceivedEventArgs event) {
    System.out.println("inside onEvent");
    if(event.getMessageTypeId() == "Hello"){
    System.out.println("######################## message : "+event.getMessage().toString());
    }else{
    System.out.println("########## message : NOK");
    }
    }
    });

    // Attach the channel to the broker client to be able to send and receive messages.
    myBrokerClient.attachDuplexOutputChannel(myOutputChannel);


    // Subscribe in broker to receive chat messages.
    myBrokerClient.subscribe("Hello");
    i can see that the client is connected , but i can't receive any message from the service.

    what can be the problem ?

    ReplyDelete
    Replies
    1. You say you have working communication between JavaScript client and .NET service.
      It means your service must use a JSON serializer instead of default XmlStringSerializer. It means your service contains code like this:

      ISerializer aSerializer = new DataContractJsonStringSerializer();
      IDuplexBrokerFactory aBrokerFactory = new DuplexBrokerFactory(aSerializer);
      IDuplexBroker aBroker = aBrokerFactory.CreateBroker();

      Looking into your code I do not see you you set a serializer. It means your Java client uses default XmlStringSerializer.

      IDuplexBrokerFactory aDuplexBrokerFactory = new DuplexBrokerFactory();
      IDuplexBrokerClient myBrokerClient = aDuplexBrokerFactory.createBrokerClient();

      Therefore I think the problem could be your Java client uses XmlStringSerializer and your .NET service uses DataContractJsonSerializer and so they do not understand each other (although the connection can be still established).

      Eneter for Java does not provide a built-in JSON serializer (like in Eneter for .NET) but you can use this one:
      http://eneter.blogspot.de/2014/04/eneter-json-serializer-for-java.html

      I hope my response will help.

      Delete
    2. i tried something according to your last replay , but i still can't fix it
      here is what i have done


      public static void main(String[] args) throws Exception {

      // Provide JSON serializer when constructing the factory.
      ISerializer aSerializer = new JsonSerializer();
      IDuplexTypedMessagesFactory aFactory = new DuplexTypedMessagesFactory(aSerializer);

      // Factory create duplex typed message sender that uses our JSON serializer.
      IDuplexTypedMessageSender myCreateUser = aFactory.createDuplexTypedMessageSender(String.class, UserData.class);
      // Subscribe to receive response messages.
      myCreateUser.responseReceived().subscribe(myOnResponseHandler);


      // Create Socket messaging.
      IMessagingSystemFactory aMessaging = new WebSocketMessagingSystemFactory();
      IDuplexOutputChannel anOutputChannel = aMessaging.createDuplexOutputChannel("ws://127.0.0.1:8843/BarCodeReader2D/");

      // Attach output channel and be able to send messages and receive responses.
      myCreateUser.attachDuplexOutputChannel(anOutputChannel);
      }

      static private void onResponseReceived(Object sender, TypedResponseReceivedEventArgs e)
      {
      // Get the response message.
      String aReceivedResponse = e.getResponseMessage();
      System.out.println(aReceivedResponse);
      }


      // Response message handler
      static EventHandler> myOnResponseHandler = new EventHandler>()
      {
      @Override
      public void onEvent(Object sender, TypedResponseReceivedEventArgs e) {
      onResponseReceived(sender, e);
      }
      };

      Delete
    3. You use DuplexTypedMessageSender in this code snippet. However, in your previous code snippet you used DuplexBrokerClient in your Java client and DulpexBroker in your .NET service.
      I am not sure (because I do not see the whole code) but if your .NET service uses DuplexBroker and your client DuplexTypedMessageSender then it will not work.
      If service uses DuplexBroker then your client must use DuplexBrokerClient to interact with the broker.

      Just try to use the your original code snippet but put your JSON serializer into the constructor of the broker factory.

      Then also please do not forgot to subscribe in the broker to message Id you want to get as notification. E.g. by:
      myBrokerClient.subscribe("Hello");
      (That call will send message to the broker saying that your client wants to be notified about all messages with type id "Hello".)

      Delete
  2. thank you for your help , but i still can't fix it , i did what you asked me in your last post , and you can find my full client code here

    http://ideone.com/M39poU

    ReplyDelete
    Replies
    1. Could you please send me your service code too?
      Maybe we could switch to communicate via e-mail:
      eneter@eneter.net

      Delete
  3. Hi, I reference the Eneter in .NET, but the project didn't compile, it show me the message said that "the type or name 'Eneter' could not be found...". Do you know why don't work this project? Thanks. I'm waiting your answer. Thank you

    ReplyDelete
    Replies
    1. It looks the library was not correctly added into the project. Please doublecheck you followed these steps:
      1. Open the project in Visual Studio.
      2. Open 'References' and remove Eneter.Messaging.Framework
      3. Right click on 'References' and choose 'Add Reference ...' from the context menu.
      4. Browse to Eneter.Messaging.Framework.dll (to the place where you installed it). Then select the library (In Visusla Studio 2013 it must be checked) and press the Ok button.

      If everything is ok you should see Eneter.Messaging.Framework among references and you should be able to compile.

      Delete