Using multiple JnagSessions based on separatly compiled network interfaces.

  13 posts   Feedicon  
Replies: 12 - Last Post: July 23, 2010 01:30
by: Vincent Cantin
showing 1 - 13 of 13
 
Posted: May 28, 2010 14:53 by Mumrah
Hi,

I'm actualy working on a reddwarf game center. The user log in to redwarf, populate a list with available games and select the game he wants to play.

Our project structure is like this:

MainApp
---rds-client
---rds-server
---network-model
---ui

Game1
---network-model
---client-ui
---server-addon

Game2
---network-model
---client-ui
---server-addon

....

So what's the problem?
When a game is selected, the associated network-model needs to extend the MainApp's network-model (only with clientSession communication) but the root decoder aldready loaded is from MainApp and i can't add another one.

How could i generate a RootDecoder aware of others network-model projects in MainApp. Or if you've aldready crossed this kind of problem what's the direction you've chosen?

Thanks

 
Posted: May 28, 2010 18:18 by Vincent Cantin
Hello Mumrah,

Your usage of Jnag is pretty uncommon. Jnag compiles code because it supposes that it has all the information about the messages at the compile time, but it doesn't seem to be the case for you.

I see 2 possibilities to solve your problem:

1) Compile all at the same time.
Pros: no particular issue with Jnag.
Cons: no pluggability, which is not what you want.

2) Switch the JnagSessions at runtime once the player choose the game.

Once the player choose the game, it sends a message to the server, the server receives it, reply to say that it received it and approve the choice, then in the same task it is selecting a second JnagSession configured with the configuration object of the chosen game - you will have to root the incoming messages to this second session. Remember, you are the one to root the messages in and out, not Jnag, you have a perfect control over it.

When the client receives the confirmation from the server, the client is doing the same.

Pros: no particular issue with Jnag, and you can compile your projects separatly.
Cons: you can't have 2 different JnagSessions on the same communication channel at the same time. This may not be a problem for you, you still can open an additional channel just between your client and the server if you need to have the 2 types of JnagSessions communicating at the same time.

Did it answer your question ?
 
Posted: June 01, 2010 11:20 by Mumrah
Hi Vincent,

It's a great step forward but not enough for my scenario cause i don't want to rewrite
functions provided by the main application.

I'm actually trying to solve this by developping some kind of jnag-addon.
This addon will negociate with the server an id (byte) for each new JnagSession on the same Channel or ClientSession.
Then for each message the identifier will be inserted as first byte of the ByteBuffer.
The receiver will read the identifier and redirect the remaining bytes to the right JnagSession.

Like this i can use Jnag without modifying the code for a 1 byte/message cost

I hope i could give news about this soon.

EDIT: Ouch!! the server side will be more difficult to adapt...
 
Posted: June 01, 2010 22:34 by Vincent Cantin
So if I understand well, you are implementing a small message dispatcher between the main client-server channel and a few instances of the JnagSession. That sounds correct to me so far, and you don't have to modify the implementation of Jnag to reach your goal.

Note that what you are doing looks similar to what an implementation of Red Dwarf would do to encode data from different channels into 1 socket. I am wondering if you are not re-inventing the wheel here.
 
Posted: June 07, 2010 15:13 by Mumrah
Hi,

You are right, i'm re-inventing the wheel. So i've changed my mind to something more simple. Using channels like you said in your previous post but still without switching JnagSession. In fact on the client side multiple JnagSession is not a problem. The problem is on the server

I've created 3 classes

NamedJnagSessionImpl extends JnagSessionImpl
NamedProxyFactoryImpl extends ProxyFactoryImpl
NamedSharedIdObjectBinding extends SharedIdObjectBinding

NamedJnagSessionImpl class take a String parameter named sessionId and an AbstractConfiguration (like the client side)
This sessionId is propagated to NamedProxyFactoryImpl and NamedSharedIdObjectBinding
Each binding is done using the sessionId. No static initialization needed anymore

Like this i can use multiple JnagSession on the server side. Do you see any problem with this?

Changes i've made in jnag-rds :

JnagSessionImpl
protected sharedIdStoreRef
protected sharedIdObjectBinding
protected proxyFactory

ProxyFactoryImpl
protected remoteClassNameToProxyClassRef

SharedIdObjectBinding
protected idToObjectRef
protected objectRefToId

Could you change your repository with those protected fields? If you can't, could you create an interface for
JnagSessionImpl and use it instead of JnagSessionImpl (Ex: RootDecoder.decodeAndInvoke)?

Mumrah
 
Posted: June 10, 2010 10:59 by Vincent Cantin
I've changed my mind to something more simple. Using channels like you said in your previous post but still without switching JnagSession. In fact on the client side multiple JnagSession is not a problem. The problem is on the server.

Hi,

I don't see how using multiple sessions could be a problem on the server. Can you explain more about the problem ?
 
Posted: June 10, 2010 13:15 by Mumrah
Hello,

In fact using multiple instances of JnagSession is not a problem but using multiple instances of JnagSession with multiple AbstractConfiguration is my problem.

The problem lies in JnagSessionImpl.initializeGlobalObjects(AbstractConfiguration configuration).
When this static function is used, it initialize the JnagSessionImpl once for all instances.
If i need to load a second AbstractConfiguration i will erase the previous one.

So i've chosen to give an id to the created JnagSessionImpl, like this i can use multiple AbstractConfiguration.

Maybe some code will be better than an english explanation from a french guy.

!!Do not forget that this code use protected fields (see previous post)!!

NamedJnagSessionImpl
package com.lemoulinstudio.jnag.rds;

import java.io.Serializable;
import java.util.logging.Logger;

import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.ManagedReference;
import com.sun.sgs.app.NameNotBoundException;

public class NamedJnagSessionImpl extends JnagSessionImpl implements Serializable {

  private static final long serialVersionUID = 1L;
 
  public static Logger logger = Logger.getLogger(JnagSession.class.getName());
  private static String sharedIdStoreBindingName = "NamedJnagSharedIdStore";
  private static String configBindingName = "NamedJnagConfig";

  private ManagedReference<LocalIdStore> getSharedIdStoreRef(String sessionId) {
	String bindingName = sharedIdStoreBindingName + "_" + sessionId;
    try {
		return AppContext.getDataManager().createReference((LocalIdStore)
		        AppContext.getDataManager().getBinding(bindingName));
	} catch (NameNotBoundException e) {
		AppContext.getDataManager().setBinding(bindingName, new SharedIdStore());
		return AppContext.getDataManager().createReference((LocalIdStore)
		        AppContext.getDataManager().getBinding(bindingName));
	}
  }

  private AbstractConfiguration getConfiguration(String sessionId, AbstractConfiguration configuration) {
    String bindingName = configBindingName + "_" + sessionId;
    try {
		return (AbstractConfiguration)AppContext.getDataManager().getBinding(bindingName);
	} catch (NameNotBoundException e) {
		AppContext.getDataManager().setBinding(bindingName, configuration);
		return (AbstractConfiguration)AppContext.getDataManager().getBinding(bindingName);
	}
  }


  /**
   * Creates a Jnag session.
   */
  public NamedJnagSessionImpl(String sessionId, AbstractConfiguration configuration) {
    sharedIdStoreRef = getSharedIdStoreRef(sessionId);
    sharedIdObjectBinding = new NamedSharedIdObjectBinding(sessionId);
    
    proxyFactory = new NamedProxyFactoryImpl(sessionId, configuration);
    rootDecoder = getConfiguration(sessionId, configuration).getRootDecoder();
  }
}




NamedProxyFactoryImpl
package com.lemoulinstudio.jnag.rds;

import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.ManagedReference;
import com.sun.sgs.app.NameNotBoundException;
import com.sun.sgs.app.util.ScalableHashMap;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.lemoulinstudio.jnag.common.Remote;
import java.lang.reflect.Constructor;
import java.util.Map;


class NamedProxyFactoryImpl extends ProxyFactoryImpl implements Serializable {

  private static final long serialVersionUID = 1L;

  private static String remoteClassNameToProxyClassBindingName = "NamedJnagRemoteClassNameToProxyClass";

  private ManagedReference<ScalableHashMap<String, Class>> getRemoteClassToProxy(String id, AbstractConfiguration configuration)
  {
	  String bindingName = remoteClassNameToProxyClassBindingName + "_" + id;
	  try {
		return AppContext.getDataManager().createReference(
		            (ScalableHashMap<String, Class>)
		            AppContext.getDataManager().getBinding(bindingName));
	} catch (NameNotBoundException e) {
		ScalableHashMap<String, Class> remoteClassNameToProxyClass = new ScalableHashMap<String, Class>();
	    for (Map.Entry<Class<? extends Remote>, Class> entry : configuration.getRemoteClassToProxyClass().entrySet())
	      remoteClassNameToProxyClass.put(entry.getKey().getName(), entry.getValue());

	    AppContext.getDataManager().setBinding(bindingName, remoteClassNameToProxyClass);
	    
	    return AppContext.getDataManager().createReference(
	            (ScalableHashMap<String, Class>)
	            AppContext.getDataManager().getBinding(bindingName));
	}
  }

  private String sessionId;
  
  public NamedProxyFactoryImpl(String sessionId,AbstractConfiguration configuration) {
	 
	this.sessionId = sessionId;
	Logger.getLogger(NamedProxyFactoryImpl.class.getName()).log(Level.SEVERE, "Named :"+sessionId);
    remoteClassNameToProxyClassRef = getRemoteClassToProxy(sessionId, configuration);
  }

@Override
public String toString() {
	return "NamedProxyFactoryImpl [sessionId=" + sessionId + "]";
}
  
}



NamedSharedIdObjectBinding
package com.lemoulinstudio.jnag.rds;

import com.lemoulinstudio.jnag.common.BindToSharedId;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.ManagedReference;
import com.sun.sgs.app.NameNotBoundException;
import com.sun.sgs.app.util.ScalableHashMap;
import java.io.Serializable;
import java.util.Map;

/**
 *
 * @author Vincent Cantin
 */
class NamedSharedIdObjectBinding extends SharedIdObjectBinding implements Serializable {

  private static final long serialVersionUID = 1L; 

  private static String idToObjectRefBindingName = "NamedJnagIdToObjectRef";
  private static String objectRefToIdBindingName = "NamedJnagObjectRefToId";
  
  private ManagedReference<Map<Id, ManagedReference<? extends BindToSharedId>>> getIdToObjectRef(String sessionId) {
	  String bindingName = idToObjectRefBindingName + "_" + sessionId;
    try {
		return AppContext.getDataManager().createReference((Map<Id, ManagedReference<? extends BindToSharedId>>)
		        AppContext.getDataManager().getBinding(bindingName));
	} catch (NameNotBoundException e) {
		AppContext.getDataManager().setBinding(bindingName, new ScalableHashMap<Id, ManagedReference<? extends BindToSharedId>>());
		return AppContext.getDataManager().createReference((Map<Id, ManagedReference<? extends BindToSharedId>>)
		        AppContext.getDataManager().getBinding(bindingName));
	}
  }

  private ManagedReference<Map<ManagedReference<? extends BindToSharedId>, Id>> getObjectRefToId(String sessionId) {
	  String bindingName = objectRefToIdBindingName + "_" + sessionId;
	  try {
			return AppContext.getDataManager().createReference((Map<ManagedReference<? extends BindToSharedId>, Id>)
			        AppContext.getDataManager().getBinding(bindingName));
		} catch (NameNotBoundException e) {
			AppContext.getDataManager().setBinding(bindingName, new ScalableHashMap<ManagedReference<? extends BindToSharedId>, Id>());
			return AppContext.getDataManager().createReference((Map<ManagedReference<? extends BindToSharedId>, Id>)
			        AppContext.getDataManager().getBinding(bindingName));
		}
  }

  private String sessionId;
  
  public NamedSharedIdObjectBinding(String sessionId) {
	this.sessionId = sessionId;
    idToObjectRef = getIdToObjectRef(sessionId);
    objectRefToId = getObjectRefToId(sessionId);
  }

}

 
Posted: June 10, 2010 15:03 by Vincent Cantin
Your english is good enough, and the verbose code was not necessary here (I wanted to know the problem, not the fix). I will take a look at the static code of the rds (Red Dwarf Server) module and see what I can do.
 
Posted: June 13, 2010 05:39 by Vincent Cantin
I updated the source code of the rds module on the mercurial repository. The changeset enable you to use different configurations at the same time. However, I didn't see the need to change my class "SharedIdObjectBinding", please correct me if I am wrong. I also didn't test the changeset deeply, so if you find out that something doesn't work as you expected, let me know.
 
Posted: June 18, 2010 11:40 by Vincent Cantin
Allo ya quelqu'un ? Ca serait bien d'avoir du feedback de la part des personnes qui demandent des ameliorations, une fois l'amelioration faite.

That would be nice to have feedback, specially after changes are done, from the persons who requested the changes.
 
Posted: July 13, 2010 08:45 by Mumrah
Sorry i was on holiday,

I will get the code and give it a try

Thanks for your efforts
 
Posted: July 19, 2010 10:28 by Mumrah
Hi,

I've tested it and it works fine

Thanks again
 
Posted: July 23, 2010 01:30 by Vincent Cantin
You're welcome.
showing 1 - 13 of 13
Replies: 12 - Last Post: July 23, 2010 01:30
by: Vincent Cantin
  • Mysql
  • Glassfish
  • Jruby
  • Rails
  • Nblogo
Terms of Use; Privacy Policy;
© 2010, Oracle Corporation and/or its affiliates
(revision 20120518.3c65429)
 
 
Close
loading
Please Confirm
Close