13

I want to share some information between two classes (A and B), which are running in different java programs. Instead of writing a whole communication protocol I want to use the java build-in rmi classes for that purpose. Currently class B is able to run a method which belongs to class A remotely. Is it somehow possible to use the same "connection" within class A to call a method of class B? Otherwise I probably have to implement a second rmi service ...

BR,

Markus

Markus
  • 457
  • 1
  • 10
  • 20

8 Answers8

12

If B implements Remote, it can be export and passed as a parameter in an RMI call to A. In this scenario, there's no need to register B in an RMI registry, since the client is being passed a reference to it explicitly.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • Mhhh, i think if I implement it that way there might occure some data inconsistencies between the "copied" and the "real" B, or? – Markus May 26 '09 at 18:28
  • 1
    No, there is no "copy". When A calls a method on B, it's really invoking a remote method call via a stub to the "real" B. It's identical to B invoking a remote method on A, except that instead of looking up the object in a registry, A received the remote object as a parameter in a method call. – erickson May 26 '09 at 18:37
  • However, if I'm not mistaken, this won't work when B cannot accept incoming connections, e.g. when it is on a consumer network with NAT. Is there a way to do callbacks over the already existing connection? – Bart van Heukelom Apr 09 '12 at 11:17
  • @BartvanHeukelom In very early versions of RMI, traffic from the server to the client could be multiplexed over the original connection from client to server. But this feature was deprecated and removed in 1.2.2. With RMI, the "client" callback is in fact a service, and the "server" can only invoke the callback after opening a socket back to the "client." – erickson Jan 20 '16 at 23:41
  • @erickson you mentioned "it can be exported". This means additional explicit code for exporting it, or there is some magic happening behind the scene ? If you have a good reference to read, please do share it. As a context: I am trying to implement the bidirectional communication in Spring env, but also trying to understand the process behind – Victor Mar 13 '19 at 09:51
  • @Victor the export of a result that implements `Remote` is automatic, “magic happening behind the scenes.” I will try to find a reference. – erickson Mar 15 '19 at 14:02
  • @erickson I have played a little more with the notions and did the magic! Anyway, thanks for the reply ! – Victor Mar 15 '19 at 14:50
6

I implemented 2 way RMI between cleint and server with server exposing its stub using Registry

  1. The client gets a stub of the server
  2. Then the client puts its stub as Observer to the server's addObserver method
  3. The server notifies the clients using this stub

The following code will gives a better idea

import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
import java.util.Observable;
import java.util.Observer;
import java.net.*;

import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;

interface ReceiveMessageInterface extends Remote
{
    /**
     * @param x
     * @throws RemoteException
     */
    void receiveMessage(String x) throws RemoteException;

    /**
     * @param observer
     * @throws RemoteException
     */
    void addObserver(Remote observer) throws RemoteException;
}

/**
 * 
 */
class RmiClient extends UnicastRemoteObject
{
    /**
     * @param args
     */
    static public void main(String args[])
    {
        ReceiveMessageInterface rmiServer;
        Registry registry;
        String serverAddress = args[0];
        String serverPort = args[1];
        String text = args[2];
        System.out.println("sending " + text + " to " + serverAddress + ":" + serverPort);
        try
        { // Get the server's stub
            registry = LocateRegistry.getRegistry(serverAddress, (new Integer(serverPort)).intValue());
            rmiServer = (ReceiveMessageInterface) (registry.lookup("rmiServer"));

            // RMI client will give a stub of itself to the server
            Remote aRemoteObj = (Remote) UnicastRemoteObject.exportObject(new RmiClient(), 0);
            rmiServer.addObserver(aRemoteObj);

            // call the remote method
            rmiServer.receiveMessage(text);
            // update method will be notified
        }
        catch (RemoteException e)
        {
            e.printStackTrace();
        }
        catch (NotBoundException e)
        {
            System.err.println(e);
        }
    }

    public void update(String a) throws RemoteException
    {
        // update should take some serializable object as param NOT Observable
        // and Object
        // Server callsbacks here
    }
}

/**
 * 
 */
class RmiServer extends Observable implements ReceiveMessageInterface
{
    String address;
    Registry registry;

    /**
     * {@inheritDoc}
     */
    public void receiveMessage(String x) throws RemoteException
    {
        System.out.println(x);
        setChanged();
        notifyObservers(x + "invoked me");
    }

    /**
     * {@inheritDoc}
     */
    public void addObserver(final Remote observer) throws RemoteException
    {
        // This is where you plug in client's stub
        super.addObserver(new Observer()
        {
            @Override
            public void update(Observable o,
                Object arg)
            {
                try
                {
                    ((RmiClient) observer).update((String) arg);
                }
                catch (RemoteException e)
                {

                }
            }
        });
    }

    /**
     * @throws RemoteException
     */
    public RmiServer() throws RemoteException
    {
        try
        {
            address = (InetAddress.getLocalHost()).toString();
        }
        catch (Exception e)
        {
            System.out.println("can't get inet address.");
        }
        int port = 3232;
        System.out.println("this address=" + address + ",port=" + port);
        try
        {
            registry = LocateRegistry.createRegistry(port);
            registry.rebind("rmiServer", this);
        }
        catch (RemoteException e)
        {
            System.out.println("remote exception" + e);
        }
    }

    /**
     * 
     * @param args
     */
    static public void main(String args[])
    {
        try
        {
            RmiServer server = new RmiServer();
        }
        catch (Exception e)
        {
            e.printStackTrace();
            System.exit(1);
        }
    }
}
Saurabh
  • 195
  • 1
  • 2
  • 7
1

It's been a while since I've used RMI, but IIRC if class B implements java.rmi.Remote and passes a reference to an instance of itself as a parameter to the method in class A, then class A should receive a stub and methods called on it will be called on the original instance.

However, if you have a lot of such RMI calls going back anf fro, you will probably encounter performance problems.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
1

If you pass B as an argument to a method in A and then use that reference to call a method on B I am fairly certain that a reverse connection is established, and I am fairly certain that RMI Registry is created for the JVM where B resides. At some point this got us into a bit of trouble with particularly strict firewall rules. Our code looked a little something like

Web Server

public int uploadFile(FileItem fileItem){
    return ApplicationClassLoader
        .get(DocumentManager.class)
        .attachFile(new RemoteInputStreamImpl(fileItem.getInputStream());
    )
}

Application Server

public int attachFile(RemoteInputStream in){
    ...

    byte[] buffer;
    while((buffer = in.read(1024)) != null) // Would return null to indicate EOF
      // Do some stuff

    return documentId;       
}
Steve Skrla
  • 1,620
  • 5
  • 16
  • 24
-1

RMI bidirectional:

SERVER:

import java.io.IOException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class RMISERVER {

   public RMISERVER() throws IOException {

       Thread t;
        try {
            t = new Prou_run();
            t.start();
        } catch (RemoteException ex) {
            Logger.getLogger(RMISERVER.class.getName()).log(Level.SEVERE, null, ex);
        }

   }


   public static void main(String args[]) throws IOException {
     new RMISERVER();
   }
}


import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.tree.DefaultMutableTreeNode;

//extends java.rmi.server.UnicastRemoteObject
public class Prou_run extends Thread implements Runnable{

            New_Object  root = null,root2=null,root3=null,root4=null,root5;
              New_Object new_root=null;
            Object xt = null, xt2=null , xt3=null;
            Registry r1,r2;
            RMIClientSocketFactory csf,csf2;
            RMIServerSocketFactory ssf,sf2;


            new_Patryk npal;

 public Prou_run() throws java.rmi.RemoteException, IOException
 {
         if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
                  }

//            csf = new RMIClientSocketFactory() {
//
//                public Socket createSocket(String host, int port) throws IOException {
//                    return new Socket("rmi://localhost/getchil",1080);
//                }
//            };
//            csf2 = new RMIClientSocketFactory() {
//
//                public Socket createSocket(String host, int port) throws IOException {
//                   return new Socket("rmi://localhost/getchild",1081);
//                }
//            };
//            ssf=new RMIServerSocketFactory() {
//
//                public ServerSocket createServerSocket(int port) throws IOException {
//                    return new ServerSocket(1099);
//                }
//            };// ssf.createServerSocket(1099);
//              sf2=new RMIServerSocketFactory() {
//
//                public ServerSocket createServerSocket(int port) throws IOException {
//                   return new ServerSocket(1098);
//                }
//            };//sf2.createServerSocket(1098);
       try {
         r1=java.rmi.registry.LocateRegistry.createRegistry(1098);
                 r2=java.rmi.registry.LocateRegistry.createRegistry(1099);//, csf2, ssf);
                 java.rmi.registry.LocateRegistry.createRegistry(1097);
                 java.rmi.registry.LocateRegistry.createRegistry(1095);
                 java.rmi.registry.LocateRegistry.createRegistry(1096);
         System.out.println("RMI registry ready.");
      } catch (Exception e) {
         System.out.println("Exception starting RMI registry:");
         e.printStackTrace();
      }
        this.xt = null;this.xt2 = null;this.xt3 = null;
        npal = new new_Patryk();
        System.out.println("sdmmmfxxxxxxxx");

   }






    public void run() {

//while(true){

      try{

//             root =  new_root;
//             xt=npal.getChild((File)new_root.getob(), (int)new_root.geti());
             New_ObjectIMPL sl = new New_ObjectIMPL();
             sl.i=354;
                System.out.println("sdmmmf2");
                //r2
             Naming.rebind("rmi://localhost:1099/getchild",(New_Object) sl);
                System.out.println("sdmmmf3");

         }catch (Exception e) {
       System.out.println("Trouble: " + e);
     }

        while(new_root==null){
            try{
                 //System.out.println("sdmmmf1"   +   new_root.geti());
         new_root = (New_Object) Naming.lookup("rmi://localhost:1080/getchil");
         System.out.println("sdmmmf1"   +   new_root.geti());
            }catch (Exception e) {
       System.out.println("Trouble: " + e);
     }
        }
    }

}

























/**
 *
 * @author austinchuma
 */
public interface New_Object extends java.rmi.Remote {

     public int geti() throws java.rmi.RemoteException;

     public Object getob() throws java.rmi.RemoteException;

     public Object getobchild() throws java.rmi.RemoteException;

      public boolean getbbol() throws java.rmi.RemoteException;

       public byte[] getb() throws java.rmi.RemoteException;



}





public class New_ObjectIMPL extends java.rmi.server.UnicastRemoteObject implements New_Object

{
   Object ob = null,obchild = null;
    int  i=0;
    boolean bol = false;
    byte[] b = null;

    public New_ObjectIMPL() throws RemoteException{
        ob = null;obchild = null;
        i=0;
        bol = false;
        b = null;
    }

    public int geti() throws RemoteException {
       return i;
           }

    public Object getob() throws RemoteException {

       return ob;
    }

    public Object getobchild() throws RemoteException {
       return obchild;
    }

    public boolean getbbol() throws RemoteException {
      return bol;
    }

    public byte[] getb() throws RemoteException {

        return b;

    }


}
user35443
  • 6,309
  • 12
  • 52
  • 75
-1

Both JVMs would need to implement RMI services. But that is really very easy look at the various classes in java.rmi.

What you can't do is somehow use one RMI connection and do two way communication.

Tom
  • 242
  • 2
  • 3
-1

Some RMI service support callbacks or listeners which allow the server to asynchronous call the client down the same connection. (Sorry I don't remember the name of the open libraries which do this, a quick google wasn't very helpful) Standard RMI doesn't support this, instead you need to expose the client as an RMI service as well.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
-2

How would class B know about class A without a second RMI server though? I think you are going to need two servers.

Gandalf
  • 9,648
  • 8
  • 53
  • 88