Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.
SOLVED

Knocking Queue Example

Avatar

Former Community Member

Hello all,

I have been working on my app for a while and it's almost finished, however, I need to implement a knocking queue.  I just had a couple questions about the KnockingQueue sample app. in the examples.

1)  The app. seems to be set up for both "owners" and "guests".  In the comments it says "For the guests, the app has to run without user credentials and with only room url and username."  For testing purposes I just want to keep things simple and don't want to use external authentication.  Does this mean I should create 2 separate apps - one with user creds. hardcoded and one without?

2)  According to the documentation, the user who can accept/deny users in the queue has to log in as "OWNER".  Although it makes perfect sense, I was under the assumption (from reading the wiki files and watching the videos) that this was a bad idea, but wasn't sure.  I will use external authentication in my deployed app., so there is no way anyone can get my user creds. and add/change rooms.  The system I am implementing will actually have 2 apps., one for a staff member and one for a client.  The one for the staff will have a few extra features (pending/accepted user windows, buttons, etc...).  Under these circumstances I am assuming that the staff will log in to the room as "OWNER" and the client will log in as "PUBLISHER".  Is this a good setup?  I'm sure the answer is probably obvious, but I just want to make sure I am on the right track with this!

Thanks a lot!

Matt

1 Accepted Solution

Avatar

Correct answer by
Employee

Hi,

For question 1, the answer is YES

For question 2, You don¹t have to login in the controller(staff member) as

Owner by supplying credentials, but you can always promote the user to the

role of OWNER through external authentication.

Thanks

Arun

View solution in original post

12 Replies

Avatar

Correct answer by
Employee

Hi,

For question 1, the answer is YES

For question 2, You don¹t have to login in the controller(staff member) as

Owner by supplying credentials, but you can always promote the user to the

role of OWNER through external authentication.

Thanks

Arun

Avatar

Former Community Member

Apponusa,

First off, I wanted to say thanks to the Adobe LCCS team!  You guys are always very helpful!  Also, I wanted to I apologize for asking what are probably elementary questions.  I work for a small company in Japan, and I am the only Flex developer, so I don't have anybody I can bounce ideas off of or go to for help (particularly when it comes to LCCS).  I need to make absolutely sure I understand what I am doing, and this forum is the only place I can go to!

Ok, great!  I understand what you're saying and will be using external authentication.

I did have one more question:

For the staff, the external authentication is pretty straight forward.  I will have them log in to my system, and when they go to the page which launches the app., it will create a token for them and log them into an LCCS session in a particular room with a role of "OWNER".

For the client, I am not exactly sure which is the best way to approach this.  What I'm thinking is the client will not log into my site, but will just go to a specified URL, enter their name and a generic password (which will be the same for all clients).  The password entry is simply a way to try and keep out unwanted/malicious users and has nothing to do with logging into the site.  On top of that, I am using a knocking queue as a further barrier.  Once the client's app. has launched, he/she will be accepted into the room by a staff and then be promoted to a "PUBLISHER".  I will still use external authentication for the client for security reasons, but they will arrive as a guest (until they are let into the room and promoted).

How does that sound?  Again, I just want to make sure that I'm implementing the best possible setup for my use case, while still being secure.

I understand that you are busy, so I really appreciate the help.  Thank you very much for any feedback you can give on this!

Matt

Avatar

Employee

Hi Matt,

You approach sounds good to me.

LCCS gives you control over your authentication mechanism, role, session expiration and a few other parameters. That must help you control the users on who use it, and how they use it.

Thanks

Arun

Avatar

Former Community Member

Thanks a lot Arun!

I did get the knockingQueue example working a few days ago without a problem.  However, once I migrated the code over to my app. I'm getting an error.

I first copied my original project so there are now 2 apps.("staff" and "client").  Both compiled/ran fine before trying to implement the knocking queue.

I then inserted the relevant parts of the KnockingQueue.mxml file into their respective apps and created the List components in the staff app.  The "staff" app. seems to work fine.  I"m getting the GUEST showing up in the "Pending" window and can accept or deny him/her.  The problem is that I"m getting very strange behavior in the "client" app.  About 80% of the time I get an "insufficient permissions to create node" error (to my knowledge, I haven't created any new nodes), which occasionally freezes the browser and causes the Flash Player to crash.  The other 20% of the time it starts fine, but the GUEST doesn't show up in the "Pending" window of the "client" app.

I have spent 2 days and tried almost everything, but can't track down the problem.

Here is my code (edited to show the relevant parts):

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
                  xmlns:s="library://ns.adobe.com/flex/spark"
                  xmlns:mx="library://ns.adobe.com/flex/mx"
                  xmlns:fp="com.devaldi.controls.flexpaper.*" width="960" height="600" minWidth="960" minHeight="600" maxWidth="900" maxHeight="600" xmlns:rtc="http://ns.adobe.com/rtc"
                  backgroundColor="#eeeeee" xmlns:coreUI="http://ns.adobe.com/coreUI" xmlns:components="components.*">
     <fx:Declarations>
          <!-- Place non-visual elements (e.g., services, value objects) here -->
     </fx:Declarations>

     <fx:Script>
          <![CDATA[

                        private function onCreationComplete():void
               {
                    cSession.roomManager.guestsHaveToKnock = true ;
                                // The following conditional is probably not needed
                    if ( cSession.userManager.myUserRole == UserRoles.LOBBY )
                    {
                         cSession.roomManager.knockingQueue.addEventListener(UserQueueEvent.ACCEPT,onMyAccept);
                         cSession.roomManager.knockingQueue.addEventListener(UserQueueEvent.DENY,onMyDeny);
                    }
               }

               // Update the user's user interface when they are accepted by letting them see the chat window.
               private function onMyAccept(p_evt:UserQueueEvent):void
               {
                    cSession.roomManager.autoPromote = true ;
               }
              
               // Show denied users a blank screen.
               private function onMyDeny(p_evt:UserQueueEvent):void
               {
                    studentUI.removeAllElements();
               }

               //  This is the section which handles a video subscriber
               private function onSyncChange():void{
                    if (cSession.isSynchronized){
                         cSession.roomManager.autoPromote = true ;
                        
                         _userManager = cSession.userManager;
                         _streamManager = cSession.streamManager;
                         _streamManager.addEventListener(StreamEvent.STREAM_DELETE, onStreamLeft);
                         _streamManager.addEventListener(StreamEvent.STREAM_RECEIVE, onStreamReceive);
                        
                         trace("onSyncChange");
                         displayExistingStreams()
                    }
                    else {
                         // to do
                    }
               }
              
               private function onStreamReceive(p_evt:StreamEvent):void
               {
                    trace("on Stream receive : "+p_evt.streamDescriptor.streamPublisherID);
                   
                    if (p_evt.streamDescriptor.streamPublisherID!=_userManager.myUserID) {
                        
                         setUpSubscriber( p_evt.streamDescriptor.streamPublisherID);
                    }
               }
              
               private function onStreamLeft(evt:StreamEvent):void{
                    if (evt.streamDescriptor.streamPublisherID != _userManager.myUserID){
                         // to do
                    }
               }
              
               private function setUpSubscriber(p_publisherID:String):void
               {
                    trace("subscribing to : "+p_publisherID);
                    webcamSubExt.connectSession = cSession ;
                    webcamSubExt.subscribe();
                    webcamSubExt.publisherIDs = [p_publisherID];
               }
              
               // run this function once, on connectSession.synchronizationChange (once the room is ready)
               // sets up all streams that were in the room before you showed up
               private function displayExistingStreams():void
               {
                    // loop over the whole set of camera streams
                    var publishers:Object = _streamManager.getStreamsOfType(StreamManager.CAMERA_STREAM);
                    trace("all the "+publishers.length+" publishers : "+publishers);
                    var found:Boolean = false;
                    for (var publisherID:String in publishers) {
                         trace("found a camera : "+publisherID);
                         if (publisherID!=_userManager.myUserID) {
                              trace("it's not me");
                              setUpSubscriber(publisherID);
                              found=true;
                         }
                    }
               }
               //  End of code which handles a video subscriber


]]>
     </fx:Script>
     <rtc:ConnectSessionContainer id="cSession" width="100%" height="100%" roomURL="https://collaboration.adobelivecycle.com/myAccount/myfirstroom" synchronizationChange="onSyncChange()" creationComplete="onCreationComplete()">
          <rtc:authenticator>
               <rtc:AdobeHSAuthenticator id="auth" userName="Guest" protocol="rtmfp" />
          </rtc:authenticator>
          <s:BorderContainer height="600" width="960" backgroundColor="#eeeeee" borderWeight="3" horizontalCenter="0" verticalCenter="0">
                    <s:HGroup id="studentUI" left="10" top="10" right="10" bottom="10">
                         <rtc:WebcamPublisher width="0" height="0" id="webcamPub" resolutionFactor="4" quality="70" fps="10"/>
                         <s:VGroup width="240" height="100%" gap="0" paddingRight="10">
                              <s:VGroup width="240" height="280">
                                   <rtc:WebcamSubscriber width="240" height="240" id="webcamSubExt" displayUserBars="false"/>
                                   <s:VGroup width="100%" horizontalAlign="center">
                                        <s:HSlider id="gainSlider" minimum="0" maximum="100" value="{localVol}" liveDragging="true" change="onGainChange(event)" />
                                   </s:VGroup>
                                   <rtc:AudioPublisher id="audioPub" width="0" height="0" useEchoSuppression="true" gain="60"/>
                                   <rtc:AudioSubscriber id="audioSub" width="0" height="0"/>
                              </s:VGroup>
                              <s:VGroup width="240" height="100%" horizontalAlign="left">
                                   <s:HGroup width="240">
                                        <rtc:WebcamSubscriber width="120" height="120" webcamPublisher="{webcamPub}" publisherIDs="{[cSession.userManager.myUserID]}" displayUserBars="false"/>

                                        <s:VGroup width="100%" height="100%">
                                             <s:HGroup width="110">
                                                  <s:ToggleButton id="videoStartBtn" label="Publish" click="onPublishClick(event)"/>
                                                  <s:Button id="settingsPopupBtn" styleName="settingsButton" skinClass="controls.skins.IconButtonSkin" click="settingsButtonHandler(event)"/>
                                             </s:HGroup>
                                        </s:VGroup>
                                   </s:HGroup>
                              </s:VGroup>
                         </s:VGroup>
                    </s:HGroup>
          </s:BorderContainer>
     </rtc:ConnectSessionContainer>
</s:Application>

I included the video subscriber code because that may or may not be a source of the problem.  I am using Flex SDK 4.5 and lccs_10_3Beta.swc.  I don't think the error has to do with duplicating the app. and coming in as a GUEST with the "client" because I have previously come in the room with the master app. and I haven't changed any of my LCCS component ID's since then.  This is very strange!

Thanks in advance for your help!

Matt

Avatar

Employee

I had a cursory glance of your app, and I am guessing it has something to do with onSyncChange & onCreationComplete methods.

Can you post your entire code, and the error message trace. That would help us help you

Thanks

Arun

Avatar

Former Community Member

Arun,

Thank you very much!  This is basically the core of my app., so if I can get it working then everything else is pretty much "bells and whistles"!

I did some more tests and stripped the app. down to the basic essentials (just audio and webcam publishers/subscribers).  When I did this, both apps logged in and worked as expected.  What I found was that as soon as I added the "whiteboard" or "notes" pods then the errors started.  This doesn't make sense to me because both of these pods are in the "staff" app. as well, which is set up as an "OWNER", and they are both left as defaults (i.e. no IDs)!  I have to be overlooking something really simple, which is causing the "insufficient permissions" errors.

Here is the full code (not much more than my previous code, but with the whiteboard and notes added):

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
                  xmlns:s="library://ns.adobe.com/flex/spark"
                  xmlns:mx="library://ns.adobe.com/flex/mx"
                  xmlns:fp="com.devaldi.controls.flexpaper.*" width="960" height="600" minWidth="960" minHeight="600" maxWidth="900" maxHeight="600" xmlns:rtc="http://ns.adobe.com/rtc"
                  backgroundColor="#eeeeee" xmlns:coreUI="http://ns.adobe.com/coreUI" xmlns:components="components.*">
     <fx:Declarations>
          <!-- Place non-visual elements (e.g., services, value objects) here -->
     </fx:Declarations>
    
     <fx:Script>
          <![CDATA[
               import com.adobe.rtc.events.AuthenticationEvent;
               import com.adobe.rtc.collaboration.WebcamSubscriber;
               import com.adobe.rtc.events.StreamEvent;
               import com.adobe.rtc.events.UserEvent;
               import com.adobe.rtc.sharedManagers.StreamManager;
               import com.adobe.rtc.sharedManagers.UserManager;
              
               import com.adobe.rtc.events.UserQueueEvent;
               import com.adobe.rtc.messaging.UserRoles;
               import com.adobe.rtc.sharedManagers.RoomManager;
               import com.adobe.rtc.sharedManagers.constants.UserStatuses;
               import com.adobe.rtc.sharedModel.UserQueue;
               import com.adobe.rtc.sharedModel.userQueueClasses.UserQueueItem;
              
               import com.adobe.rtc.events.SessionEvent;
               import mx.events.FlexEvent;
               import mx.utils.ObjectUtil;
               import mx.core.FlexGlobals;
              
               import com.adobe.rtc.events.UserQueueEvent;
               import com.adobe.rtc.messaging.UserRoles;
              
               import flash.media.Microphone;
              
               import mx.collections.ArrayCollection;
               import mx.controls.Alert;
               import mx.managers.PopUpManager;
              
               import spark.components.TitleWindow;
               import spark.events.TitleWindowBoundsEvent;
              
               private var _userManager:UserManager;
               private var _streamManager:StreamManager;
              
               [Bindable]
               private var localVol:Number = 70;


               private function onCreationComplete():void
               {
                    cSession.roomManager.guestsHaveToKnock = true ;
                    // The following conditional is probably not needed
                    if ( cSession.userManager.myUserRole == UserRoles.LOBBY )
                    {
                         cSession.roomManager.knockingQueue.addEventListener(UserQueueEvent.ACCEPT,onMyAccept);
                         cSession.roomManager.knockingQueue.addEventListener(UserQueueEvent.DENY,onMyDeny);
                    }
               }
              
               // Update the user's user interface when they are accepted by letting them see the chat window.
               private function onMyAccept(p_evt:UserQueueEvent):void
               {
                    cSession.roomManager.autoPromote = true ;
               }
              
               // Show denied users a blank screen.
               private function onMyDeny(p_evt:UserQueueEvent):void
               {
                    clientUI.removeAllElements();
               }
              
               //  This is the section which handles a video subscriber
               private function onSyncChange():void{
                    if (cSession.isSynchronized){
                         cSession.roomManager.autoPromote = true ;
                        
                         _userManager = cSession.userManager;
                         _streamManager = cSession.streamManager;
                         _streamManager.addEventListener(StreamEvent.STREAM_DELETE, onStreamLeft);
                         _streamManager.addEventListener(StreamEvent.STREAM_RECEIVE, onStreamReceive);
                        
                         trace("onSyncChange");
                         displayExistingStreams()
                    }
                    else {
                         // to do
                    }
               }
              
               private function onStreamReceive(p_evt:StreamEvent):void
               {
                    trace("on Stream receive : "+p_evt.streamDescriptor.streamPublisherID);
                   
                    if (p_evt.streamDescriptor.streamPublisherID!=_userManager.myUserID) {
                        
                         setUpSubscriber( p_evt.streamDescriptor.streamPublisherID);
                    }
               }
              
               private function onStreamLeft(evt:StreamEvent):void{
                    if (evt.streamDescriptor.streamPublisherID != _userManager.myUserID){
                         // to do
                    }
               }
              
               private function setUpSubscriber(p_publisherID:String):void
               {
                    trace("subscribing to : "+p_publisherID);
                    webcamSubExt.connectSession = cSession ;
                    webcamSubExt.subscribe();
                    webcamSubExt.publisherIDs = [p_publisherID];
               }
              
               // run this function once, on connectSession.synchronizationChange (once the room is ready)
               // sets up all streams that were in the room before you showed up
               private function displayExistingStreams():void
               {
                    // loop over the whole set of camera streams
                    var publishers:Object = _streamManager.getStreamsOfType(StreamManager.CAMERA_STREAM);
                    trace("all the "+publishers.length+" publishers : "+publishers);
                    var found:Boolean = false;
                    for (var publisherID:String in publishers) {
                         trace("found a camera : "+publisherID);
                         if (publisherID!=_userManager.myUserID) {
                              trace("it's not me");
                              setUpSubscriber(publisherID);
                              found=true;
                         }
                    }
               }
               //  End of code which handles a video subscriber

               /*****
                * Handler for the stop and start buttons.
                *******/
               private function onPublishClick(p_evt:MouseEvent):void
               {
                    if ( p_evt.currentTarget.label == "Publish" )
                    {
                         webcamPub.publish();
                         audioPub.publish();
                         p_evt.currentTarget.label = "Stop" ;
                    }else if (p_evt.currentTarget.label == "Stop" )
                    {
                         webcamPub.stop();
                         audioPub.stop();
                         p_evt.currentTarget.label = "Publish" ;
                    }
               }

               private function onGainChange(event:Event):void
               {
                    audioSub.setLocalVolume(gainSlider.value/100);
               }
          ]]>
     </fx:Script>
     <rtc:ConnectSessionContainer id="cSession" width="100%" height="100%" roomURL="https://collaboration.adobelivecycle.com/myaccount/myfirstroom" synchronizationChange="onSyncChange()" creationComplete="onCreationComplete()">
          <rtc:authenticator>
               <rtc:AdobeHSAuthenticator id="auth" userName="Guest" protocol="rtmfp" />
          </rtc:authenticator>
          <s:BorderContainer height="600" width="960" backgroundColor="#eeeeee" borderWeight="3" horizontalCenter="0" verticalCenter="0">
               <s:HGroup id="clientUI" left="10" top="10" right="10" bottom="10">
                    <rtc:WebcamPublisher width="0" height="0" id="webcamPub" resolutionFactor="4" quality="70" fps="10"/>
                    <s:VGroup width="240" height="100%" gap="0" paddingRight="10">
                         <s:VGroup width="240" height="280">
                              <rtc:WebcamSubscriber width="240" height="240" id="webcamSubExt" displayUserBars="false"/>
                              <s:VGroup width="100%" horizontalAlign="center">
                                   <s:HSlider id="gainSlider" minimum="0" maximum="100" value="{localVol}" liveDragging="true" change="onGainChange(event)" />
                              </s:VGroup>
                              <rtc:AudioPublisher id="audioPub" width="0" height="0" useEchoSuppression="true" gain="60"/>
                              <rtc:AudioSubscriber id="audioSub" width="0" height="0"/>
                         </s:VGroup>
                         <s:VGroup width="240" height="100%" horizontalAlign="left">
                              <s:HGroup width="240">
                                   <rtc:WebcamSubscriber width="120" height="120" webcamPublisher="{webcamPub}" publisherIDs="{[cSession.userManager.myUserID]}" displayUserBars="false"/>
                                  
                                   <s:VGroup width="100%" height="100%">
                                        <s:HGroup width="110">
                                             <s:ToggleButton id="videoStartBtn" label="Publish" click="onPublishClick(event)"/>
                                        </s:HGroup>
                                   </s:VGroup>
                              </s:HGroup>
                         </s:VGroup>
                    </s:VGroup>
                    <mx:Spacer width="10"/>
                    <mx:TabNavigator width="660" height="100%">

                         <s:NavigatorContent label="Notes" width="100%" height="540">
                              <rtc:Note width="100%" height="520" sessionDependentItems="true" />
                         </s:NavigatorContent>
                         <s:NavigatorContent label="Whiteboard" width="100%" height="540">
                              <rtc:SharedWhiteBoard width="100%" height="540" sessionDependent="false" />
                         </s:NavigatorContent>
                    </mx:TabNavigator>
               </s:HGroup>
          </s:BorderContainer>
     </rtc:ConnectSessionContainer>
</s:Application>

Here is the error message when I add the notes:

Error: MessageManager.createNode : insufficient permissions to create node
     at com.adobe.rtc.messaging.manager::MessageManager/http://www.adobe.com/2006/connect/cocomo/messaging/internal::createNode()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/messaging/manager/MessageManager.as:270]
     at com.adobe.rtc.sharedModel::CollectionNode/createNode()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/sharedModel/CollectionNode.as:379]
     at com.adobe.rtc.sharedModel::SharedProperty/onSynchronizationChange()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/sharedModel/SharedProperty.as:571]
     at flash.events::EventDispatcher/dispatchEventFunction()
     at flash.events::EventDispatcher/dispatchEvent()
     at com.adobe.rtc.sharedModel::CollectionNode/http://www.adobe.com/2006/connect/cocomo/messaging/internal::setIsSynchronized()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/sharedModel/CollectionNode.as:700]
     at com.adobe.rtc.messaging.manager::MessageManager/receiveAllSynchData()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/messaging/manager/MessageManager.as:851]
     at com.adobe.rtc.messaging.manager::MessageManager/http://www.adobe.com/2006/connect/cocomo/messaging/internal::receiveItems()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/messaging/manager/MessageManager.as:596]
     at com.adobe.rtc.session.managers::SessionManagerBase/receiveItems()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/session/managers/SessionManagerBase.as:427]

...and here is the error message for the whiteboard:

TypeError: Error #1009: Cannot access a property or method of a null object reference.
     at com.adobe.coreUI.controls.whiteboardClasses::WBCanvas/onShapePropertiesChange()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/coreUI/controls/whiteboardClasses/WBCanvas.as:1070]
     at flash.events::EventDispatcher/dispatchEventFunction()
     at flash.events::EventDispatcher/dispatchEvent()
     at com.adobe.rtc.pods.sharedWhiteBoardClasses::SharedWBModel/onShapePropertiesChange()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/pods/sharedWhiteBoardClasses/SharedWBModel.as:529]
     at com.adobe.rtc.pods.sharedWhiteBoardClasses::SharedWBModel/onItemReceive()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/pods/sharedWhiteBoardClasses/SharedWBModel.as:678]
     at flash.events::EventDispatcher/dispatchEventFunction()
     at flash.events::EventDispatcher/dispatchEvent()
     at com.adobe.rtc.sharedModel::CollectionNode/http://www.adobe.com/2006/connect/cocomo/messaging/internal::receiveItem()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/sharedModel/CollectionNode.as:777]
     at com.adobe.rtc.messaging.manager::MessageManager/http://www.adobe.com/2006/connect/cocomo/messaging/internal::receiveItem()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/messaging/manager/MessageManager.as:718]
     at com.adobe.rtc.messaging.manager::MessageManager/http://www.adobe.com/2006/connect/cocomo/messaging/internal::receiveItems()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/messaging/manager/MessageManager.as:593]

Thanks again for taking the time to look at this!

Matt

Avatar

Employee

Matt,,

Please add the pods after the kockingqueue uesr is accepted. That should fix your issue.

I have attached the working version of your app.

<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"

                  xmlns:s="library://ns.adobe.com/flex/spark"

                  xmlns:mx="library://ns.adobe.com/flex/mx"

                  xmlns:fp="com.devaldi.controls.flexpaper.*"

                  xmlns:rtc="http://ns.adobe.com/rtc"

                  xmlns:coreUI="http://ns.adobe.com/coreUI"

                  xmlns:components="components.*"

                  xmlns:session="com.adobe.rtc.session.*"

                  xmlns:authentication="com.adobe.rtc.authentication.*"

                  xmlns:collaboration="com.adobe.rtc.collaboration.*"

                  xmlns:pods="com.adobe.rtc.pods.*"

                  width="960" height="600" minWidth="960" minHeight="600" maxWidth="900"

                  maxHeight="600" backgroundColor="#eeeeee">

     <fx:Declarations>

          <!-- Place non-visual elements (e.g., services, value objects) here -->

     </fx:Declarations>

     

     <fx:Script>

          <![CDATA[

               import com.adobe.rtc.collaboration.WebcamSubscriber;

               import com.adobe.rtc.events.AuthenticationEvent;

               import com.adobe.rtc.events.SessionEvent;

               import com.adobe.rtc.events.StreamEvent;

               import com.adobe.rtc.events.UserEvent;

               import com.adobe.rtc.events.UserQueueEvent;

               import com.adobe.rtc.messaging.UserRoles;

               import com.adobe.rtc.pods.Note;

               import com.adobe.rtc.sharedManagers.RoomManager;

               import com.adobe.rtc.sharedManagers.StreamManager;

               import com.adobe.rtc.sharedManagers.UserManager;

               import com.adobe.rtc.sharedManagers.constants.UserStatuses;

               import com.adobe.rtc.sharedModel.UserQueue;

               import com.adobe.rtc.sharedModel.userQueueClasses.UserQueueItem;

               

               import flash.media.Microphone;

               

               import mx.collections.ArrayCollection;

               import mx.controls.Alert;

               import mx.core.FlexGlobals;

               import mx.events.FlexEvent;

               import mx.managers.PopUpManager;

               import mx.utils.ObjectUtil;

               

               import spark.components.TitleWindow;

               import spark.events.TitleWindowBoundsEvent;

               

               private var _userManager:UserManager;

               private var _streamManager:StreamManager;

               

               [Bindable]

               private var localVol:Number = 70;

               

               

               private function onCreationComplete():void

               {

                    // Require that guests must knock.Note that this line changes the setting of your room so that in future anyone guest coming in

                    // has to knock. You can change this setting again either from code by making roomManager.guestsHaveToKnock as false or from the DevConsole's manage tab

                    cSession.roomManager.guestsHaveToKnock = true ;

                    

                    // For owners that arrive late, the pending queue is populated with waiting users.

                    if ( cSession.userManager.myUserRole == UserRoles.OWNER ) {

                         

                         var pendingArray:Array = new Array();

                         var queue:Array = cSession.roomManager.knockingQueue.pendingQueue ;

                         for ( var i:int = 0 ; i < queue.length ; i++ ) {

                              var item:UserQueueItem = queue[i] as UserQueueItem ;

                              pendingArray.push({label:item.descriptor.displayName,descriptor:item.descriptor});

                         }

                         knockingList.dataProvider = pendingArray ;

                         cSession.roomManager.knockingQueue.addEventListener(UserQueueEvent.ITEM_UPDATE,onKnockingQueueUpdate);

                         cSession.roomManager.knockingQueue.roleForManaging =  UserRoles.PUBLISHER;

                         addPods();

                    }else {

                         //Else, remove all UI from seeing.

                         knockingUI.removeAllChildren() ;

                    }

                    

                    

                    if ( cSession.userManager.myUserRole == UserRoles.LOBBY ) {

                         cSession.roomManager.knockingQueue.addEventListener(UserQueueEvent.ACCEPT,onMyAccept);

                         cSession.roomManager.knockingQueue.addEventListener(UserQueueEvent.DENY,onMyDeny);

                    }

                    

               }

               

               private function addPods():void

               {

                    var notePod:Note = new Note();

                    notePod.width = notePod.height = 200;

                    notePod.connectSession = cSession;

                    notePod.sharedID = "notePod";

                    notePod.subscribe();

                    //noteTab.addChild(notePod);

                    noteTab.addElement(notePod);

               }

               

               // When the queue changes, update the pending, knocking, and denied list

               private function onKnockingQueueUpdate(p_evt:UserQueueEvent):void

               {

                    var pendingArray:Array = new Array();

                    // For the pending list you can directly use the pendingQueue property in UserQueue. For details,

                    // see the onCreationComplete documentation in the LCCS API Reference.

                    

                    var acceptedArray:Array = new Array();

                    var deniedArray:Array = new Array();

                    

                    var queue:Array = cSession.roomManager.knockingQueue.queue ;

                    

                    for ( var i:int = 0 ; i < queue.length ; i++ ) {

                         var item:UserQueueItem = queue[i] as UserQueueItem ;

                         if ( item.status == UserQueueItem.STATUS_PENDING ) {

                              pendingArray.push({label:item.descriptor.displayName,descriptor:item.descriptor});

                         }

                         

                         if ( item.status == UserQueueItem.STATUS_ACCEPTED ) {

                              acceptedArray.push({label:item.descriptor.displayName,descriptor:item.descriptor});

                         }

                         if ( item.status == UserQueueItem.STATUS_DENIED ) {

                              deniedArray.push({label:item.descriptor.displayName,descriptor:item.descriptor});

                         }

                         

                    }

                    

                    

                    knockingList.dataProvider = pendingArray ;

                    acceptedList.dataProvider = acceptedArray ;

                    deniedList.dataProvider = deniedArray ;

                    

               }

               

               // Accept the knocker.

               private function onAcceptClick(p_evt:MouseEvent):void

               {

                    if ( cSession.userManager.myUserRole == UserRoles.OWNER ) {

                         cSession.roomManager.knockingQueue.acceptUser(knockingList.selectedItem.descriptor.userID);

                    }

               }

               

               

               // Deny the knocker.

               private function onDenyClick(p_evt:MouseEvent):void

               {

                    if ( cSession.userManager.myUserRole == UserRoles.OWNER )

                         cSession.roomManager.knockingQueue.denyUser(knockingList.selectedItem.descriptor.userID);

               }

               

               

               

               // Update the user's user interface when they are accepted by letting them see the chat window.

               private function onMyAccept(p_evt:UserQueueEvent):void

               {

                    knockingUI.addChild(compContainer);

                    addPods();

                    compContainer.percentWidth = 100 ;

                    compContainer.percentHeight = 80 ;

               }

               

               // Show denied users a blank screen.

               private function onMyDeny(p_evt:UserQueueEvent):void

               {

                    knockingUI.removeAllChildren();

               }               

               //  This is the section which handles a video subscriber

               private function onSyncChange():void{

                    if (cSession.isSynchronized){

                         cSession.roomManager.autoPromote = true ;

                         

                         _userManager = cSession.userManager;

                         _streamManager = cSession.streamManager;

                         _streamManager.addEventListener(StreamEvent.STREAM_DELETE, onStreamLeft);

                         _streamManager.addEventListener(StreamEvent.STREAM_RECEIVE, onStreamReceive);

                         

                         trace("onSyncChange");

                         displayExistingStreams()

                    }

                    else {

                         // to do

                    }

               }

               

               private function onStreamReceive(p_evt:StreamEvent):void

               {

                    trace("on Stream receive : "+p_evt.streamDescriptor.streamPublisherID);

                    

                    if (p_evt.streamDescriptor.streamPublisherID!=_userManager.myUserID) {

                         

                         setUpSubscriber( p_evt.streamDescriptor.streamPublisherID);

                    }

               }

               

               private function onStreamLeft(evt:StreamEvent):void{

                    if (evt.streamDescriptor.streamPublisherID != _userManager.myUserID){

                         // to do

                    }

               }

               

               private function setUpSubscriber(p_publisherID:String):void

               {

                    trace("subscribing to : "+p_publisherID);

                    webcamSubExt.connectSession = cSession ;

                    webcamSubExt.subscribe();

                    webcamSubExt.publisherIDs = [p_publisherID];

               }

               

               // run this function once, on connectSession.synchronizationChange (once the room is ready)

               // sets up all streams that were in the room before you showed up

               private function displayExistingStreams():void

               {

                    // loop over the whole set of camera streams

                    var publishers:Object = _streamManager.getStreamsOfType(StreamManager.CAMERA_STREAM);

                    trace("all the "+publishers.length+" publishers : "+publishers);

                    var found:Boolean = false;

                    for (var publisherID:String in publishers) {

                         trace("found a camera : "+publisherID);

                         if (publisherID!=_userManager.myUserID) {

                              trace("it's not me");

                              setUpSubscriber(publisherID);

                              found=true;

                         }

                    }

               }

               //  End of code which handles a video subscriber

               

               /*****

                * Handler for the stop and start buttons.

                *******/

               private function onPublishClick(p_evt:MouseEvent):void

               {

                    if ( p_evt.currentTarget.label == "Publish" )

                    {

                         webcamPub.publish();

                         audioPub.publish();

                         p_evt.currentTarget.label = "Stop" ;

                    }else if (p_evt.currentTarget.label == "Stop" )

                    {

                         webcamPub.stop();

                         audioPub.stop();

                         p_evt.currentTarget.label = "Publish" ;

                    }

               }

               

               private function onGainChange(event:Event):void

               {

                    audioSub.setLocalVolume(gainSlider.value/100);

               }

          ]]>

     </fx:Script>

     <session:ConnectSessionContainer id="cSession" width="100%" height="80%"

                                              creationComplete="onCreationComplete()"

                                              roomURL="https://collaboration.adobelivecycle.com/ag/as/"

                                              synchronizationChange="onSyncChange()">

          <session:authenticator>

               <authentication:AdobeHSAuthenticator id="auth" protocol="rtmfp"

                                                             userName="aponn"/>

          </session:authenticator>

          <mx:VBox id="knockingUI" width="100%" height="100%">

               <s:BorderContainer width="960" height="600" backgroundColor="#eeeeee" borderWeight="3"

                                      horizontalCenter="0" verticalCenter="0" id="compContainer">

                    <s:HGroup id="clientUI" left="10" right="10" top="10" bottom="10">

                         <collaboration:WebcamPublisher id="webcamPub" width="0" height="0" fps="10"

                                                               quality="70" />

                         <s:VGroup width="240" height="100%" gap="0" paddingRight="10">

                              <s:VGroup width="240" height="280">

                                   <collaboration:WebcamSubscriber id="webcamSubExt" width="240" height="240"

                                                                           displayUserBars="false"/>

                                   <s:VGroup width="100%" horizontalAlign="center">

                                        <s:HSlider id="gainSlider" change="onGainChange(event)"

                                                     liveDragging="true" maximum="100" minimum="0"

                                                     value="{localVol}"/>

                                   </s:VGroup>

                                   <collaboration:AudioPublisher id="audioPub" width="0" height="0" gain="60"

                                                                        useEchoSuppression="true"/>

                                   <collaboration:AudioSubscriber id="audioSub" width="0" height="0"/>

                              </s:VGroup>

                              <s:VGroup width="240" height="100%" horizontalAlign="left">

                                   <s:HGroup width="240">

                                        <collaboration:WebcamSubscriber width="120" height="120"

                                                                                displayUserBars="false"

                                                                                publisherIDs="{[cSession.userManager.myUserID]}"

                                                                                webcamPublisher="{webcamPub}"/>

                                        

                                        <s:VGroup width="100%" height="100%">

                                             <s:HGroup width="110">

                                                  <s:ToggleButton id="videoStartBtn" label="Publish"

                                                                      click="onPublishClick(event)"/>

                                             </s:HGroup>

                                        </s:VGroup>

                                   </s:HGroup>

                              </s:VGroup>

                         </s:VGroup>

                         <mx:Spacer width="10"/>

                         <mx:TabNavigator id="podTabs" width="660" height="100%">

                              <s:NavigatorContent id="noteTab" width="100%" height="540" label="Notes">

                              </s:NavigatorContent>

                              <s:NavigatorContent width="100%" height="540" label="Whiteboard" id="wbTab">

                              </s:NavigatorContent>

                         </mx:TabNavigator>

                    </s:HGroup>

               </s:BorderContainer>

               <mx:HBox width="100%" height="20%" horizontalGap="15">

                    <mx:VBox>

                         <mx:Label text="Pending User List"/>

                         <mx:List id="knockingList" width="100%" height="200"/>

                         <mx:HBox>

                              <mx:Button label="accept" click="onAcceptClick(event)"

                                           enabled="{knockingList.selectedItem}"/>

                              <mx:Button label="deny" click="onDenyClick(event)"

                                           enabled="{knockingList.selectedItem}"/>

                         </mx:HBox>

                    </mx:VBox>

                    <mx:VBox>

                         <mx:Label text="Accepted User List"/>

                         <mx:List id="acceptedList" width="100%" height="200"/>

                    </mx:VBox>

                    <mx:VBox>

                         <mx:Label text="Denied User List"/>

                         <mx:List id="deniedList" width="100%" height="200"/>

                    </mx:VBox>

               </mx:HBox>

          </mx:VBox>

     </session:ConnectSessionContainer>

</s:Application>

Thanks

Arun

Avatar

Former Community Member

Arun,

I tested your code and it works perfectly

Thank you very much for taking the time to troubleshoot/test this!  Besides delivering outstanding products, the support in the forums by engineers is why I totaly stand by Adobe!  (I wonder how many Objective C engineers will help Apple fanatics with their iPhone apps.!)

I separated the logic between the 2 apps., removing the unnecessary code in each (most of the KnockingQueue code is mutually exclusive) and it still works!

The only serious problem I'm having is when I add the code for the SharedWhiteBoard pod:

var wbPod:SharedWhiteBoard = new SharedWhiteBoard();
wbPod.width = wbPod.height = 500;
wbPod.connectSession = cSession;

wbPod.sharedID = "wbPod";
wbPod.subscribe();
wbTab.addElement(wbPod);

I thought it would compile/run without a problem, but I keep getting the following error:

TypeError: Error #1009: Cannot access a property or method of a null object reference.
     at com.adobe.rtc.pods::SharedWhiteBoard/subscribe()[/Users/arun/Work/aponnusa_theoden.corp.adobe.com_1666/depot/branches/connect/1106/cocomoPlayer10/src/com/adobe/rtc/pods/SharedWhiteBoard.as:191]
     at StaffApp/addPods()[C:\Documents and Settings\Matt\Adobe Flash Builder 4\StaffApp\src\StaffApp.mxml:109]
     at StaffApp/onCreationComplete()[C:\Documents and Settings\Matt\Adobe Flash Builder 4\StaffApp\src\StaffApp.mxml:88]
     at StaffApp/__cSession_creationComplete()[C:\Documents and Settings\Matt\Adobe Flash Builder 4\StaffApp\src\StaffApp.mxml:319]
     at flash.events::EventDispatcher/dispatchEventFunction()
     at flash.events::EventDispatcher/dispatchEvent()
     at mx.core::UIComponent/dispatchEvent()[E:\dev\hero_private\frameworks\projects\framework\src\mx\core\UIComponent.as:13128]
     at mx.core::UIComponent/set initialized()[E:\dev\hero_private\frameworks\projects\framework\src\mx\core\UIComponent.as:1818]
     at mx.managers::LayoutManager/doPhasedInstantiation()[E:\dev\hero_private\frameworks\projects\framework\src\mx\managers\LayoutManager.as:842]
     at mx.managers::LayoutManager/doPhasedInstantiationCallback()[E:\dev\hero_private\frameworks\projects\framework\src\mx\managers\LayoutManager.as:1180]

All the relevant inport statements/code appears to be there!

Besides that it's working awesome!  Hopefully this thread will help other users who are trying to implement the KnockingQueue in a slightly more complex environment than the example app.

Matt

Avatar

Employee

Hi Matt,

Changing your code from

var wbPod:SharedWhiteBoard = new SharedWhiteBoard();
wbPod.width = wbPod.height = 500;
wbPod.connectSession = cSession;
wbPod.sharedID = "wbPod";
wbPod.subscribe();
wbTab.addElement(wbPod);

to this should fix your issue

var wbPod:SharedWhiteBoard = new SharedWhiteBoard();
wbPod.width = wbPod.height = 500;
wbPod.connectSession = cSession;
wbPod.sharedID = "wbPod";
wbTab.addElement(wbPod);
wbPod.subscribe();

The fix is - you subscribe after you add the pod to the tab. This step ensure all the whiteboard components are created.

Thanks

Arun

Avatar

Former Community Member

Arun,

I just made the change and it works great!

Thank you very much!

Matt

Avatar

Former Community Member

Hi Arun,

Thanks again for the awesome help!  I have uploaded both apps. to my web server and have implemented external authentication for the staff app. (I got this to work without to much difficulty).  However, I am having one last problem which I'm not able to solve.

Both apps. log in fine, but when I test using the Flash Debug Player, I instantly get one of the following 2 error messages:

TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at com.adobe.rtc.sharedManagers::RoomManager/set guestsHaveToKnock()
    at StaffApp/onCreationComplete()
    at StaffApp/____StaffApp_Application1_applicationComplete()
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at mx.core::UIComponent/dispatchEvent()
    at mx.managers::SystemManager/preloader_preloaderDoneHandler()
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at mx.preloaders::Preloader/displayClassCompleteHandler()


Error: There are users waiting to knock. Please add event listener to knocking Queue in RoomManagerand handle it to accept or deny users
    at com.adobe.rtc.sharedManagers::RoomManager/onKnockingQueueUpdate()
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at com.adobe.rtc.sharedModel::UserQueue/onItemReceive()
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at com.adobe.rtc.sharedModel::CollectionNode/http://www.adobe.com/2006/connect/cocomo/messaging/internal::receiveItem()
    at com.adobe.rtc.messaging.manager::MessageManager/http://www.adobe.com/2006/connect/cocomo/messaging/internal::receiveItem()
    at com.adobe.rtc.session.managers::SessionManagerBase/receiveItem()


When I was doing all the testing locally, I occasionally got these errors, but everything usually worked fine.  Now that everything is on the server, I get the error(s) everytime I load the app through the browser.  The client app. doesn't generate any errors and appears to work fine, however, the user name doesn't appear in the pending list of the StaffApp.  Also, if I dismiss the errors, the StaffApp will run, but the note and whiteboard pods don't load.

I'm thinking this has something to do with the creationComplete() function in the StaffApp and the fact that the knocking list is being accessed before login to the LCCS service has actually finished.  What do you think?  Here is my creationComplete() function:

            private function onCreationComplete():void
            {
                roomURL = FlexGlobals.topLevelApplication.parameters["roomURL"];
                authToken = FlexGlobals.topLevelApplication.parameters["authToken"];
                cSession.login();



                // Require that guests must knock.Note that this line changes the setting of your room so that in future anyone guest coming in
                // has to knock. You can change this setting again either from code by making roomManager.guestsHaveToKnock as false or from the DevConsole's manage tab
                cSession.roomManager.guestsHaveToKnock = true ;
               
                // For owners that arrive late, the pending queue is populated with waiting users.
                if ( cSession.userManager.myUserRole == UserRoles.OWNER ) {
                   
                    //  var pendingArray:Array = new Array();
                    var pendingArray:ArrayList = new ArrayList();
                    var queue:Array = cSession.roomManager.knockingQueue.pendingQueue ;
                    for ( var i:int = 0 ; i < queue.length ; i++ ) {
                        var item:UserQueueItem = queue[i] as UserQueueItem ;
                        pendingArray.addItem({label:item.descriptor.displayName,descriptor:item.descriptor});
                    }
                    knockingList.dataProvider = pendingArray ;
                    cSession.roomManager.knockingQueue.addEventListener(UserQueueEvent.ITEM_UPDATE,onKnockingQueueUpdate);
                    cSession.roomManager.knockingQueue.roleForManaging =  UserRoles.PUBLISHER;
                    addPods();
                }else {
                    //Else, remove all UI from seeing.
                    knockingUI.removeAllChildren() ;
                }               
            }

The StaffApp is logging in with the role set to "100", so that shouldn't be the problem.

I am trying to set up a demo for next week, so I have to get this worked out soon!

Thanks again,

Matt

Avatar

Former Community Member

I've spent some time over past few days going over many of the examples again to see if I could get some insight into the latest problem (above).  I'm really starting to get a good grasp of how it all works, especially the flow of events when a user is login into the service!  The dificult part is knowing what exactly to do where.

It seems that the best way to make sure all the pods and collection nodes sync up to the service without throwing errors is to listen for the "SessionEvent.SYNCHRONIZATION_CHANGE" event and then instantiate them within the handler function for it.  So I should do something like the following:

private function onCreationComplete():void

{

     roomURL = FlexGlobals.topLevelApplication.parameters["roomURL"];

     authToken = FlexGlobals.topLevelApplication.parameters["authToken"];

     cSession.Login();

     lccsConnectSession.addEventListener(SessionEvent.SYNCHRONIZATION_CHANGE,onSync_Handler);

}

private function onSync_Handler(event:SessionEvent):void

{

     cSession.roommanager.guestsHaveToKnock = true;

     if(cSession.userManager.myUserRole == UserRoles.OWNER)

     {

          // Knocking queue logic

     }

}

or, should I listen for the "AuthenticationEvent.AUTHENTICATION_SUCCESS" event?

Am I going about this the right way?  If so, which one is better?

Thanks,

Matt