The Chat System


The chat system will have a user interface that interacts with the server through a controller CFC. All the Chat Center UI calls are passed to the server through this controller. The CFC allows for one entry point that controls the systems responses. Any additions to the system can then also be controlled through this CFC.

Testing

Testing should be implemented early in the development process so that you can identify any problems that could evolve into major challenges if left unchecked. TSC finds that testing smaller components during the development will greatly reduce the chances of some coding errors that may arise when components interact with other components. The collaboration of components may also identify challenges that have not been detected beforehand.

TSC has also discovered that the product must be tested in all environments to ensure that it will be delivered successfully.

The chat-system development consists of creating three user interfaces. The first allows the user to change his user name, log in, and view the current users and chat rooms online. The second user interface displays an individual chat dialog box with the functionality to send and receive messages. The third allows for the administration of user accounts.

TSC will be developing the client-side user interface in Flash MX, which gives the ability for the client-side application to interact with both the FCS and CFMX server.

Flash Components

The Flash components used were the PushButton, TextField, and ListBox. I will not go into these components, as they've been covered in the previous chapter.

Local Shared objects on the clientside have also been discussed and demonstrated in previous chapters, so discussing them here would not be beneficial. However, the server-side shared object gives the FCS the ability to synchronize data to all clients that are connected to a particular instance of the object. The FCS manages this transfer of data itself. A remote shared-object function can modify the refresh rate at which it synchronizes with the server. This command is the setFps method. It takes a parameter that specifies how many updates are required per second. This method is used to control the amount of traffic that the server receives.

Shared objects can be local (clientside) or remote (serverside). The local shared object can hold persistent data on the client side for writing and retrieval only by itself or other Flash MX applications from the same domain. These local objects are similar in functionality to Web-browser cookies but are more powerful.

ColdFusion Components

To create CFCs, related functions are grouped into a file or component. These are in turn packaged (in the same directory) together with other related CFCs. The CFC gets its name from where you store the file. To restrict access to components, you can place them all in the same directory, which is a package. The directory structure au/com/tatam/chat can be represented by au.com.tatam.chat. Once the components are in the same directory, the attribute access="package" only allows method calls from CFCs within the same directory (or package).

There are four straightforward component tags that can make up a CFC: cfcomponent, cfproperty, cffunction and cfargument.

instant message

Packaging groups similar or related CF's into a directory or package. Methods can be restricted to the current package if desired.


Inheriting allows not only for using the methods of the parent component but also for creating additional methods within the child component. This means that all code that accesses the parent component can also access the child.

Access specifiers:

Public: Available to any calling code.

Private: Access is restricted to calls from code within the same CFC.

Package: Accessible from within the same package (directory).

Remote: Accessible from external systems such as Web services and Flash MX.

As we have already seen, CFCs have much more power than just organizing the code into relevant components. Remote invocation can broaden the CFC's functionality beyond the ColdFusion MX server to other services such as Flash MX.

Remote access to components is allowed through the <cffunction> tag's remote attribute. As you have seen in previous chapters, this enables a Flash MX system to call a server-side component and invoke a method, and then receive the result. The following is an example of creating a log-in object in ActionScript, and then calling a server CFC and invoking the setNewSession method.

 //Theresultfromthecfcomponentau.com.tatam.chatwillbe passedtothismethodasanobject. //CreateaLogin_objectobjecttosendtotheCFcomponent. //TheObjectpropertieswillbereceivedasargumentstothe Server-sidecomponentsfunction varLogin_object=newObject(); Login_object.userID=Username; Login_object.password=Password; Login_object.status="Online"; login_object.datasourceName="chat"; login_object.datasourceType="ODBC"; Login_object.message="Iwanttologin"; chatserverSession= this.chat_conn.getService("au.com.tatam.chat.Controller", this); status_Window.text=status_Window.Text+chr(10)+ "connectingtoserver..."; chatserverSession.setNewSession(Login_object); this.onResult=function(result)  } this.onStatus=function(result)  } 

The methods in this component are wrappers around other components which perform all the actual processing. Each method passes and receives arguments to those other components, and then returns any returned data.

Controller.cfc

The controller is used to direct the traffic of calls to the specific CFC and method. This CFC is called from within the Flash MX client-side Chat center user interface.

 <cfcomponentname="Controller"hint="controlstheChatCenter"> <cffunctionname="setNewSession"access="remote"> <cfinvokecomponent="au.com.tatam.chat.session.Session"    method="setNewSession"    argumentCollection="#ARGUMENTS#"    returnVariable="result"/> <cfreturnresult/> </cffunction> <cffunctionname="CreateChatRequest"access="remote"> <cfinvokecomponent="au.com.tatam.chat.dialog.Request"    method="CreateChatRequest"    argumentCollection="#ARGUMENTS#"    returnVariable="result"/> <cfreturnresult/> </cffunction> <cffunctionname="getSpecificUserSessionID"access="remote"> <cfinvokecomponent="au.com.tatam.chat.session.Session"   method="getSpecificUserSessionID"    argumentCollection="#ARGUMENTS#"    returnVariable="result"/> <cfreturnresult/> </cffunction> </cfcomponent> 

If a new version of the Chat were to be produced, then within these methods there could exist a statement that could detect the newer version and then redirect the call to another CFC and method.

 <cffunctionname="getSpecificUserSessionID"access="remote"> <cfifparameterexists(arguments.version)ANDarguments.  versionEQ"1"> <cfinvoke  component="au.com.tatam.chat.session.VideoSession"    method="getSpecificUserSessionID"    argumentCollection="#ARGUMENTS#"    returnVariable="result"/> <cfreturnresult/> <cfelse> <cfinvoke component="au.com.tatam.chat.session.Session"    method="getSpecificUserSessionID"    argumentCollection="#ARGUMENTS#"    returnVariable="result"/> <cfreturnresult/> </cfcif> </cffunction> 
The Database

The database schema at design time had a lot more tables defined (Figure IV-3.1). This was due to the fact that the data was to be stored within a database. With the introduction of the Flash Communication Server, some of the tables in the design document have been replaced with server-side shared objects. The database's role was diminished at the time the FCS was implemented into the project. The data still must be stored, but with the establishment of the FCS, its mode of storage and fields have now changed.

Figure IV-3.1. Initial Database schema.

graphics/04fig08.gif

The Users table can be reused with the user CFC. The present table exists within the chat database but could exist elsewhere if the data needs to be shared by other systems.

The user sessions and chat requests were initially a large part of the design. However, with the introduction of the FCS this data is apparently redundant, as most of the information is duplicated within the server-side shared object.

Session.cfc

The session CFC holds all the logic to get, set, process, and return the data associated with a user's chat session. It accesses the database as well as calling the User CFC to authenticate the user ID and password.

 <cffunctionname="setNewSession"access="remote">  <!---Setuptheargumentsforthefunction---> <cfargumentname="userID"required="true"/> <cfargumentname="password"required="true"/> <cfparamname="UserAuthenticated"default="0"/> <cfsetst_Return=StructNew()/> <cfsetst_Return.userID=ARGUMENTS.userID/> <cfinvoke  component="au.com.tatam.User.User"  method="Authenticate"  datasourceName="#ARGUMENTS.datasourcename#"  datasourceType="#ARGUMENTS.datasourcetype#" returnVariable="UserAuthenticated"> <cfinvokeargument name="UserName"  value="#ARGUMENTS.userID#"> <cfinvokeargument    name="password"    value="#ARGUMENTS.password#"> </cfinvoke> <cfifUserAuthenticated> <cfsetst_Return.sessionid=createUUID()/> <!---DefaultexpiryTimeissetinDBasNow+1hr---> <cfquerydatasource="#DSN_Session#"    dbtype="#DSN_    SessionDBType#"    name="qry_DeleteOldSessions">    DELETE*FROMtblUserSessions    Whereusername='#ARGUMENTS.userid#' </cfquery> <cfquerydatasource="#DSN_Session#"    dbtype="#DSN_SessionDBType#"    name="qry_InsertNewSession">    INSERTINTOtblUserSessions    (UserSessionUUID,username)    VALUES    ('#st_Return.sessionid#','#ARGUMENTS.userID#') </cfquery> <cfelse> <cfsetst_Return.sessionid=0/> </cfif> <cfreturnst_Return/> </cffunction> 

Michelle did research on CFCs and their use, and presented some thoughts at the next developer's meeting:

graphics/04fig08a.gif

graphics/04fig08b.gif

Versioning and Backing Up

TSC developers always create a customizable automated process that backs up files that are currently being worked upon. Versioning software is the ideal solution; however, with its small developer team, TSC finds that the reality of cost far outweighs its advantages. To overcome this, the developers work on separate components and use a batch file to automate backups. This batch file is run through the CFMX scheduling feature.

A TSC junior developer who has learned that code copies are of extreme benefit when major problems arise, uses manual versioning. The usage of numbers on the file name (Figure IV-3.2) allows the developer to determine the previous saved code. The directory acts as a local and convenient area to store and retrieve code versions in an environment without a commercial versioning system.

Figure IV-3.2. This is the junior developer's code directory.

graphics/04fig09.gif

User-Account Administration

The user-account administration screen was developed completely in ColdFusion and used a generic user CFC (Figure IV-3.3). It will be accessible from any browser and will give the ability to change a user's account details. This interface will satisfy the project's first requirement.

Figure IV-3.3. The user-account-administration screen allows for adding, editing and deleting chat user accounts.

graphics/04fig10.gif

The user-account administration uses the User.cfc file to get and set its variables. The interface is very basic, listing the users and then giving the opportunity to edit or delete a current user, and to add users to the system. This account management can be reused elsewhere and was designed and coded in a manner to allow for this.

All the user properties have set and get methods, making the calls very generic and thus hiding the details of how the data is actually updated or extracted. This CFC can be extended or reused in other applications very easily.

The cfm file uses a self-posting form. This allows for all the code for the edit and update functionality to be held in one file. It also allows for the code to be portable.

Displaying users online uses the database to deliver the information required.

To update modified data, the edit form is self-posting. When modified data is submitted, a <cfloop> Tag is used to loop over the submitted form fields and calls the <cfinvoke> tag to update the details.

 <cfloop.....................> <cfinvokecomponent="au.com.tatam.user.user" method="set#form[currentformfield]#" datasourcename="#ARGUMENTS.datasourceName#"  datasourceType="#ARGUMENTS.datasourceType#"> <cfargumentname="#currentformfield#" value="#form[currentformfield]#"/> </cfinvoke> </cfloop> 

This code will only work if the set method's naming is constant on all the fields that can be updated. That is, if a field is userID, then the set method will be setUserID.

Chat-Center Development

The second requirement outlined the process of logging in with a user name and password (Figure IV-3.4). CFCs are used for authentication and the creation of a session ID. Flash is used for the client-side user interface and server-side shared object. After authentication, a user can request to chat with other users online or participate in a chat room. If a user accepts an invitation to chat, then the individual chat user interface is displayed in a new window (Figure IV-3.5).

Figure IV-3.4. The chat-center log-in screen.

graphics/04fig11.gif

Figure IV-3.5. The chat-center user interface.

graphics/04fig12.gif

This request of and response to a chat invitation is controlled by the Flash Communication Server, which will be discussed in more detail later in the chapter.

Logging out of a session is also the responsibility of this screen, which calls a CFC to terminate the session.

The chat center needs both server-side CFCs and client-side Flash interface to provide a solution. The first step in the development required a frame that would set and display a user name as well as providing a password box and a button to submit the details for authentication.

graphics/04fig12a.gif

Storage of persistent data needed to be written to the local file system. Flash MX provides such a feature called the shared object.

Remote shared objects can be thought of as a real-time system for data storage and transfer.

graphics/04fig12h.gif

There is a security feature that is part of the system object that can grant other domain(s) access to a local shared object. The syntax is as follows:

 System.security.allowDomain("tatam.com.au", "anotherdomain.com",.......);  

For further security-related information, please visit the Flash MX Web site (www.macromedia.com/software/flash/).

Access cannot be revoked once it is given.

Security Concerns

All security-sensitive data will be loaded at run time from the server. The only risk is that the local file system could be compromised and the user ID that is stored could be discovered. TSC has a policy of not hard-coding any values that may compromise the client security. However, be aware that data still needs to be passed from client to server and that the transfer mechanism may need to be addressed (Secure Socket Layer). In this case it has been brought to the client's attention and no further action is required. The hosting environment also needs to be addressed. This means both the physical security as well as electronic security. Security compromises may be stopped remotely, but you must also consider the scenario of someone's breaking into the premises where the server is physically hosted. TSC is proactive in its security and constantly refers to the Macromedia site as well as other resources to be aware of any security issues.

Silicon Coast was satisfied with the proactive stance that TSC had taken to identify any concerns that may have arisen later.

The Flash user-interface construction had begun. A number of different layers needed to be produced.

Through experience, TSC has realized that due to the many components and objects in Flash, it can be difficult to keep track of the code. Debugging can become a nightmare if some basic conventions are not adhered to. As a rule, most if not all ActionScript code is placed within one layer, which is named ActionScript. Ideally, but not always, the first frame houses all the code.

instant message

Layers can be either ActionScript holders or transparent art work sheets. They can be stacked upon each other to produce a desired frame or frames.


This centralizing of data also may create problems in that the code can become unmanageable as it grows. This gives rise to the #include statement. It enables the developer to group reusable code into one or more files that can then be reused. This technique also makes it possible for object code to be encapsulated in the one file. TSC realizes the power of this feature and has developed extensive generic code libraries of objects and functionality that can then allow for future rapid development of systems with similar functionality. These files are named according to their functionality, so that their use can be determined by their filenames.

The following code gets the local shared object and creates a local-user object. The name of the constructor is obviously the name of the class. The constructor for the ChatLocalUser class takes the local shared object and places it in the object's UserDetails attribute:

 _global.currentChatLocalUser=newChatLocalUser (SharedObject.getLocal("ChatUser"));  

The constructor for the ChatLocalUser class is as follows:

 ChatLocalUser=function(UserDetails)  {  this.Userdetails=UserDetails; } 

The events in the first frame are associated with the button that changes and stores the user name, and the button that logs in the user.

graphics/04fig12b.gif

NOTE Objects are created by calling their constructor with the new statement. This new call instantiates an instance of the class called an object. Constructors may have arguments that allow for the initialization of the newly created object.

Objects can be created in ActionScript. TSC uses a separate file for all its objects and then includes them in the code. When creating objects, you must be aware of the way in which memory is used. If a function of the object is defined in its constructor, then for every instance of the object that is created an instance of that function exists. To overcome this, an ActionScript object has a prototype property that is created automatically when you define the function. This allows not only functions but also properties to be shared across all instances. Each new instance then has a __proto__ property that can be referenced from within the instance.

Here is a code snippet from the ChatLocalUser object (ChatLocalUser.as file).

 ChatLocalUser.prototype.getUserID=function()  { return(this.Userdetails.data.UserID); } 

TSC does not attach code to movie clips or buttons. Having the code within a central location is all that is needed in the Flash system. Events then trigger the code to be executed.

graphics/04fig12c.gif

For your reference, I've included Button events and related methods (Table IV-3.1) and Movie-clip events and related methods (Table IV-3.2).

Table IV-3.1. Button events and related methods
Event Method
on (rollover) onRollOver
on(rollOut) onRollOut
on(dragover) onDragOver
on(dragOut) onDragOut
on(press) onPress
on(release) onRelease
on(keyPress "ENTER") onKeyDown, onKeyUp

Table IV-3.2. Movie-clip events and related methods
Event Method
onClipEvent (load) onLoad
onClipEvent (unload) onUnload
onClipEvent (enterFrame) onEnterFrame
onClipEvent (keyDown) onKeyDown
onClipEvent (keyUp) onKeyUp
onClipEvent (mouseDown) onMouseDown
onClipEvent (mouseUp) onMouseUp
onClipEvent (mouseMove) onMouseMove
onClipEvent (data) onData

The on(press) event is used to call the functions setUserID() and login().

graphics/04fig12d.gif

The setUserID() function is called on the localUser object, which in turn writes to the local shared object the passed userID. The following code is a snippet that sets the local shared object:

 ChatLocalUser.prototype.setUserID=function(strName)   this.Userdetails.data.UserID=strName;  //flushingthesharedobjectsavestheinfo'now'ratherthan savingonclose.  this.Userdetails.flush(); } 

The use of the flush() function enables Flash to write all memory data to the local shared object.

The login function does exactly that. It creates a log-in object that gets sent to the server, which consists of a user name and password. To communicate with the server, a NetConnection object needs to be created, and a CFC method needs to be called.

The NetConnection class allows the developer to create a duplex connection between the server and the client-side. When instantiated in the Flash MX code, the NetConnection object creates a TCP socket that permits the nonstop exchange of Real-Time Messaging Protocol (RTMP) encoded data.

graphics/04fig12e.gif

The NetConnection object's main method is the connect method. This method must take the targetURI parameter. This parameter is the Uniform resource identifier (URI) of the Flash Communication Server application instance.

To create a remote shared object, you must first create the NetConnection object. The call the connect method on this NetConnection object.

 IndividualChat_nc=newNetConnection();  IndividualChat_nc.connect("rtmp:/chat/Individual", _global.userID); IndividualChat_nc.onStatus=function(info){ } IndivChatSession_so=SharedObject.getRemote(_global.IndivSessionID, IndividualChat_nc.uri,false); IndivChatSession_so.connect(IndividualChat_nc); 

The NetConnection.onStatus event is invoked on the NetConnection's .connect method call. On invocation, this onStatus method receives an object that provides information regarding the connection that was attempted. If the connection is successful, the isConnected property is set to true.

While your computer is attempting to connect to the FCS server, all data that uses this connection is queued until the connection is successful.

graphics/04fig12f.gif

The chat application has three application instances:

  • Chat/usersonline Holds the current online users.

  • Chat/General Holds the individual logged-in user sessions within their own unique shared objects.

  • Chat/individualChat Manages the shared objects for the individual chat dialogs.

Within the chat/general instance are the individual session instances. These objects are named as the UUIDs that are returned by the setNewSession method of the user CFC.

graphics/04fig12g.gif

 ChatServer_nc=newNetConnection();  ChatServer_nc.connect("rtmp:/chat/users", _global.currentChatLocalUser.getUserID()); 

The .connect function opens up a TCP socket connection on the Flash server. This connection then exchanges real-time data through the use of the RTMP. The RTMP encodes the data that is streamed between client and server.

This is the code from the ChatServer object (ChatServer.as file):

 ChatServer.prototype.login=function(LocalSessionObject,status_Window,Username,Password)  { varLogin_object=newObject();  Login_object.userID=Username;  Login_object.password=Password;  Login_object.status="Online";  login_object.datasourceName="chat";  login_object.datasourceType="ODBC";  Login_object.message="Iwanttologin";  chatserverSession= this.chat_conn.getService("au.com.tatam.chat.Controller", this);  status_Window.text=status_Window.Text+chr(10)+ "connectingtoserver...";  chatserverSession.setNewSession(Login_object);  this.onResult=function(result)  {   status_Window.text=status_Window.Text+chr(10)+ "..Retrieveddata..."+chr(10)+"session="+result.sessionid;  LocalSessionObject.data.SessionID=result.sessionid;  LocalSessionObject.data.userID=result.userID;  LocalSessionObject.flush();  if(result.sessionid!=0)  {  //createaremotesharedobjectforUsersessionaswellrequests   this.createSessionRemoteSharedObject(result.sessionid, Username);   gotoAndStop(2);  } }  this.onStatus=function(result)  {   trace(result.description);   status_Window.text=status_Window.Text+chr(10)+"Error"+ result.description; }   } 

The createSessionRemoteSharedObject creates a shared object, using the UUID of the session as its name:

 ChatServer.prototype.createSessionRemoteSharedObject=function(SessionID,UserID)  {  newSession_nc=newNetConnection();  newSession_nc.connect("rtmp:/chat/General",UserID);  newSession_nc.onStatus=function(info)  {   trace(info.description);  }  generalSession_so=SharedObject.getRemote(SessionID, newSession_nc.uri,false);  generalSession_so.connect(newSession_nc);  generalSession_so.setFps(2);  generalSession_so.data.request.Session;  generalSession_so.onSync=function(list){.............} } 

Figure IV-3.6 shows the App Inspector.

Figure IV-3.6. This is the App Inspector with two user sessions under chat/General.

graphics/04fig13.gif

User.cfc

The User.cfc file manages the user details. Both the chat center and the user-account-administration sections use this CFC. It has also been coded generically, allowing for its reuse or extension in other solutions TSC may deliver.

The first line of code after the comments is the cfcomponent tag.

This tag takes the Name attribute. Name: is the name of the component:

 <cfcomponentName="User">  .......... <cffunctionname="createUserTable"access="remote"> ............. </cffunction> </cfcomponent ><!---thenendofthecomponent---> 

The method createUserTable called is defined with the <cffunction> tag. This tag takes the following attributes:

  • Name: Defines the name of the function (and method) that can be called to invoke the code.

  • Access: In this case it is remote, as Flash is calling it remotely.

  • Output: This is 0, as only data will be returned and no HTML is required to be produced. (HTML is more the View segment, and CFCs are usually the Model section).

Within the <cffunction> tag there is the <cfargument> tag. This tag helps define the purpose of the argument as well as throwing an error if the argument was not passed (Required attribute):

 <cfargumentNAME="DatasourceName"required="yes"/>  <cfargumentNAME="DatasourceType"required="yes"/> 
  • Name: Defines the name of the argument.

  • Type: Defines the data type. If the data type passed is not correct, then CFMX will throw an exception. This will be returned to the Flash App, and the .onStatus function will be invoked.

  • Required: This is set to yes, which means the argument must be supplied.

The CFC receives the data in its argument's scope. It then calls another CFC with the invoke method, to query the database:

 <cfinvoke  

This component call needs the following attributes to execute:

  • Component: The name of the component with the method that is desired to call for example, "au.com.tatam.User.User". This looks for the User.cfc file under the directory au/com/tatam/User.

  • Method: The name of the method that is, the CFFUNCTIONname=''.

  • ReturnVariable: The name of the variable that the result is put in from the <cfreturn tag within the cffunction tag.

graphics/04fig13a.gif

The following is a mixture of arguments that are within the cfinvoke tag as well as that use the <cfinvokeargument> tag:

 <cffunctionname="setNewSession"access="remote">  <!---Setuptheargumentsforthefunction---> <cfargumentname="userID"required="true"/> <cfargumentname="password"required="true"/> <cfparamname="UserAuthenticated"default="0"/> <cfsetst_Return=StructNew()/> <cfsetst_Return.userID=ARGUMENTS.userID/> <cfinvokecomponent="au.com.tatam.User.User"    method="Authenticate"    datasourcename="#ARGUMENTS.datasourcename#"    DATASOURCETYPE="#ARGUMENTS.datasourceType#" returnVariable="UserAuthenticated"> <cfinvokeargument     name="UserName"     value="#ARGUMENTS.userID#"/> <cfinvokeargument     name="password"     value="#ARGUMENTS.password#"/> </cfinvoke> 

If the user is authenticated, then a new UUID is sent back down to the Flash client; if not, an integer (0) will be returned:

graphics/04fig13b.gif

 <cfreturn  <cfsetst_Return=StructNew()/> <cfsetst_Return.userID=arguments.userID/> <cfifUserAuthenticated> <cfsetst_Return.sessionid=createUUID()/> <cfelse> <cfsetst_Return.sessionid=0/> </cfif> <cfreturnst_Return/> 

Once the result is returned, the onResult() function is called and the local shared object is updated with the sessionID. If the sessionID is not 0, then the movie moves to the next frame.

 this.onResult=function(result)  {  status_Window.text=status_Window.Text+chr(10)+ "..Retrieveddata..."+chr(10)+"session="+result.sessionid;  LocalSessionObject.data.SessionID=result.sessionid;  LocalSessionObject.data.userID=result.userID;  LocalSessionObject.flush();  if(result.sessionid!=0)  {   //createaremotesharedobjectforUsersessionaswell requests  this.createSessionRemoteSharedObject(result.sessionid, Username);   gotoAndStop(2);  } } 

For the log-in to be a success, a sessionID must be returned on the correct authentication.

GotoAndStop() is used to proceed to the chatcenter UI, once the user has been authenticated. This function moves the focus to the next frame in the Flash User Interface.

The next frame displays the current users, chat rooms online, and current chat invitations.

This data is populated through the use of a number of server-side shared objects. These objects allow for real-time data transfer of video, audio, and text.

Flash Communication Server

The Flash Communication Server enables you to store an object on the server side. This object can then be accessible by the FCS or any system that can connect to the FCS. If there the object's state changes, the altered data is available to the clients in real time. This scenario is ideal for the chat system. Both chat rooms and individual chat sessions can use this technology.

If the client is not connected to the server, then the next time the client is connected to the object, the data that has been changed is updated.

graphics/04fig13c.gif

As this server is a new concept for TSC, the developers needed to research how the management of the shared object's server side was to be implemented. They researched issues such as synchronization and locking, and whether the object would be persistent.

The developers have identified that storage of data on the server-side shared object is the same as that on the client side. It is stored in the object's data property. Within this property, each set of subproperties constitutes an attribute. Each time a client changes one of these attributes (or its sub-attributes), it is sent to the server. So if the attribute so.data.users.matt.requests has an attribute of Amanda added, then the whole so.data.users will be updated (but not so.data.chatrooms) and synchronized to other clients. Network traffic must be considered when designing a remote shared object, as these updates will consume bandwidth.

The so.getRemote function returns a reference to an object that is specified in the first parameter of the function call. This getRemote function takes two required parameters:

  • The first is the name of the object that the variable will point to.

  • The second is the Universal Resource Identifier (URI), which must be identical to the URI of the NetConnection object that was declared when a NetConnection object was instantiated.

The final optional parameter is the persistence parameter. This specifies whether the object will be persistent. If it is deemed persistent, then it will be stored in the FlashCom directory. If Flash cannot find the file, then it creates it. If it can't create it, then a null is returned.

The Flash Communication Server directory is specified in the vhost.xml configuration file under the <AppsDir> element. This directory must have a Chat subdirectory for connection using the connect method on the NetConnection object.

The so.connect method is then called.

graphics/04fig13d.gif

The remote shared object's connect method is used to connect to a remote shared object. There must be a connection to the FCS before a server-side shared object can be accessed. So the connect method must take the parameter of a NetConnectionobject. This method returns a true value if there was a successful connection. Once successful reference has been established, the .onsync event is invoked. The .onsync event handler should be defined so that the application receives notification of a change's failure or success.

NOTE If two different movies located in the same directory create objects with identical names, they can overwrite each other without warning. On the other hand, movies in different directories in the same domain can read and write each other's shared objects.

 ChatServer_nc.onStatus=function(info)  {  trace(info.description); } Usersonline_so=SharedObject.getRemote("Usersonline", ChatServer_nc.uri,false); Usersonline_so.connect(ChatServer_nc); Usersonline_so.data[_global.currentChatLocalUser.getUserI D()]="online"; 

The onsync method receives an array of objects when it is invoked. This array of objects has properties that represent the server-side shared object's modifications. As previously mentioned, it is invoked on connection as well as when there is a change to the server-side shared object.

  • The name property in the array element contains the name of the server-side SharedObject property that has been modified.

  • The oldValue property contains the value that existed before the property was updated to a different value.

 Usersonline_so.onSync=function(list){  for(vari=0;i<list.length;i++) if(Usersonline_so.data[list[i].name]=="online")  {   _root.UsersOnline_lstBx.Additem(list[i].name);  }  elseif(Usersonline_so.data[list[i].name]=="offline")  { for(varofflinecountr=0;offlinecountr< _root.UsersOnline_lstBx.getlength();offlinecountr++)   { if(_root.UsersOnline_lstBx.getItemAt(offlinecountr).label ==list[i].name)   {  _root.UsersOnline_lstBx.removeItemAt(offlinecountr);   }   }  } }; 
Logging Out

A request is sent to the server to log out. This deletes the session row from the User Session table and then notifies the user of the disconnection.

The session's termination can then be tested by checking for its presence in the User session table.

Request a Chat Process

The development team had a number of different options available for this invitation process.

Initially, before the FCS was accepted, the database was to be used to store requests. An ActionScript setInterval method was to be used to poll the server for new requests.

With the introduction of the FCS, the development team needed to research other possible techniques that could lever off the new technology.

The team decided that as the chat/general instance held a shared object related to the current session, it would be possible to use a requests slot to hold incoming and outgoing requests.

The createChatRequest method was declared as a prototype function. It calls the createChatRequest method in the dialog.request CFC. The returned value of the createChatRequest function is a RequestUUID. This value can be accessed as a property of the returned object. This returned object is the result variable that is sent to the onResult function when it is invoked.

 this.onResult=function(result)  

The returned value is the result.requestUUID variable.

 ChatInvitation.prototype.createChatRequest=function(userID, Responder,SessionID)   {   NetServices.setDefaultGatewayUrlchatServerGatewayAddress);   this.request_conn= NetServices.createGatewayConnection();   varRequest_object=newObject();   Request_object.RequestorUserID=userID;   Request_object.ResponderUserID=Responder;   varchatserverRequests=this.request_conn.getService  ("au.com.tatam.chat.Controller",this); //callstheCFCau.com.tatam.chat.Controllerandcallsthe methodcreateChatRequest  chatserverRequests.createChatRequest(Request_object);   this.onResult=function(result)   {  this.addRequestToGeneralSessionSharedObject(SessionID, userID,result.requestUUID,Responder);   }   this.onStatus=function(result)   {    trace("inonstatus"+result.description);   }   } 

The createChatRequest method called through the controller is as follows:

 <cffunctionname="createChatRequest"access="remote">  <cfargumentname="RequestorUserID"Required="Yes"/> <cfargumentname="ResponderUserID"required="yes"/> <cfsetrequestUUID=createUUID()/> <cfquerydatasource="#DSN_Session#" dbtype="#DSN_SessionDBType#"name="qry_addChatREquest">   INSERTINTOtblChatRequest   (ChatRequestUUID,ChatRequestorUserID, ChatResponderUserID)   VALUES   ('#requestUUID#','#ARGUMENTS.RequestorUserID#', '#ARGUMENTS.ResponderUserID#') </cfquery> <cfsetst_Return=StructNew()/> <cfsetst_Return.requestUUID=requestUUID/> <cfreturnst_Return/> </cffunction> 

The addRequestToGeneralSessionSharedObjectfunction is called once a RequestUUID has been returned.

 this.addRequestToGeneralSessionSharedObject(SessionID, userID,result.requestUUID,Responder);  

When a user requests a chat with another particular user (the responder), the invitation is updated on the responder's server-side shared object. This immediately invokes the onsync event. Within the onsync method the code notifies the user of a request to chat (Figure IV-3.7).

Figure IV-3.7. The Chat Invitation dialog screen.

graphics/04fig14.gif

If the request is accepted, a new window is created that sends the SessionID and other parameters on the URL string to the Chatcontroller.cfm file.

Individual Chat Development

The Chatcontroller.cfm file uses ColdFusion to populate the sessionID and other variables into the JavaScript code. It also populates the JavaScript function that is called from within the .swf Flash MX file. The skeleton JavaScript code is created when Flash MX publishes the .swf file.

NOTE The HTML publish setting needs to be set to Flash with FSCommand. This can be accessed under the menu FilePublishSettings option then select the HTML tab and scroll down the Template listbox to select the flashwithFSCommand.

Chatcontroller.cfm
 <cfifparameterexists(url.sessionid)>  <cfsetChatSessionID=url.sessionid/> <cfsetusername=url.username/> <cfsetrequestor=url.requestor/> 

The Chatcontroller.cfm and .swf files have the ability to be re-used in other applications that may require similar chat functionality. By placing URL variables on the URL request, the system can also be a stand-alone chat application.

 <HTML>  <HEAD> <metahttp-equiv=Content-Typecontent="text/html; charset=ISO-8859-1"> <TITLE>individualChatUI</TITLE> </HEAD> <BODYbgcolor="#D4D0C8"> <SCRIPTLANGUAGE=JavaScript> <!--  varInternetExplorer=navigator.appName.indexOf  ("Microsoft")!=-1;  //HandlealltheFSCommandmessagesinaFlashmovie  functionindividualChatUI_DoFSCommand(command,args){   varindividualChatUIObj=InternetExplorer?individualChatUI:document.individualChatUI;   //   //Placeyourcodehere...   //   if(command=="getChatSessionID")   { <cfoutput>Session;</cfoutput>  individualChatUIObj.SetVariable("SessionID_txt.text", SessionID);  individualChatUIObj.SetVariable("_global.IndivSessionID", SessionID);  //mustgetsessionIUDanduseingetremote   alert(SessionID);   }    if(command=="getUsername")   { <cfoutput>username="#username#";</cfoutput>   alert(username);  individualChatUIObj.SetVariable("userid_txt.text", username);  individualChatUIObj.SetVariable("_global.userid", username);   }   if(command=="getRequestor")   { <cfoutput>requestor="#requestor#";</cfoutput>   //alert(requestor);  individualChatUIObj.SetVariable("invitation_txt.text",requestor+"wantstochat");   }   }   //HookforInternetExplorer   if(navigator.appName&& navigator.appName.indexOf("Microsoft")!=-1&&    navigator.userAgent.indexOf("Windows")!=-1&& navigator.userAgent.indexOf("Windows3.1")==-1){ document.write('<SCRIPTLANGUAGE=VBScript\>\n');    document.write('onerrorresumenext\n');    document.write('SubindividualChatUI_FSCommand(ByVal command,ByValargs)\n');    document.write('call individualChatUI_DoFSCommand(command,args)\n');    document.write('endsub\n'); document.write('</SCRIPT\>\n');   }   //--> </SCRIPT> <!--URLsusedinthemovie--> <!--textusedinthemovie--> <!--<PALIGN="LEFT"></P>Hello--><OBJECT class codebase="http://download.macromedia.com/pub/shockwave/ca bs/flash/swflash.cab#version=6,0,0,0"   WIDTH="440"HEIGHT="300"ALIGN=""> <PARAMNAME=movieVALUE="individualChatUI.swf"><PARAM NAME=qualityVALUE=high><PARAMNAME=bgcolorVALUE=#D4D0C8> <EMBEDsrc="/books/2/447/1/html/2/individualChatUI.swf"quality=high bgcolor=#D4D0C8WIDTH="440"HEIGHT="300"swLiveConnect=true NAME="individualChatUI"ALIGN="" TYPE="application/x-shockwave-flash" PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer" ></EMBED> </OBJECT> </BODY> </HTML> 

Once an acceptance to an invitation is submitted, the user is presented with a screen that records the dialog of the current chat and allows the creation of new messages (Figure IV-3.8).

Figure IV-3.8. The individual chat user interface.

graphics/04fig15.gif

There can exist a number of different instances of this UI per user session, compared with the single chat-center UI per user logged-in session.

This frame uses Flash MX on both the server and client sides. The FCS will handle the real-time text messaging.

The individual chat UI was fairly straightforward (once an initial prototype of it was developed). The Flash MX file calls the browser's script code, which populates the sessionid and userid variables. This browser method call is invoked from within the .swf file through the use of the FSCommand function.

 _FSCommand("getUsername");  _FSCommand("getChatSessionID"); 

The above ActionScript FSCommand code calls the following JavaScript code:

 functionindividualChatUI_DoFSCommand(command,args){  varindividualChatUIObj=InternetExplorer?individualChatUI :document.individualChatUI;  if(command=="getChatSessionID")   {........}  if(command=="getRequestor")   {........} 

Once the SWF file has the sessionID, it connects to the chat/individualChat instance to get the server-side shared object that corresponds to the returned sessionID.

After the code has populated the variables with the FSCommand, a connection is made to the server. It then calls the server-side shared object (with the sessionID as its instance name). Once the sharedObject is retrieved, the user can enter a message and send it to the server-side shared object, which will then invoke all current connected users.onsync method. This onsync code populates the dialog texfield with HTML text.

 IndividualChat_nc=newNetConnection();  IndividualChat_nc.connect("rtmp:/chat/Individual", _global.userID); IndividualChat_nc.onStatus=function(info){ trace(info.description); } ChatSession_so= SharedObject.getRemote(_global.IndivSessionID, IndividualChat_nc.uri,false); ChatSession_so.connect(IndividualChat_nc); ChatSession_so.setFps(2); ChatSession_so.data.chatDialog=""; ChatSession_so.onSync=function(list){ for(vari=0;i<list.length;i++)  if(list[i].name=="chatDialog")  {  _root.dialog_txt.HTMLtext=ChatSession  _so.data.chatDialog;   break;  } }; 

NOTE If a text field has been selected as an HTML text field, then the field contains the HTML code. This component will then display the contents as HTML.

Once the chat instance has been instantiated, a server-side shared object is created by the FCS. This gives users the ability to send and receive messages in real-time.

You program the application to send messages in HTML format with the following code.

 ChatSession_so.data.chatDialog="<fontcolor=\"blue\"><b>" +_global.UserID+":</b>"+_root.message_txt.text+chr(10)+ ChatSession_so.data.chatDialog;  


Reality Macromedia ColdFusion MX. Macromedia Flash MX Integration
Reality Macromedia ColdFusion MX: Macromedia Flash MX Integration
ISBN: 0321125150
EAN: 2147483647
Year: 2002
Pages: 114

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net