Section 8.5. Temporary and Persistent Shared Objects

8.5. Temporary and Persistent Shared Objects

The preceding examples in this chapter all used temporary shared objects. Each temporary shared object is created either by a server-side script or when the first client with write access tries to connect to the shared object. The shared object is disposed of as soon as it is no longer in use by the server-side script and by its clients . At the very latest, it will be disposed of when the application instance shuts down. If you tried out the shared ball example, you may have noticed that if every client disconnected, the application did not remember the last position of the ball. The ball position was simply reset by the first client to connect. The shared ball example does not use a server-side script, so the shared object is created when the first client connects and then disposed of when the last client disconnects.

If you need to retain shared object data even after the application instance quits, you can create a persistent shared object, which is stored on the server and available the next time an instance starts up. Use persistent shared objects whenever you need to maintain application state between instance sessions.

When creating shared objects, you can make them temporary or persistent (persistent ones can be made to persist only on the server or on both the client and server). Shared objects that are persistent only on the client are called local shared objects and are not covered here (they are created with SharedObject.getLocal( ) as discussed in ActionScript for Flash MX: The Definitive Guide ). If you need to share data between clients only while an application is running, use temporary shared objects.

Persistent shared objects are stored as .fso files in each application's sharedobjects directory. A shared object that persists on the server can also be saved locally on the client. When remote shared objects are stored locally, they have an .sor extension, which is different from the .sol extension given to local shared objects. Locally persistent remote shared objects enable your application to update the shared object offline and resynchronize with the server when the connection is reestablished.

When using the getRemote( ) method on the client, the optional third parameter, persistence , is a Boolean or string that determines whether and where the object should persist:

 SharedObject.getRemote(SharedObjectURI, AppInstanceURI [, persistence]); 

To request a temporary shared object (one not persisted on the server), pass false or null or omit the persistence parameter altogether. Passing true requests a persistent shared object that is stored on the server only. To store a remote shared object both on the server and locally, pass in a string that defines the path for local persistence, as described later under "Locally and Remotely Persistent Shared Objects." The getRemote( ) method will return null if it detects that any of the parameters passed to it are invalid.

On the server, the second parameter of the SharedObject.get( ) method specifies persistence:

 SharedObject.get (SharedObjectURI [, persistence [, netConnection]]); 

Passing TRue makes the shared object persistent while passing false or omitting the parameter means a temporary shared object will be returned. The optional netConnection parameter is described later under "Proxied Shared Objects."

Persistent and temporary shared objects occupy different namespaces. For example, the following two client-side statements return different shared objects even though the shared object's relative URI ("Test") and the application URI ("rtmp:/testSO") are the same:

 x1_so = SharedObject.getRemote("Test", "rtmp:/testSO", true); x2_so = SharedObject.getRemote("Test", "rtmp:/testSO", false); 

If you specify persistence on the client and non-persistence on the server (or vice versa), only client-side updates are visible on the client and only server-side updates are visible on the server. The problem is that the two shared objects are differentthey exist in different namespaces even though they appear to have the same name . Make sure that you are consistent with the type of persistence you specify on the client and on the server.


For example, to create a temporary shared object on the server, you can use:

 userList_so = SharedObject.get("public/userList"); 

To connect to the same temporary shared object in Flash, use:

 userList_so = SharedObject.getRemote ("public/userList", nc.uri); 

To persist the shared object on the server between sessions, pass true as the second parameter:

 userList_so = SharedObject.get("public/userList",  true  ); 

and in Flash, pass true as the third parameter:

 userList_so = SharedObject.getRemote("public/userList", nc.uri,  true  ); 

Temporary shared objects exist within an application instance only while a client or the application instance has a reference to it. For example, if a single client creates a temporary shared object and then disconnects, the shared object is disposed of. If several clients connect to the same temporary shared object, when the last one disconnects or closes the shared object, the shared object is disposed of.

If a temporary shared object must last through an application instance session, the application instance should use SharedObject.get( ) to get a reference to the shared object and keep it from being disposed of until the instance itself is disposed of. The application.onAppStart( ) method is a good place to get( ) a temporary shared object. Provided the application does not close the shared object, the shared object and the data it contains will be available until the application instance is disposed of.

When the last connection to a persistent shared object is closed, the server writes the latest state to the shared object's .fso file and disposes the shared object from memory. Later, if a client connects to the shared object, the contents of the .fso file are read into memory again by the server and copied to the client's version of the shared object.

8.5.1. Synchronizing Temporary and Persistent Shared Objects

Temporary and persistent shared objects are not synchronized in the same way. When you write an onSync( ) method for a shared object, you must match your event handling approach to the persistence option you select. Whenever a client connects to a temporary shared object, any data already in the client's copy of the shared object is cleared (deleted). As a result, the client must wait until it gets the first onSync( ) call before it updates the shared object. Persistent shared objects are not synchronized in the same way. Their data properties might not be deleted before the first onSync( ) is called. Since handling synchronization is perhaps one of the most complex aspects of FlashCom development, let's walk through the differences between synchronizing temporary and persistent shared objects in detail.

After a Flash movie connects to a shared object with SharedObject.connect( ) , the local copy of the shared object is synchronized with the remote shared object on the server. When a temporary shared object is synchronized, any properties in the local copy are cleared and any data in the remote shared object is downloaded to the movie's shared object. As a result, the onSync( ) method of the movie's shared object will always be passed a list containing one information object with a code value of "clear" followed by information objects with code values of "change" for each property in the remote shared object.

For example, if a movie connects to a temporary shared object that already has three properties named a , b , and c , then the list passed into the onSync( ) method will contain four information objects:

 {code: "clear"} {code: "change", name: "a"} {code: "change", name: "b"} {code: "change", name: "c"} 

Since temporary shared objects are always cleared whenever they are synchronized, they cannot be used to read or write data until they are synchronized. That is why it is essential to wait until the onSync( ) method of a temporary shared object is called the first time before using a temporary shared object in a movie. A common way to make sure the shared object has been synchronized before using it is to use a private flag in the shared object to trigger an initialization function. The shared ball in Example 8-4 uses the same technique to show when the ball has been synchronized:

 so = SharedObject.getRemote("myTemporarySharedObject", nc.uri); // Create a private flag property named   synchronized   . so.synchronized = false; // Define the   onSync( )   handler. so.onSync = function (list) {   // If   synchronized   is not set, this is the first call.   if (!this.synchronized) {     this.synchronized = true;     // Call a function or object method, which can now use this shared object.   }   // Do other synchronization work here. }; // Don't forget to actually connect to the shared object! so.connect(nc); 

Persistent shared objects are not automatically cleared when they are synchronized. For example, if a movie creates a persistent shared object and connects for the first time to a shared object on the server that already has properties a , b , and c , the list passed into the onSync( ) method will contain only three information objects:

 {code: "change", name: "a"} {code: "change", name: "b"} {code: "change", name: "c"} 

If there are no properties on the server's copy of the shared object, the onSync( ) method will be passed an empty list.

When using persistent shared objects, the movie may set properties of the shared object before calling its connect( ) method. In this short code snippet, two properties named a and b are created before connecting the shared object. When connect( ) is called, the local copy of the shared object will send any data it already has to the server:

 // Get a reference to the shared object. //   nc.connect(  )   must have been called already (to create the net connection). so = SharedObject.getRemote("myPersistentSharedObject", nc.uri, true); // Define a simple   onSync( )   method for testing. so.onSync = function (list) {   writeln("----onSync list.length: " + list.length + "----");   for (var i = 0; i < list.length; i++) {     var info = list[i];     for (var p in info) {       writeln(i + ": " + p + ": " + info[p]);     }   } }; // Set some initial values for the shared object. so.data.a = "A is for alienation."; so.data.b = "B is for the boss..."; // Now, connect the shared object. so.connect(nc); 

If the shared object on the server doesn't have any properties already, the code produces this output. Note the onSync( ) method is called twice. The first time it is called with an empty list. The second time, you can see that the server has accepted the new a and b slot data from the local copy of the shared object and added them to the server's copy:

 ----onSync list.length: 0---- ----onSync list.length: 2---- 0: code: success 0: name: a 1: code: success 1: name: b 

If the properties a and b already exist in the shared object on the server, onSync( ) is called only once:

 ----onSync list.length: 2---- 0: oldValue: A is for alienation. 0: code: reject 0: name: a 1: oldValue: B is for the boss... 1: code: reject 1: name: b 

If the shared object on the server already has just two properties, x and y , then this output occurs:

 ----onSync list.length: 2---- 0: code: change 0: name: x 1: code: change 1: name: y ----onSync list.length: 2---- 0: code: success 0: name: a 1: code: success 1: name: b 

In this case, onSync( ) was called twiceonce to notify us about what properties had been added to the local copy by the server and the second time to tell us what happened to the properties of the shared object that existed before we tried to connect.

You can close the connection to a persistent shared object by calling the shared object's close( ) method. Later, the shared object can be reconnected by calling the shared object's connect( ) method again without necessarily deleting data in the local copy of the shared object. This feature makes it possible to build applications in which the client does not have to be connected to keep working with shared object data.

Every time the client connects to a persistent shared object and synchronization takes place, the server compares each shared object slot's version number in the client to the version number for each slot on the server. The server sends slot data to the client only for slots that are not current in the movie's shared object. For example, if the only property of the shared object that has changed since the movie's shared object was closed is a property named c , then onSync( ) , containing only one information object, is called:

 {code: "change", name: "c"} 

If there have been no changes, onSync( ) is passed an empty list.

The persistent shared object synchronization process also manages the deletion of slots. If a local copy contains a slot that has since been deleted in the remote version of the shared object, when the local copy connects again, the local slot will be deleted. For example, if slot c was deleted on the server and still exists in the local copy, an information object will be returned in the onSync( ) list:

 {code: "delete", name: "c"} 

Similarly, if a property of the local shared object is deleted while the local copy of a shared object is not connected and no changes to that property are made in the remote copy, when the shared object reconnects, the property will be deleted on the server. If the property was updated on the server while the client was disconnected, the request to delete the property will be rejected.

8.5.2. Clearing and Deleting Persistent Shared Objects

Even if all the properties of the shared object have been cleared, the .fso file on the server, if it exists, will not be deleted.

If you want to delete the .fso file of a persistent shared object you must call application.clearSharedObjects( ) . It is similar to the application.clearStreams( ) method in that it will clear and delete many shared objects at once and is passed a path string that can contain the * and ? wildcard characters . The ? character matches a single character, while the * matches any number of characters. The clearSharedObjects( ) method returns true if the shared objects within the path were deleted and false if they were not. If the following list of shared object URIs are used to create persistent shared objects, several directories will be created within the application instance's sharedobjects directory:

 userList chat/index chat/history_01 chat/history_02 private/blesser/streamList private/blesser/accessLog 

The actual file paths will be:

 .../applications/   appName   /sharedobjects/   instanceName   /userList.fso .../applications/   appName   /sharedobjects/   instanceName   /chat/index.fso .../applications/   appName   /sharedobjects/   instanceName   /chat/history_01.fso .../applications/   appName   /sharedobjects/   instanceName   /chat/history_02.fso .../applications/   appName   /sharedobjects/   instanceName   /private/blesser/streamList.fso .../applications/   appName   /sharedobjects/   instanceName   /private/blesser/accessLog.fso 

where the path to the applications directory depends on where FlashCom is installed, and appName and instanceName are placeholders for actual application and instance names .

To delete an individual file, pass the URI for the shared object to clearSharedObjects( ) . For example, to clear and delete only the userList shared object, use:

 application.clearSharedObjects("/userList"); 

To clear and delete only the chat/index shared object, use:

 application.clearSharedObjects("/chat/index"); 

To delete shared objects beginning with "history", use either the * or ? wildcard character. Deleting the entire contents of the chat path also deletes the index shared object. See the comments for each statement:

 // Delete by matching any last two characters after "history_". application.clearSharedObjects("/chat/history_??"); // Delete by matching any characters after "history". application.clearSharedObjects("/chat/history*"); // Delete everything in the   chat   directory and its subdirectories using a "*". application.clearSharedObjects("/chat/*"); // Delete everything in the   chat   directory and its subdirectories // using a trailing forward slash after "/chat". application.clearSharedObjects("/chat/"); 

To clear and delete all the shared objects in the private directory and all its subdirectories, use either of the following two statements:

 application.clearSharedObjects("/private/"); application.clearSharedObjects("/private/*"); 

Either statement will delete both the private and blesser directories as well as the shared object files in them.

To clear all the properties and delete all the .fso files of all the shared objects owned by an application instance, use a path string of "/":

 application.clearSharedObjects("/") 

Any shared object subdirectories that no longer have any files in them will also be deleted.

8.5.3. Locally and Remotely Persistent Shared Objects

Persistent RSOs can also be stored locally much like local shared objects. Local persistence makes it possible to extend how long a user can work offline before having to connect to the server. A user can work with a local copy of a persistent shared object without connecting to the server, save his work locally, and quit the application. Later, he can retrieve the locally persistent copy and keep working. Finally, when he needs to connect to FlashCom, his data will be synchronized with the data on the server. Locally persistent RSOs can also reduce the bandwidth required to synchronize to the server, as described a bit later under "Resynchronization Depth."

When connected to the FlashCom Server, both the local and remote copies of the shared object are updated. While disconnected from FlashCom, only the local copy can be updated.

To request that a remotely persistent object also persist locally, the persistence parameter of the SharedObject.getRemote( ) method must contain a local path string similar to the localPath string required to create a local shared object:

 SharedObject.getRemote(SharedObjectURI, AppInstanceURI [, persistence]); 

The local path string must be part of the path information from the URL to the movie's .swf file. For example, if a .swf is downloaded from:

 rtmp://host.domain.com/so/sample1.swf 

then the local path string can be any one of the following:

 / /so /so/sample1.swf 

To avoid the possibility that two different Flash movies might inadvertently overwrite each other's local copy of a shared object, use the full path to the movie. In the preceding example:

 /so/sample1.swf 

note that sample1.swf is the name of a subdirectory, not the name of a .swf file. The shared object is stored in a subdirectory based on the local path string, plus the application instance's URI.

For example, if a shared object named Test were acquired via a call to getRemote( ) this way:

 SharedObject.getRemote("Test", "rtmp:/myApp/myInstance", "/so/sample1.swf"); 

it would show up on my Windows 2000 computer under the file path:

 C:\Documents and Settings\blesser\Application Data\Macromedia\Flash Player\ host.domain.com\so\sample1.swf\myApp\myInstance\Test.sor 

The first part of the path, down to the Flash Player subdirectory, is where all local shared object data is stored for my account on my computer. The last part of the path:

 \host.domain.com\so\sample1.swf\myApp\myInstance\Test.sor 

has four parts :


host.domain.com

The hostname of the system from which the movie was downloaded. There will be a directory for each host accessed.


so\sample1.swf

The path to the .swf file available at the named host. In this example, the subdirectory is named sample1.swf after the movie that stored the shared object in the /so/sample1.swf local path.


myApp\myInstance

The FlashCom application name and instance name where the remote shared object exists. It is always added after the local path in order to separate locally persistent shared objects that belong to different applications and instances.


Test.sor

The file containing the local copy of the remote shared object. The .sor extension is used for locally persistent remote shared objects. The .sol extension is used for purely local shared objects.

Under some circumstances, it may be desirable that more than one movie on a given client have access to the same remote shared object as well as its local copy. Any movie available from the same hostname and within the path defined by the local path string can access the local copy of the remote shared object of a different movie.

When a Flash movie disconnects from a remote shared object or disconnects completely from the server, it can continue to update the local copy of the shared object. When the movie reconnects to the server, the local and remote copies are synchronized. The server uses slot version numbers to determine if each slot in the local copy of the shared object should overwrite the corresponding slots in the remote version or if the remote data should overwrite some or all of the local data. If a slot in the local copy of the shared object has a higher version number, it will overwrite the slot in the remote version. In practice, this means that if a local slot in a shared object has been updated and the remote slot has not been updated, the local value will overwrite the remote value. However, if the remote version has changed at all since the client disconnected, the remote version will overwrite the local versionthe server version takes precedence.

The client-side onSync( ) method of the remote shared object will be called when the movie reconnects to the shared object. Whenever a local value is used to update the remote shared object, an info object will contain a code value of "success." If a local change is rejected, an info object will contain a code value of "reject". When a local change is rejected, the old local value is included in the info object's oldValue property.

Prior to Version 7.2 of Flash MX 2004, ActionScript 2.0's strict type checking caused an error when using a local path string as the persistence parameter passed to SharedObject.getRemote( ) . To solve the problem, you should upgrade to the latest release. If for some reason you can't upgrade, change the SharedObject definition file at .../First Run/Classes/SharedObject.as from:

 static function getRemote(name:String,  remotePath:String, persistence:Boolean):SharedObject; 

to:

 static function getRemote(name:String,  remotePath:String, persistence:Object):SharedObject; 

to fix the problem.


8.5.4. Using flush( ) to Write to a Persistent Shared Object

Normally, when a persistent shared object is created and data added to it, an .fso file is not immediately written to the server's disk by the application instance. The file may be written to disk some time later or when the instance shuts down. In fact, if no file exists and a persistent shared object has no properties when the instance shuts down, no .fso file is created. You can force the instance to write out the current state of a shared object by calling the Server-Side ActionScript SharedObject.flush( ) method. On the client side, you can also use SharedObject.flush( ) to write the shared object to local storage if the remote shared object is locally persistent (it also works for purely local shared objects). In each case, the flush( ) method affects only the system on which it is called. On the server, calling flush( ) forces a write to the server's disk, while on the client, flush( ) forces a write to local disk.

In two cases, calling flush( ) on the server does not write the RSO to the disk. Updating any slot in a shared object increments the version property of the shared object. If the version number has not changed since the last time the file was written to disk, calling flush( ) has no effect (there is no point in writing to disk if the data has not changed since the last write). The second case is when you lock( ) a shared object. All updates to a locked shared object are considered part of one batch, so the version number does not increment until unlock( ) is called. Therefore, if you call flush( ) on a locked shared object, there is no guarantee anything will be written to disk. A short code snippet illustrates:

 so.setProperty("balance", 0); so.flush(  );  // The shared object has changed, so it will be written to disk so.lock( );   // The lock freezes the version number so.setProperty("balance", 100);  // Property change does not increment version so.flush( );  //   flush( )   does nothing because the version number has not changed so.unlock( ); 

To guarantee that the latest version of the shared object is written to disk, unlock the shared object before calling flush( ) :

 so.lock(  );   // The lock freezes the version number so.setProperty("balance", 100); // Make a property change so.unlock(  ); // Unlocking allows FlashCom to increment the version number so.flush( );  // Updated data is written to disk 

8.5.5. Resynchronization Depth

By default, persistent shared objects are never cleared of data when they connect or reconnect to the remote shared object managed by the server. Version numbers are used to determine what local properties must be updated on the remote shared object and what slot changes in the local copy must be rejected. This scheme can be very efficient for large shared objects because reconnections may require fewer updates. For example, if a remote shared object has 1000 properties, the first time a client connects to it, all 1000 properties must be sent by the server to the movie. However, if the client movie also saves a local copy of the shared object, when it reconnects later, only the slots on the remote shared object that have changed must be downloaded to the local copy. If only 10 slots have changed, synchronization is much faster than if all 1000 slots must be downloaded. However, for applications in which an object is locally and remotely persistent and many deletions must be performed, it may be more efficient to simply clear all the local slots and download all the data from the remote shared object.

Server-side shared objects have a property named resyncDepth that can be used to control how many remote version changes must occur before reconnecting shared objects are cleared and repopulated with data from the remote shared object. For example, the resynchronization depth can be set this way:

 my_so.resyncDepth = 5; 

As a consequence, when a persistent shared object connects, its local version number is added to the resyncDepth value. If the total is less than the current version number of the remote shared object on the server, all its properties will be cleared and all the properties of the remote shared object will be downloaded.

When the data in a slot is deleted on the server, the actual slot and its associated version number are not deleted. The resyncDepth property also controls when properties of a shared object are completely deleted. If the version number of a slot plus resyncDepth is less than the shared object's current overall version number, the slot is completely deleted. The default value for resyncDepth is -1, which effectively sets the resynchronization depth to infinity so that the actual slot is never deleted.

Setting the resynchronization depth of a shared object is not always the best way to control the complete deletion of shared object slots. An alternative is to purge stale slots as necessary by calling the purge( ) method, which accepts a version threshold for the purge. For example:

 my_so.lock(  ); my_so.purge(my_so.version - 5); my_so.unlock(  ); 

In this example, if the version number of the shared object is 100, any slots with a version number less than 95 will be completely deleted.

Using the purge( ) method may lead to some surprising synchronization behaviors when locally and remotely persistent shared objects are reconnected. For example, a property may be saved both locally and remotely before disconnecting from an application. Later, while disconnected, the remote property may be deleted by another client and eventually purged. When the local copy reconnects, there will be no record on the server that the property was deleted, so the local version of the property will successfully update the remote shared object.

When either the resyncDepth property or purge( ) method is used, the complete deletion of slots frees memory for the application. Applications that are long livedthat is, not frequently garbage collectedand those that create and delete large numbers of shared object slots should completely delete them to reduce memory usage.



Programming Flash Communication Server
Programming Flash Communication Server
ISBN: 0596005040
EAN: 2147483647
Year: 2003
Pages: 203

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