[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[news.eclipse.technology.ecf] Re: How do SharedObjects become known on the remote systems

Hi John,

John F. Patterson wrote:
In this analysis, I assume I send to Li-Te by performing a send on his chat object and he sends to me by performing a send on my chat object. Is this correct? Li-Te seemed to believe a connector would be needed, but that does not appear to be correct. Am I right on this?

A connector is not needed. Because (in this implementation) the Chat instance ID (called 'chat' in this case) is named the same on all participating processes (it's 'well-known'/assumed for this chat), messages send by any Chat instance via:


config.getContext().sendMessage(null, <whatever>)

<whatever> will be directed automatically to all remote instances of the Chat object with the name 'chat' (but not to other named shared objects).

Note the 'null' in the above sendMessage means 'send to all remotes with ID 'chat' that exist within this container'. It is, of course, possible that sendMessage would be used under other app circumstances (e.g. 1-1 conversation) as follows:

config.getContext().sendMessage(specificID, <whatever>)


How does a remote system come to "know" about SharedObjects created by someone else? I assume the SharedObject is not automatically created.

In the implementation below, the shared object 'chat' is assumed to be there (because of the assumed name 'chat').


In general, however, a shared object 'primary copy' needs to be able to dynamically create/destroy/control remote replicas of itself.

That's the purpose of

config.getContext().sendCreate(...);
config.getContext().sendDispose(...);

Do I learn of the ID in a listener on the Container?

You can...if it's not well known, yes. Actually, I suspect that these sorts of notification (of ID arrival/departure) are going to be so commonly needed that I've been assuming that most providers will want to *by default* (without any explicit 'addlistener' additions by the shared object) send some events via handleEvent(...) that notify of the arrival/departure of sharedobjects and of group members. So in the provider I'm implementing, for example, there will be events delivered to the sharedobject (via handleEvents(...) that indicate that a new sharedobject created/disposed and that a group member arrived/departed. I'm not sure whether these notifications should be required of all providers, however...it's probably something that should be left up to the provider (whether such notifications should be automatically delivered to a sharedobject, or whether explicit registration is required to receive such events).


Will I learn of the type as well, so that I will know what sort of object needs to be created?

The SharedObjectDescription passed into the sendCreate(...) call contains all the information necessary to create a remote replica of the given shared object (it's 'root' classname, it's id, homeid, any constructor params, and it's properties).


Incidently...there is some trickiness associated with classloaders necessary (caused by OSGI 'classloader-per-plugin' model in Eclipse 3.0) to be able to have the provider code that receives a sendCreate message be able to 'see' the class that it's trying to load...if it's in a plugin other than the provider plugin. The class specified in the SharedObjectDescription is a String, and so the provider classloader needs to call:

Class.forName(description.getClassName())
(or classloader.loadClass(...))

to load the class before creating the instance.

Happily, the OSGI model allows for specifying 'dynamic-importpackages' so that a given provider plugin can access packages/classes defined by other plugins.

Thanks,

Scott



jfp

Scott Lewis wrote:

Hi Li-Te and all,

Here are some comments on Li-Te's chat application.

Li-Te wrote:

(note: newsgroup is acting funny .... seeing double posts. so apologies if this appears twice, or if someone already answered John's question)

Here is my rough understanding of this from what I've read so far in the javadoc and online documentation. Someone say something in if I'm wrong here. Putting in "???" in unknown bits.

--- for user 1

// create a chat container - ignore what kind of protocols, etc for now
ISharedObjectContainer chatLobby =
  SharedObjectContainerFactory.
      makeSharedObjectContainer("chatLobby");


Yes.  This is the first step...to create a container for the chat.

In preference to all of the code you have below, I would design things so that the program would be structured as follows:

// join the group...this connects to some server...using any/all
// authentication required by 'chatLobby'
chatLobby.joinGroup(IDFactory.makeStringID("slewis@xxxxxxxxxx",null);

// Create an ISharedObject that represents me...i.e. has my nickname
Chat chat = new Chat("slewis");

// And add the shared object to the container...this will init the Chat
// instance and allow it to start sending/receiving messages
chatLobby.getSharedObjectManager().addSharedObject(IDFactory.makeStringID("chat"),chat,new Properties(),null);


For a simple chat, this would be all I would need. All the functionality of sending/receiving messages would be implemented in the Chat class. I've attached a copy of the source to this class in this email, and I'll also be checking it in to the org.eclipse.ecf.test plugin later today.

User two would have *exactly the same code* except for

"slewis@xxxxxxxxxx" would be changed to "li-te_cheng@xxxxxxxxxx"
and new Chat("slewis") would be changed to "li-te". The Chat object code itself would also be the same on both/all participants.


There would be other ways to implement this functionality, but I think this is the most straightforward. The basic notion is that the ISharedObject is really responsible for implementing all of the 'chat' functionality (or presence functionality, or file sharing functionality, etc).

Some more comments are interspersed below:

<code deleted>

--- for user 2

// similar process like user 1 (log into a chat lobby) and
// then in the chatLobby listener defined by calling
// chatLobby.addListener(), check if an invitation message
// came through.  the invite should hold chatSessionId so
// user 2 can do something like this

private void joinSession( ID chatSessionId )
{
   ISharedObjectContainer chatSession =
    SharedObjectContainerFactory.
      makeSharedObjectContainer("chatSession");

   // e.g. join info could might be a password if the chat room is
   // protected
   Object    joinInfo = ...; // ???
   chatSession.joinGroup(chatSessionId,joinInfo);
}


Several notes:

In this setup, there is a container for a "lobby", the public space to discover other members. The lobby is where people can send invites, and find out who's online (e.g. buddy list). Shared objects in the lobby are really tokens for user presence : if user1's object is in the lobby, then user1 is "online". Login is done via the join() method. Listing who's online is done by listing what object IDs are in the "lobby" container (after join() is called).


Yes.


The lobby needs a unique ID for all users to connect to. This is done by defining an ID via namespaces. IDFactory.getNamespace(...) would define an appropriate namespace for IDFactory.makeID(...)


Yes.


To start a chat, user1 creates another container for the 1-to-1 chat session. join() here denotes creating a session for 1-to-1 access, so a lot of this hinges upon the Object[] parameter in join()... this doesn't feel quite right to me.


As my example above shows, this isn't strictly necessary. There's no reason to have the container create a new container for this simple chat case. To implement a 'multiple chat groups with a lobby chat' sort of approach it might be helpful to define new containers to represent other chat groups, but it's not necessary for this simple chat


To invite user2, user1 sends a message to user2 in the lobby container via a ISharedObjectContext... not sure how to get one of these context objects. I don't see a ISharedObject.getContext() method.


It's on the ISharedObjectConfig that is passed to the shared object via the 'init' method. ISharedObjectConfig.getContext(). This is explicitly modelled after the ServletConfig/ServletContext relationship.

/And

ISharedContainerObject.getConfig() doesn't return a ISharedObjectConfig which has the needed getContext() method.


No, the ISharedObjectContainer.getConfig() is the config associated with the container (not the config associated with the ISharedObject).


user2 would have to set up a listener in the lobby container for invitation message types, and join the 1-to-1 chat session container by calling join(). The ID of the chat session must be passed along in the invitation message.


Does this sound right?


Well yes, except that I would put most of what you are describing for invitation messages and etc in the implementation of the Chat class. The chat class I've provided simply sends around nickname/message pairs...but an invitation message could easily also be defined/responded to within the Chat class itself as you describe.

Hopefully this helps.

Scott


Li-Te


John F. Patterson wrote:

It would help me if we identified a few use cases and worked them through using the API. The simplest is probably starting a chat. We can worry later about actually using the chat. For now, I just want to understand how it all gets started.

Normally, I think of chat as having three main steps to get it going:

  1. One user initiates a chat session.
  2. Somehow another user becomes informed of the existence of the chat
     (the invite)
  3. The second user joins the chat.

For step 1), I assume that I create a SharedContainer to correspond to the chat session. Then I create a SharedObject that will be used to accomplish the actual communication.
For step 2), I assume I must send the ID of the SharedContainer to the other user. I don't believe the ECF provides an explicit way to do this, but one could be invented on top of it.
For step 3), I assume the second user creates a SharedContainer of the appropriate type (how do they know the type?) and Join the group identified by the ID sent to them. Then somehow, they find the SharedObject.


Do I have this right, as far as it goes? As you can see, I am a little unclear on some of the specifics, particularly on how the SharedObjects come to exist on the second machine. Are they brought into existence upon joining the group? Does the second user learn about them via an event?

I would find it helpful to see the code for this use case.


------------------------------------------------------------------------

package org.eclipse.ecf.test.ui.actions;

import java.io.IOException;

import org.eclipse.ecf.core.ISharedObject;
import org.eclipse.ecf.core.ISharedObjectConfig;
import org.eclipse.ecf.core.ISharedObjectContext;
import org.eclipse.ecf.core.SharedObjectInitException;
import org.eclipse.ecf.core.events.RemoteSharedObjectEvent;
import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.core.util.Event;

public class Chat implements ISharedObject {

   public Chat(String nickname) {
       super();
   }

String nickname;
ISharedObjectConfig config;
public void init(ISharedObjectConfig initData)
throws SharedObjectInitException {
this.config = initData;
}


public ID getID() {
if (config == null) return null;
else return config.getSharedObjectID();
}
public void handleEvent(Event event) {
// This is the code to handle a
if (event instanceof RemoteSharedObjectEvent) {
// We've got an instance of event sent by remote
// We'll assume it's a chat message
RemoteSharedObjectEvent chatEvent = (RemoteSharedObjectEvent) event;
handleChatEvent(chatEvent);
} else System.out.println("ID: "+getID().getName()+" got unknown event: "+event);
}
protected void handleChatEvent(RemoteSharedObjectEvent chatEvent) {
// It's actually an object array...see sendChatToRemote
Object [] data = (Object []) chatEvent.getData();
showChatToUI((String) data[0], (String) data[1]);
}
protected void showChatToUI(String senderNickname, String message) {
System.out.println("Received chat message from: "+senderNickname);
System.out.println("Message: "+message);
}


   // This is method to call to send message to remotes
   protected void sendChatToRemote(String message) {
       ISharedObjectContext ctx = config.getContext();
       Object [] data = new Object[] { nickname, message };
       try {
           // Note that null means to *all* remotes in current container
           ctx.sendMessage(null,data);
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
   public void handleEvents(Event[] events) {
       for(int i=0; i < events.length; i++) {
           handleEvent(events[i]);
       }
   }

   public void dispose(ID containerID) {
       System.out.println("ID:"+getID().getName()+" dispose("+containerID.getName()+")");
       config = null;
   }

   public Object getAdapter(Class clazz) {
       System.out.println("ID:"+getID().getName()+" getAdapter("+clazz.getName()+")");
       return null;
   }

}