Migrating Java RMI to .NET Remoting

Introduction

.NET and J2EE are both widely used technologies for creating Middleware solutions. Sometimes we do have code that can get us more benefit from running on the .NET framework and some of the code exists in the J2EE application already. Also to achieve the best of everything in a short time, we sometimes need to migrate our existing code to another environment instead of redesigning and rewriting them. For many customers, migration of technology will happen over time and there is a huge opportunity exists in the migration space.

In these two technologies, both .Net Remoting and Java RMI are mechanisms that allow the user to invoke methods/methods in another address space. The other address space could be on the same machine or a different one. These two mechanisms can be regarded as RPC in an object-oriented fashion. This article describes how to migrate Java RMI applications to .NET Remoting applications, with practical examples.

Before the migration process starts, we can make a comparison of these two concepts.

Java RMI vs. DotNet Remoting
 

Java RMI VS .NET Remoting .NET Remoting Java RMI
Proxy Dynamics Statics (rmic) or dynamics
Skeletons Integrated into Framework Integrated into Framework
Distributed object Classes / Remote Interfaces Remote Interfaces
Configuration File XML/ System Property System Property
Distributed directory No RmiRegistry
Addition of protocols Channels SocketFactoryImpl
Addition of formats Formatters Serialization
Activation Server (SingleCall or Singleton) and Client activated API with dimensions waiter Activable objects
CustomProxy Custom RealProxy Dynamic Proxy
Existing protocols HTTP, TCP, SOAP JRMP, IIOP, ORMI (Orion), T3 in WebLogic
Management of error Remote Exceptions Remote Exceptions


The Migration

Let us start our migration by creating a simple RMI application that will be used in our process of migration to .NET Remoting. This small application will accept a name from the client and return a welcome message.

The RMI Solution

Our RMI Solution will comprise of.

  • A remote Interface
  • A removable class that implements the interface
  • A Server that will sit and listen for object requests
  • A Client that will make requests for the remote object

Now create a folder called RMISample. We will use this as our default folder for the RMI solution.

The Remote Interface

Create a Java file called RemoteInterface.java and paste the following code. It contains two methods that will be implemented by a remote class.

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteInterface extends Remote { 
    public void WriteName(String str) throws RemoteException; 
    public String ReadWelcome() throws RemoteException;
}

The Implementation

Create a Java file called RemoteClass.java and paste the following code. Here we are implementing the remote methods ReadWelcome and WriteName. We will get a name (string) from the ReadWelcome method and will return a welcome message to the client application.

import java.rmi.RemoteException;
public class RemoteClass implements RemoteInterface {
    private static String strWelcomeClient;
    RemoteClass() {
        System.out.println("Object created");
    }
    public String ReadWelcome() {
        return strWelcomeClient;
    }
    public void WriteName(String strNameFromClient) {
        strWelcomeClient = "HI " + strNameFromClient + ". Welcome to RMI World!!";
    }
}

Our remote object class must implement the remote interface (RemoteInterface). And since we don’t throw any exceptions, we can omit the import of RemoteException.

The Server Application

Create a Java file called Server.java and paste the following code. In this application, we are creating an instance of the remote object and exporting it. Finally, we bind this instance to the RMI registry. Here I am using Cincom as the binding name.

import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server {
    public Server() {}
    public static void main(String args[]) {
        try {
            RemoteClass robj = new RemoteClass();
            RemoteInterface stub = (RemoteInterface) UnicastRemoteObject.exportObject(robj, 0);
            Registry registry = LocateRegistry.getRegistry();
            registry.bind("cincom", stub);
            System.out.println("The Server is ready .... Press the enter key to exit...");
        } catch (Exception e) {
            System.err.println("Server exception thrown: " + e.toString());
        }
    }
}

The exportObject() method takes two parameters, the instance of the remote object and the TCP port number. Here we are passing 0 for the default port number of RMI which is 1099.

The Client Application

To call our remote method, we need to create a Client application. Now create a java file called Client.java and paste the following code. Before calling the remote method, we should locate the host from the registry mechanism and then look up the remote object by its name. In our server application, we used Cincom as the name of the object. So we are using this name cincom for the lookup again.

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
    private static RemoteInterface stub = null;
    private Client() {}
    public static void main(String[] args) {
        try {
            Registry reg = LocateRegistry.getRegistry("localhost"); // localhost OR your server name
            stub = (RemoteInterface) reg.lookup("cincom");
        } catch (Exception e) {
            System.err.println("Client exception thrown: " + e.toString());
        }
        String strArgs;
        if (args.length == 0) {
            strArgs = "Client";
        } else {
            strArgs = args[0];
        }
        try {
            stub.WriteName(strArgs);
            System.out.println(stub.ReadWelcome());
        } catch (Exception e) {
            System.out.println("Remote method exception thrown: " + e.getMessage());
        }
    }
}

Running Application

  • open a dos command prompt
  • change the current working directory to your RMI directory …/RMISample/
  • compile all the java files using java –d. *.java
    Note. If you are using jdk below 1.5.0, you must use rmic utility to generate a stub class using rmic –v1.2 Server
  • run the rmiregistry using start rmiregistry
  • start the Server using Start Java Server
  • run the Client with a name as an argument using Client TestName

You will get an output as.

HI TestName. Welcome to RMI World!!.

Migrating the application to .NET Remoting


Introduction

The migration can be done in two ways. First, we can do the migration page by page (class by class). And in another (general) technique, we can have a removable class, a server, and a client with configuration files to set up our channels and formats. Now let us start the migration using the first technique.

First Technique

In this technique, we will do the migration page by page (class by class). Parallel to the RMI application, our .NET Remoting system will comprise of.

  • An Interface
  • A removable class that implements the interface
  • A Server that will sit and listen for object requests
  • A Client that will make requests for the remote object

The Project

Create a folder called Remoting.

The Interface

Inside the Remoting, folder create a csharp file called RemoteInterface.cs and paste the following code.

public interface RemoteInterface {
    void WriteName(String str);
    String ReadWelcome();
}

The Implementation

Inside the Remoting folder create another Java file called RemoteClass.cs and paste the following code.

using System;
public class RemoteClass : MarshalByRefObject, RemoteInterface {
    private static string strWelcomeClient;
    public RemoteClass() {
        Console.WriteLine("Object created");
    }
    public string ReadWelcome() {
        return strWelcomeClient;
    }
    public void WriteName(string strNameFromClient) {
        strWelcomeClient = "HI " + strNameFromClient + ". Welcome to Remoting World!!";
    }
}

Here we are implementing the methods ReadWelcome and WriteName.

The Server Application

Again inside the Remoting folder create one more java file called RemoteClass.cs and paste the following code. For communication among applications and app domains, the .NET framework includes two default channels:

  • HttpChannel (using SOAP formatting)
  • TcpChannel (using binary formatting)

Here we are using TcpChannel.

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
public class Server {
    public static void Main(string[] args) {
        TcpChannel channel = new TcpChannel(8888);
        ChannelServices.RegisterChannel(channel);
        RemotingConfiguration.RegisterWellKnownServiceType(
            typeof(RemoteClass), "cincom",
            WellKnownObjectMode.SingleCall);
        Console.WriteLine("The Server is ready .... Press the enter key to exit...");
        Console.ReadLine();
    }
}

In object activation, Marshal By Value objects have a simple activation scheme, they are created when the client first requests them. But Marshal By Reference objects have two activation schemes:

  • Server Activated Objects (SAO)
  • Client Activated Objects (CAO)

Server-activated objects have two registration types, Singleton and SingleCall. Singleton objects are instantiated one time only, and service all client requests in a multi-threaded fashion. SingleCall objects are at the other extreme, every time a remote method is invoked on these types of SAO a new remote object is created. In our case, we are using the SingleCall type.

The Client Application

Now create another folder called Client. Copy the RemoteInterface.cs file and create another file Client. cs inside that folder. Now paste the following code to that new file.

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
public class Client {
    public static void Main(string[] args) {
        TcpChannel chan = new TcpChannel();
        ChannelServices.RegisterChannel(chan);
        // Create an instance of the remote object
        RemoteInterface obj = (RemoteInterface) Activator.GetObject(
            typeof(RemoteInterface), "tcp://localhost:8888/cincom"); // localhost OR your server name
        if (obj.Equals(null)) {
            Console.WriteLine("Error: unable to locate server");
        } else {
            string strArgs;
            if (args.Length == 0) {
                strArgs = "Client";
            } else {
                strArgs = args[0];
            }
            obj.WriteName(strArgs);
            Console.WriteLine(obj.ReadWelcome());
        }
    }
}

Running Application

  • open the Visual Studio command prompt from Visual Studio tools.
  • change the current working directory to your remoting directory …/Remoting/
  • compile all the csharp files using csc *.cs
  • run the Server using start Server
  • change the current working directory to your client folder …/Client/
  • compile all the csharp files using csc *.cs
  • run the Client with a name (TestName) as an argument using Client TestName

You will get an output as

HI TestName. Welcome to Remoting World!!

Second Technique

This time we will follow the general .NET Remoting technique and we will use configuration files to set up our channels and formats. This time our .NET Remoting system will comprise of:

  • A removable object (Abstract class and implementation)
  • A Sever console application that will sit and listen for object requests
  • Configuration file for the Server
  • A Client console application that will make requests for the remote object
  • Configuration file for the Client.

The Remotable object

Create a new class library project called and add the following class (RemoteInterface) to the project and compile it. From this process, we will get RemoteClass.dll.

using System;
public class RemoteClass : MarshalByRefObject {
    private static string strWelcomeClient;
    public RemoteClass() {
        Console.WriteLine("Object created");
    }
    public string ReadWelcome() {
        return strWelcomeClient;
    }
    public void WriteName(string strNameFromClient) {
        strWelcomeClient = "HI " + strNameFromClient + ". Welcome to Remoting World!!";
    }
}

The Server

Create a console application project called Server. Add a reference to the RemoteClass.dll and add a class (Server) as below.

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
public class Server {
    public static void Main(string[] args) {
        RemotingConfiguration.Configure("Server.exe.config");
        Console.WriteLine("The Server is ready .... Press the enter key to exit...");
        Console.ReadLine();
    }
}

To configure through a configuration file, add an app. config file and add the following lines. As in the earlier case, we are using the SingleCall activation type.

<configuration>
    <system.runtime.remoting>
        <application>
            <service>
                <wellknown
                    mode="SingleCall"
                    type="RemoteClass, RemoteClass"
                    objectUri="RemoteClass.rem"
                />
            </service>
            <channels>
                <channel ref="http" port="8888"/>
            </channels>
        </application>
    </system.runtime.remoting>
</configuration>

The Client Application

Create another console application project called Client. Add a reference to the RemoteClass.dll and add a class (Client) as below.

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
public class Client {
    public static void Main(string[] args) {
        RemotingConfiguration.Configure("Client.exe.config");
        RemoteClass obj = new RemoteClass();
        if (obj.Equals(null)) {
            Console.WriteLine("Error: unable to locate server");
        } else {
            string strArgs;
            if (args.Length == 0) {
                strArgs = "Client";
            } else {
                strArgs = args[0];
            }
            obj.WriteName(strArgs);
            Console.WriteLine(obj.ReadWelcome());
            Console.ReadLine();
        }
    }
}

Add an app. config file to your project and add the following entries.

<configuration>  
          <system.runtime.remoting>  
      <application>  
        <client>  
            <wellknown  
              type="RemoteClass, RemoteClass"  
              url="http://localhost:8888/RemoteClass.rem"  
            />  
        </client>  
      </application>  
          </system.runtime.remoting>  
</configuration> 

If you are running on a different machine then, instead of localhost, use the name of the computer where the Server application is running.

Running Application

  • Run the Server application.
  • You will get a message as

The Server is ready .... Press the enter key to exit...

  • Run the Client application

You will get an output as

HI Client. Welcome to Remoting World!!