//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.messaging { import flash.display.DisplayObject; import flash.errors.IllegalOperationError; import flash.events.StatusEvent; import mx.core.mx_internal; import mx.collections.ArrayCollection; import mx.collections.ListCollectionView; import mx.events.PropertyChangeEvent; import mx.messaging.messages.IMessage; import mx.rpc.AsyncDispatcher; import mx.rpc.AsyncToken; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.rpc.Fault; use namespace mx_internal; /** * Dispatched when the status of the message queue changes. * * @see mx.messaging.events.MessageQueueStatusCode * * @eventType flash.events.StatusEvent.STATUS * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ [Event(name="status", type="flash.events.StatusEvent")] /** * The result event is dispatched when an asynchronous operation of * the message queue completes successfully. * * @eventType mx.rpc.events.ResultEvent.RESULT * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ [Event(name="result", type="mx.rpc.events.ResultEvent")] /** * The fault event is dispatched when an asynchronous operation of * the message queue fails. * * @eventType mx.rpc.events.FaultEvent.FAULT * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ [Event(name="fault", type="mx.rpc.events.FaultEvent")] // Commenting out to avoid warnings while building rpc.swc dependencies. //[Deprecated(since="3.0.0")] [ExcludeClass] /** * @private * The base class for message queue implementations. * * NOTE: This class was deprecated in Flex 3.0 and should no longer be used. */ public class AbstractMessageStore extends ArrayCollection { //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- [Deprecated(since="3.0.0")] /** * Constructs an AbstractMessageStore. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function AbstractMessageStore() { super(); } //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // autoSend //---------------------------------- private var _autoSend:Boolean; [Bindable(event="propertyChange")] /** * Indicates whether queued messages are automatically sent upon * connect or reconnect of the associated Producer. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function get autoSend():Boolean { return _autoSend; } public function set autoSend(value:Boolean):void { if (_autoSend != value) { var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "autoSend", _autoSend, value); _autoSend = value; attemptToSendOrConnect(); dispatchEvent(event); } } //---------------------------------- // cacheID //---------------------------------- private var _cacheId:String; [Bindable(event="propertyChange")] /** * Provides access to the cache identifier for this service. * A cache identifier must be set prior to performing any operations that * require interaction with data stored locally on disk. * If a cache identifier is not set all cache methods and properties are * considered inconsistent and a IllegalOperationError will be * thrown during any operation that requires data from the local disk. * This property is provides a unique "session" identifier for data stored * locally. * A developer must set this property to a unique value for the * application. * A value of null or empty string is considered unset. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function get cacheID():String { return _cacheId; } public function set cacheID(value:String):void { if (_cacheId != value) { _initialized = value == null; _cacheId = value; internalSetCacheId(value); } } //---------------------------------- // isInitialized //---------------------------------- // only not initialized by default if there is a cacheID set otherwise // this store is in-memory only and is ready to use at construction time // for details see setter for cacheID protected var _initialized:Boolean = true; [Bindable(event="propertyChange")] /** * Indicates if the store has been initialized. * If not initialized call the initialize() method before * using this store. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function get isInitialized():Boolean { return _initialized; } //---------------------------------- // loaded //---------------------------------- private var _loaded:Boolean; [Bindable(event="propertyChange")] /** * True if the persistent state for the store has been loaded. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function get loaded():Boolean { return _loaded; } //---------------------------------- // producer //---------------------------------- private var _producer:AbstractProducer; [Bindable(event="propertyChange")] /** * The Producer the queue is assigned to. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function get producer():AbstractProducer { return _producer; } //-------------------------------------------------------------------------- // // Public Methods // //-------------------------------------------------------------------------- /** * Adds the passed message to the queue. * If the queue is configured to autoSend and the associated Producer is connected * the message is sent directly rather than being queued. * * @param item The message to add to the queue. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ override public function addItem(item:Object):void { var msg:IMessage = IMessage(item); if (msg != null) { if (autoSend && producer != null && producer.connected) producer.send(msg); else super.addItem(msg); } } /** * Clears out the persistent store and the in-memory state for the MessageStore. * Add a responder to the returned token to handle success or failure. * Alternately add any desired data to the token and listen for general * ResultEvents and FaultEvents dispatched by the MessageStore. * * @throws IllegalOperationError if no cacheID has been set * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function clearCache():AsyncToken { checkCacheId(); var token:AsyncToken = new AsyncToken(null); var ms:AbstractMessageStore = this; var success:Function = function():void { ms.internalClearCache(token); } var failed:Function = function():void { dispatchFaultEvent(getInitFailedFault(), token); } if (!isInitialized) internalInitialize(success, failed); else new AsyncDispatcher(success, [], 1); return token; } /** * This method will fill the specified ListCollectionView * with all cache identifiers previously used in the application. * * @param view ListcollectionView reference to a collection that should be * filled with all cache identifiers previously used in the application. * @return AsyncToken reference to the token that will identify this * operation in a result or fault event dispatched from this service. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function getCacheIDs(view:ListCollectionView):AsyncToken { var result:AsyncToken = new AsyncToken(null); internalGetCacheIDs(view, result); return result; } /** * Intializes the message store. * * @return AsyncToken which can be used to respond to the success or * failure of the operation. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function initialize():AsyncToken { var result:AsyncToken = new AsyncToken(null); var success:Function = function():void { var event:ResultEvent = ResultEvent.createEvent(null, result); dispatchResultEvent(event, result); } var failed:Function = function():void { dispatchFaultEvent(getInitFailedFault(), result); } if (!isInitialized) internalInitialize(success, failed); else new AsyncDispatcher(success, [], 1); return result; } /** * Loads the current persistent messages for the queue after clearing out * its current in-memory state. * This method can be used to return the store to its last known good * persistent state if the addition of a message overruns the allowed * persistent storage space and fails. * A cacheID must be set before this method can be called. * * @throws IllegalOperationError if the cacheID property is * invalid. * @see mx.messaging.MessageStore#cacheID * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function loadCache():AsyncToken { checkCacheId(); var token:AsyncToken = new AsyncToken(null); var ms:AbstractMessageStore = this; var success:Function = function():void { ms.internalLoadCache(token); } var failed:Function = function():void { dispatchFaultEvent(getInitFailedFault(), token); } if (!isInitialized) internalInitialize(success, failed); else new AsyncDispatcher(success, [], 1); return token; } /** * Releases resources used by this MessageStore. * Any pending unsaved changes are not pesisted when this method is invoked. * If this method is invoked before the MessageStore has initialized or * after it has been released it is a no-op; however it will still return * a token and dispatch a ResultEvent. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function release():AsyncToken { // Commenting out as AbstractProducer no longer has a message store. //if (producer != null) // producer.messageStore = null; setLoaded(false); var token:AsyncToken = new AsyncToken(null); internalRelease(token); return token; } /** * Saves any manual modifations that have been made to messages within the * Store. * The MessageStore only automatically saves when messages are added or * removed as the result of a Producer.send() call. * Any other manual modifications are not saved unless saveCache() is * called. * Add a responder to the returned token to handle success or failure. * Alternately add any desired data to the token and listen for general * ResultEvents and FaultEvents dispatched by the MessageStore. * * @throws IllegalOperationError If the cacheID has not been set. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public function saveCache():AsyncToken { checkCacheId(); var token:AsyncToken = new AsyncToken(null); var ms:AbstractMessageStore = this; var success:Function = function():void { ms.internalSaveCache(token); } var failed:Function = function():void { dispatchFaultEvent(getInitFailedFault(), token); } if (!isInitialized) internalInitialize(success, failed); else new AsyncDispatcher(success, [], 1); return token; } //-------------------------------------------------------------------------- // // Internal Methods // //-------------------------------------------------------------------------- /** * Invoked by the associated Producer to add a message to the store. * Subclasses may override to persist the message or perform other custom * processing. Messages that are added to the store must be tagged with a * DSMessageStore header by the store implementation. * * @param msg The message to queue. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ mx_internal function addMessage(msg:IMessage):void {} /** * Invoked by the associated Producer to remove a message from the store. * This method is called when the store has autoSend = false and the * application passes a queued message to the Producer to send. * The producer calls this method to ensure that the message is fully * removed from the store before being sent over the network. * * @param msg The message to remove. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ mx_internal function removeMessage(msg:IMessage):void {} /** * Sets the producer for this message store. * This method is called by the AbstractProducer when it's messageStore * property is set. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ mx_internal function setProducer(value:AbstractProducer):void { if (_producer != value) { var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "producer", _producer, value); if (_producer != null) _producer.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, producerPropertyChangeHandler); _producer = value; if (_producer != null) { _producer.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, producerPropertyChangeHandler); attemptToSendOrConnect(); } dispatchEvent(event); } } //-------------------------------------------------------------------------- // // Protected Methods // //-------------------------------------------------------------------------- /** * @private * Utility method that checks the queue state and associated Producer state * and auto sends queued messages if possible or connects the Producer if * possible. */ protected function attemptToSendOrConnect():void { if (length && autoSend && producer != null) { if (!producer.connected && producer.autoConnect) producer.connect(); // This will trigger a send once the connect completes. else if (producer.connected && producer.clientId != null) sendAll(); } } /** * Dispatch a fault event associated with a pending call/token. * * @param token The token associated with the fault. * @param fault The fault. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ protected function dispatchFaultEvent(fault:Fault, token:AsyncToken):void { var faultEvent:FaultEvent = FaultEvent.createEvent(fault, token); faultEvent.callTokenResponders(); if (hasEventListener(FaultEvent.FAULT) && token != null) dispatchEvent(faultEvent); else throw fault; } /** * Dispatch a result event associated with a pending call/token. * * @param token The token associated with the result. * @param generalDispatch Allows code to trigger responders without dispatching a general result event. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ protected function dispatchResultEvent(event:ResultEvent, token:AsyncToken):void { event.callTokenResponders(); if (hasEventListener(ResultEvent.RESULT) && token != null) dispatchEvent(event); } /** * The decendant class must clear the local disk of any cached data. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ protected function internalClearCache(token:AsyncToken):void {} /** * The descendant class must retrieve the cache ids from the local store and * add them to the specified view. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ protected function internalGetCacheIDs(view:ListCollectionView, token:AsyncToken):void {} /** * The descendant class must initialize the local store. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ protected function internalInitialize(success:Function, failed:Function):void { success(); } /** * The descendant class must load the store with any data found for the * specified cacheID. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ protected function internalLoadCache(token:AsyncToken):void {} /** * The descendant class must release any disk/local store resources * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ protected function internalRelease(token:AsyncToken):void {} /** * The descendant class must perform any local storage configuration here. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ protected function internalSetCacheId(value:String):void {} /** * The descendant class must save the current state of the store to disk. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ protected function internalSaveCache(token:AsyncToken):void {} /** * @private * This method sends a message after stripping the queue header. * It may be invoked by sendAll() or remove() in concrete implementations * to pass a message back to the associated Producer to be sent. * * @param msg The message to send. */ protected function send(msg:IMessage):void { if (msg.headers[MESSAGE_STORE_HEADER] != null) delete msg.headers[MESSAGE_STORE_HEADER]; producer.send(msg); } /** * @private * This method passes all queued messages to the associated Producer * to be sent after removing them completely from the queue. */ protected function sendAll():void {} /** * This method is called by implementations once all data is loaded from * disk. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ protected function setLoaded(value:Boolean):void { if (_loaded != value) { var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "loaded", _loaded, value); _loaded = value; dispatchEvent(event); } } //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- private function checkCacheId():void { if (cacheID == null || cacheID.length == 0) throw new IllegalOperationError("A cache identifier must be set before performing this operation."); } private function getInitFailedFault(details:String = null):Fault { var fault:Fault = new Fault("Client.Initialization.Failed", "Could not initialize MessageStore.", details); return fault; } /** * @private * This handler responds to property changes on the associated Producer. */ private function producerPropertyChangeHandler(event:PropertyChangeEvent):void { switch (event.property) { case "autoConnect": case "connected": if (event.newValue) attemptToSendOrConnect(); break; case "clientId": if (event.newValue != null && event.oldValue == null) attemptToSendOrConnect(); break; } } //-------------------------------------------------------------------------- // // Public Static Constants // //-------------------------------------------------------------------------- /** * A header injected into messages that are added to the queue to allow the * associated Producer to ensure that queued messages that are passed to it * originate from the proper queue and have been removed from the queue before * being sent. This header is automatically removed from the message before it * is sent. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion BlazeDS 4 * @productversion LCDS 3 */ public static const MESSAGE_STORE_HEADER:String = "DSMessageStore"; } }