Archived

This forum has been archived. Please start a new discussion on GitHub.

Multiple Connections to different Server versions?

Hi,

I'm currently experiencing issues when trying to connect to different versions of the same server (Mumble) in the same process.

I want to automatically detect which server version I am talking to, and then create the appropriate handler class and work with the server. Unfortunately, when I try to load a slice for a new server object, I get uncontrollable side effects:
In [1]: from mumble.MumbleCtlIce import MumbleCtlIce

In [2]: mm118 = MumbleCtlIce( "Meta:tcp -h 127.0.0.1 -p 6512" )

In [3]: mm118.getRegisteredPlayers(1)
Out[3]:
{0: {'email': u'', 'userid': 0, 'pw': u'', 'name': u'SuperUser'},
 1: {'email': u'webmaster@localhost', 'userid': 1, 'pw': u'', 'name': u'Svedrin'}}

In [4]: mm120 = MumbleCtlIce( "Meta:tcp -h 127.0.0.1 -p 6522" )

In [5]: mm118.getRegisteredPlayers(1)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
AttributeError: 'ServerPrx' object has no attribute 'getRegisteredPlayers'

MumbleCtlIce tries to detect the version of Murmur which it is talking to, load the correct slice for it, and return a handler object.

mm118 is a handler for versions <= 1.1.8, which expose a method named getRegisteredPlayers. In 1.2.0, this method has been renamed to getRegisteredUsers, so the error message would be correct if I had been talking to a 1.2.0 server in line 5. But since I am still talking to a 1.1.8 server, this message is wrong. Simply loading the 1.2.0 slice renders the 1.1.8 object unusable.

Is there any way to prevent this?

The code I am currently using to load the slice is this:
connstring = "Meta:tcp -h 127.0.0.1 -p 6512"
slice = "/path/to/slice/file"
Ice.loadSlice( slice )
ice    = Ice.initialize()
# This allows me to at least run loadSlice multiple times, allowing at least the version detection to work.
Murmur = sys.modules.pop("Murmur")
prx    = ice.stringToProxy( connstring.encode("utf-8") )
meta   = Murmur.MetaPrx.checkedCast(prx)

murmurversion = meta.getVersion()
server = meta.getServer(1)

I need to find a way to load the new slice, without affecting any previously created meta or server instances, and without rendering meta.newServer() unusable as well.

Is that possible?

Thanks,
Svedrin

Comments

  • mes
    mes California
    Welcome to the forum.

    Whenever you request help, we ask that you also provide information about your environment, such as the versions of your operating system, Ice, compilers/interpreters, etc.

    Regarding your issue, removing the Murmur module from sys.modules is definitely not the right way to go about it. Attempting to load two incompatible versions of Slice definitions with the same names is not something we'd recommend or support. About the only option (aside from using the Dynamic Ice API, which is not fully implemented in Python yet) is to modify the packaging of the generated code to avoid conflicting definitions.

    For example, you can do something like this:
    // Slice - Murmur118.ice
    [["python:package:V118"]]
    module Murmur { ... };
    
    // Slice - Murmur120.ice
    [["python:package:V120"]]
    module Murmur { ... };
    
    In your Python script, you can load both Murmur118.ice and Murmur120.ice, but you'll have to prefix any references to Slice-generated types with the package, like this:
    import V118.Murmur
    proxy = V118.Murmur.ServerPrx.checkedCast(...)
    
    Note however that this strategy might still cause unexpected issues when receiving Slice user exceptions or objects by value, if their definitions differ between the two versions.

    You can find more information about Python packages here.

    Regards,
    Mark
  • Like you said, this works up to the point where I need to retrieve a Server object by value :(
    In [14]: mm118._getIceServerObject(1).__class__.__module__
    Out[14]: 'Murmur_v120.Murmur'
    

    The sys.modules hack was just something I tried out of desperation, and since what I'm attempting seems impossible I'll remove it. It would just have been a nice feature to automatically detect the server version, and actually being able to connect to different versions at the same time.

    Thanks anyway,
    Michael
  • matthew
    matthew NL, Canada
    It really sounds like the server slice wasn't versioned correctly... There are a few approaches to versioning outlined in the Ice manual, and in the Ice newsletter.
  • I was trying to do the very same as Svedrin described and also came up with the global meta package directive mentioned here as a possible solution.

    Unfortunately, as already noted here, this doesn't really work for objects requested from the server. Now I'm wondering about the underlying problem. I thought, judging from the documentation, the package directive would place modules in an independent namespace yet this doesn't seem to be the case. But if independent namespaces do not result in independence of the contents how does Ice handle namespace clashes in general?

    Or in other words: Is it impossible in Ice to have two modules with the same name and/or interfaces that are independent of each other and can be used at the same time?
  • mes
    mes California
    Hi Stefan,

    Welcome to the forum.

    The behavior is driven primarily by the Ice protocol, and more specifically by the information that the Ice protocol provides.

    When you transfer an Ice exception or object by value, what goes "over the wire" is a type ID such as "::Murmur::Server". The receiver has to translate that type ID into a local programming language construct that it can instantiate. In languages that support dynamic class loading, such as Java and C#, we convert the type ID into a class name and attempt to load it at run time. In the case of an abstract Slice class, we instead must rely on an application-supplied factory to provide the concrete instance.

    The Ice scripting language extensions don't perform any dynamic type resolution. Instead, the generated code directly associates a type ID with a language construct. This association is made when the generated code is loaded and therefore is stored in a global table. For this reason, attempting to load the generated code for two different versions of the same Slice type can have undefined (and typically unsatisfactory) results.

    There is a way to work around this for Slice classes. When Ice receives an object by value, the run time always checks first for an application-defined object factory. If no factory is provided, it assumes the Slice class is concrete and attempts to instantiate the equivalent language construct as usual. Object factories are registered on a per-communicator basis, therefore you could create a communicator for all interaction with version X of the Slice definitions, and another communicator for all interaction with version Y of the Slice definitions, then install object factories in each communicator that instantiate the appropriate version of the Slice classes.

    Unfortunately, this isn't possible for Slice exceptions because Ice has no concept (at the application API level) of a factory for user exceptions.

    Regards,
    Mark