Blog Projects
Student in IT & Web develop{design}er
Distributed Objects with Java & Cocoa

Distributed Objects with Java & Cocoa

RMI vs NSConnection

I’m currently learning interesting things about distributed programming in Java. Because I’m not a Java fan, I tried [and succeeded] to redo a simple RMI example from Java to Cocoa in Objective-C.

Remote method invocation

Before showing you how an application can communicate with another one on a network in Objective-C, I’ll explain you a bit how things work, and how we do it in Java to see the differences between the two languages.


Principle

RMI is a java API that let you call methods on an object which is not on the same computer. So for an easy example, let see the communication between a server application and a client application.

So the server just need to vend an object, so the client can reach a pointer to this object and call methods on it; but to do so, the client need to know the interface of this object.


Java RMI

Before programming the server, we need to create an interface (here for a Person object) and define it on the server side.

So we create a new file «PersonStructure.java» in a new java project and put this code to define the interface:

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

public interface PersonStructure extends Remote{
	public String getName() throws RemoteException;
	public void setName(String name) throws RemoteException;
}

Here our interface need to extend Remote, if not, the object can’t be vend.

Now we need to define a Person class that will implements our interface, so let’s do it be creating a new file Person.java with this code:

public class Person extends UnicastRemoteObject implements PersonStructure {
	private String name;
	private static final long serialVersionUID = 1L;
	
	protected Person(String name) throws RemoteException {
		super();
		this.name = name;
	}

	@Override
	public String getName() throws RemoteException {
		return this.name;
	}

	@Override
	public void setName(String name) throws RemoteException {
		System.out.println("the new name is = " + name);
		this.name = name;
	}
}

You can see here that our class Person need to extend UnicastRemoteObject to be shared by a server...

Now we can do our server that will vend the Person object. Create a «Server.java» file with this:

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class Server {
	public static void main(String[] args) {
		try {
			//Start a new RmiRegistry 
			LocateRegistry.createRegistry(1099);

			Person person = new Person("Doe");
			
			//We share our person
			Naming.bind("///person_server", person);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Now here we have our server sharing the person object on the localhost (the 3 slash in the bind). It’s in this bind that you can specify a hostname or an ip address (i.e. "//localhost/server_name" or "//127.0.0.1/server_name").

To have a simple example we stay in the same project to create our client. So create «Client.java» with a main method:

import java.rmi.Naming;

public class Client {
	public static void main(String[] args) {
		try {
			PersonStructure obj = (PersonStructure) Naming.lookup("person_server");
			System.out.println("Name of the person shared by the server = "+ obj.getName());
			obj.setName("Steve");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Now you can run the project with the main method of the Server class and after that run the same project but with the main method of the Client Class; and it’s Magic! the client can get and set the person’s name which is on the server.


Objective-C & Distributed Objects

Now we can see how we do the same example but with Cocoa applications. So in Xcode create a new Cocoa Application and name it Server.

Create a new file (cmd+N), choose the Objective-C protocol template and name it PersonStructure. Now we define the structure of a person with this:

@protocol PersonStructure < NSObject >

- (NSString *)getName;
- (void)setName:(NSString *)name;

@end

Now we can define our Person class (create a new class which will subclass NSObject):

In Person.h:

#import "PersonStructure.h"

@interface Person : NSObject < PersonStructure > {
    NSString *_name;
}

- (id)initWithName:(NSString *)name;
 
@end

In Person.m:

@implementation Person

- (id)initWithName:(NSString *)name
{
    self = [super init];
    if(self)
    {
        _name = [name retain];
    }
    
    return self;
}

- (NSString *)getName
{
    return _name;
}

- (void)setName:(NSString *)name
{
	NSLog("the new name is = %@", name);
    [_name release];
    _name = [name retain];
}

- (void)dealloc
{
    [_name release];
    [super dealloc];
}

@end

You can see here that I’m managing memory by myself with the retains, releases and deallocs, but it’s not necessary if your project use the automatic reference counting (ARC).

Know we just need to start our server in the AppDelegate:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Create the Person to vend
    Person *person = [[Person alloc] initWithName:@"Doe"];
    
    NSConnection *serverConnection = [NSConnection serviceConnectionWithName:@"person_server" rootObject:person];
    
    //Start the server
    [serverConnection runInNewThread];
}

Our server is done. Now we need to create a new project Client. In this project we have to drag and drop the PersonStructure. h file from the server project.

Finally we can access the person object from the server application by adding this code in the Client’s AppDelegate:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    //Until we find the server
    NSDistantObject *serverObject = nil;
    while (serverObject == nil)
    {
        serverObject = [[NSConnection rootProxyForConnectionWithRegisteredName:@"person_server" host:nil] retain];
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.]];
    }
    
    //We cast the remote object in the interface we know 
    id< PersonStructure > remoteObject = (id< PersonStructure >)serverObject;

    //we use our remote object
    NSLog(@"Person's name on server = %@", [remoteObject getName]);
    
    [remoteObject setName:@"Steve"];
}

And we are done! You can now run the server and the client, and you’ll see that the client can communicate with our server.


Conclusion

Distributed Objects is really an interesting technique to know. This comparison between Java and Objective-C confirm (for me =D) that Objective-C still more elegant and simpler than Java. (i.e. In java the distributed object as to extends Remote and UnicastRemoteObject than in Objective-C every object can be a distributed object).

Alix

Alix

October 22, 2011 at 3:07 pm

C'est dans votre cours de systeme distribué?
Alix

Alix

October 22, 2011 at 3:07 pm

PS: Y'a un bug, tu peux pas mettre d'espace dans "Full name"
Damien Legrand

Damien Legrand

October 22, 2011 at 5:58 pm

Salut,
La partie Java fait partie du cours de système distribué, mais c'est un peu simplifié ici.

(*Bug corrigé merci)


Add a Comment

Add a Comment
Full Name
Email
Message