Expand my Community achievements bar.

Dealing with missing room structure (eg Nodes)

Avatar

Former Community Member

Hi,

I'm sure most developers have seen this error pop up during development:

"Error - insufficient permissions to create a new CollectionNode. You must be an OWNER of the room to add new multi-user features to it. Log in with developer credentials in order to do so."

This error occurs when a non-owner subscribes to an object that doesn't exist (there's an implicit attempt to create it, which fails).  As far as I can tell, this condition cannot be detected cleanly by a client application -- you're just supposed to make sure it doesn't happen on a released application.

If that's correct so far, then the design seems to assume that a given collaborating application connects to a specific well-known room (or at least, the template for the room is known).  What if the Flex code providing collaboration support is part of a library, and it doesn't know for sure that the provided room has the proper structure?  The goal here is to detect this situation and handle it within the library, without the error above happening.


BTW CollectionNode.canUserSubscribe doesn't seem to help out here.  It seems to return false unless you're already subscribed!

DB

4 Replies

Avatar

Employee

Hi DB,

I see the error thrown to be perfectly reasonable. As you said this error occurs when a non-owner subscribes to an object that doesn't  exist, and attempts to create it. The app would be inconsistent if the error is handled, and yes the assumption is that clients connect to a specific well-known room ie setup.

Also if the app were to handle the issue, then the notion of permissions on the CollectionNode is lost (This would allow everyone joining the room to create a CollectionNode)

But we can always hacks around the situation. You can handle the error, and notify the owner to create the node for you and you subscribe again. Or if you are using Server side API's you can always ask your server to create one and then you can subscribe again.

Thanks

Arun

Avatar

Former Community Member

Thanks for the reply.  By "handle", I simply meant I would like to detect that a ConnectionNode of a given name does not exist, and hence not attempt to subscribe to it (since subscribing causes the error above).  It would not be a problem that there's no synchronization for that CollectionNode.  It does not imply that the user should be able to create the CollectionNode.

I realize this is slightly outside the common use case.  As I said, my code in in a library, and I can't guarantee that the calling application has configured the room correctly.  If the room is not correctly configured, I don't want my code to simply blow up with the exception in my original post (which cannot be caught).

You implied that I can handle the error.  That's really the question: how?  I'd like to either avoid causing the error, or handle it when it occurs.

DB

Avatar

Employee

Hi DB,

Sorry I misunderstood.

I have attached a code sample to see how you could handle the issue. The key here is to subscribe to your collection after your room is synchronized. Check the buildmodel() method.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" x="0" width="400" height="400"
                    xmlns:rtc="AfcsNameSpace" creationComplete="init(event);" xmlns:session="com.adobe.rtc.session.*">
     <mx:Script>
          <![CDATA[
              
              
               import com.adobe.rtc.authentication.AdobeHSAuthenticator;
               import com.adobe.rtc.events.AuthenticationEvent;
               import com.adobe.rtc.events.ChatEvent;
               import com.adobe.rtc.events.SessionEvent;
               import com.adobe.rtc.session.ConnectSessionContainer;
               import com.adobe.rtc.sharedModel.SimpleChatModel;
               import com.adobe.rtc.sharedModel.descriptors.ChatMessageDescriptor;
              
               import flash.events.*;
               import flash.ui.Keyboard;
              
               import mx.controls.Alert;
              
               // this simple example just shows how this shared model can be made easily binable for MXML.
               // See SimpleChatModel for details
              
               [Bindable]
               public var _auth:AdobeHSAuthenticator = new AdobeHSAuthenticator();
              
               [Bindable]
               public var simpleChatModel:SimpleChatModel;
              
              
              
               [Bindable]
               private var _cmd:ChatMessageDescriptor;
              
              
              
               protected function init(p_evt:Event):void
               {
                    _auth.userName = "Guest";
                    _auth.password = null;
                   
                    _auth.addEventListener(AuthenticationEvent.AUTHENTICATION_FAILURE, onAuthenticationResponse);
                    _auth.addEventListener(AuthenticationEvent.AUTHENTICATION_SUCCESS, onAuthenticationResponse);
                   
                    cSession.addEventListener(SessionEvent.SYNCHRONIZATION_CHANGE, onSessionEventResponse);
                    cSession.addEventListener(SessionEvent.ERROR, onSessionEventResponse);
                    cSession.login();
                   
               }
              
              
               public function onAuthenticationResponse(event:AuthenticationEvent):void {
                    if (event.type == AuthenticationEvent.AUTHENTICATION_SUCCESS) {
                         trace("Authentication Succeeded");
                         //buildModel();
                    }
                    else if (event.type == AuthenticationEvent.AUTHENTICATION_FAILURE) {
                         Alert.show("Authentication Error : " + event.toString());
                    }
               }
              
              
              
              
              
              
              
               public function onSessionEventResponse(event:Event):void {
                    if (event.type == SessionEvent.SYNCHRONIZATION_CHANGE) {
                         if (cSession.isSynchronized) {
                              //Now we are connected and the Pods have synchronized themselves, so switch to main Screen
                              //Switch to Collaborative Pods i.e. ConnectSessionContainer
                              //vsMain.selectedIndex = 1;
                              buildModel();
                         }
                         else {
                              //We are disconnected now
                              cSession.roomURL = null;
                              //vsMain.selectedIndex = 0;
                         }
                    }
                    else if (event.type == SessionEvent.ERROR) {
                         var sError:SessionEvent = event as SessionEvent;
                         Alert.show(sError.error.name + " : " + sError.error.message);
                    }
               }
              
              
              
              
              
               private function buildModel():void
               {
                   
                    // Create the model: just calling the constructor won't create the collection node or pass the messages.
                    // Call subscribe and five it a shared ID while creating the model.
                    // The shared ID becomes the name of the collection node.
                    try {
                         simpleChatModel = new SimpleChatModel(true);
                         simpleChatModel.sharedID = "simpleChatModel1";
                         simpleChatModel.subscribe();
                    } catch(e:Error) {
                         trace("Slow death :(");
                    }
                   
                   
                   
                    simpleChatModel.addEventListener(ChatEvent.HISTORY_CHANGE, onChatMsg);
                   
               }
              
               private function submitChat(str:String):void
               {
                    _cmd = new ChatMessageDescriptor();
                    _cmd.displayName = cSession.userManager.getUserDescriptor(cSession.userManager.myUserID).displayName;
                    trace(_cmd.displayName);
                    trace(str);
                   
                    _cmd.msg = str;
                   
                    simpleChatModel.sendMessage(_cmd);
                   
                    chat_msg_input.text = "";
               }
              
               private function clearChat():void
               {
                    chat_msg_area.text = "";
                    simpleChatModel.clear();
               }
              
               private function onChatMsg(evt:ChatEvent):void
               {
                    if (evt.message != null && evt.message.msg != null && evt.message.displayName != null)
                    {
                         chat_msg_area.text +=  evt.message.displayName + ": " + evt.message.msg + "\r";
                        
                    } else {
                         chat_msg_area.text = "";
                    }
               }
              
              
              
              
              
          ]]>
     </mx:Script>
    
     <session:ConnectSessionContainer roomURL="https://connectnow.acrobat.com/USERNAME/ROOMNAME" authenticator="{_auth}"
                                         id="cSession"
                                         width="100%"
                                         height="100%"
                                         autoLogin="false" >
         
          <mx:VBox width="100%" height="100%">
               <mx:TextArea width="100%" height="100%" id="chat_msg_area" />
              
               <mx:ControlBar >
                    <mx:TextInput width="100%" id="chat_msg_input" />
                   
                    <mx:Button label="Submit Chat" click="{ submitChat(chat_msg_input.text) }" />
                    <mx:Button label="Clear Chat" click="clearChat()" />
               </mx:ControlBar>
          </mx:VBox>
         
     </session:ConnectSessionContainer>
</mx:Application>

Avatar

Former Community Member

Hi DB,

Arun's approach will work, but there may be a (slightly) easier way - let

me see if I'm understanding this properly :

I guess the case you¹re looking for is roughly :

1. See if a collection exists. If it does, subscribe to it.

2. If the collection doesn¹t exist, if I¹m not an OWNER, don¹t subscribe.

I will say that this seems like a pretty odd use-case, likely stemming

from constraints that might not be present in most apps that we've seen (at

least, I hope!).

To solve this, you could do one of 2 things ­ either depend on try/catch

(as Arun points out), or use a RootCollectionNode to inspect which

collectionNodes exist, before attempting anything. This is, as I say, a

little off the beaten path.

// do this before you initiate your session.

var rootNode:RootCollectionNode = new RootCollectionNode();

rootNode.subscribe();

From there, you can use the rootNode.collectionNames ArrayCollection to see

if the sharedID you¹re looking for exists.

Not the most elegant experience, but as I say, not what we¹d consider a

³normal² thing to do.

hope that helps,

nigel