| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.events.EventDispatcher; |
| |
| import mx.core.IMXMLObject; |
| import mx.core.mx_internal; |
| import mx.events.PropertyChangeEvent; |
| import mx.logging.ILogger; |
| import mx.logging.Log; |
| import mx.messaging.FlexClient; |
| import mx.messaging.channels.PollingChannel; |
| import mx.messaging.config.ConfigMap; |
| import mx.messaging.config.ServerConfig; |
| import mx.messaging.errors.InvalidDestinationError; |
| import mx.messaging.events.ChannelEvent; |
| import mx.messaging.events.ChannelFaultEvent; |
| import mx.messaging.events.MessageAckEvent; |
| import mx.messaging.events.MessageEvent; |
| import mx.messaging.events.MessageFaultEvent; |
| import mx.messaging.messages.AbstractMessage; |
| import mx.messaging.messages.AcknowledgeMessage; |
| import mx.messaging.messages.CommandMessage; |
| import mx.messaging.messages.ErrorMessage; |
| import mx.messaging.messages.IMessage; |
| import mx.messaging.messages.MessagePerformanceUtils; |
| import mx.netmon.NetworkMonitor; |
| import mx.resources.IResourceManager; |
| import mx.resources.ResourceManager; |
| import mx.utils.Base64Encoder; |
| import mx.utils.UIDUtil; |
| |
| use namespace mx_internal; |
| |
| /** |
| * Dispatched when an acknowledge message is received for a sent message. |
| * |
| * @eventType mx.messaging.events.MessageAckEvent.ACKNOWLEDGE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| [Event(name="acknowledge", type="mx.messaging.events.MessageAckEvent")] |
| |
| /** |
| * Dispatched when a message fault occurs. |
| * |
| * @eventType mx.messaging.events.MessageFaultEvent.FAULT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| [Event(name="fault", type="mx.messaging.events.MessageFaultEvent")] |
| |
| /** |
| * Dispatched when the underlying Channel the MessageAgent is using connects. |
| * |
| * @eventType mx.messaging.events.ChannelEvent.CONNECT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| [Event(name="channelConnect", type="mx.messaging.events.ChannelEvent")] |
| |
| /** |
| * Dispatched when the underlying Channel the MessageAgent is using disconnects. |
| * |
| * @eventType mx.messaging.events.ChannelEvent.DISCONNECT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| [Event(name="channelDisconnect", type="mx.messaging.events.ChannelEvent")] |
| |
| /** |
| * Dispatched when the underlying Channel the MessageAgent is using faults. |
| * |
| * @eventType mx.messaging.events.ChannelFaultEvent.FAULT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| [Event(name="channelFault", type="mx.messaging.events.ChannelFaultEvent")] |
| |
| /** |
| * Dispatched when the <code>connected</code> property of the MessageAgent changes. |
| * Also dispatched when the <code>subscribed</code> of a Consumer changes. |
| * @see mx.messaging.Consumer |
| * @eventType mx.events.PropertyChangeEvent.PROPERTY_CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| [Event(name="propertyChange", type="mx.events.PropertyChangeEvent")] |
| |
| [ResourceBundle("messaging")] |
| |
| /** |
| * The MessageAgent class provides the basic low-level functionality common to |
| * message handling for a destination. |
| * |
| * <p><b>Note:</b> For advanced use only. |
| * Use this class for creating custom message agents like the existing Producer |
| * and Consumer classes.</p> |
| * |
| * @mxml |
| * <p> |
| * All message agent classes, including the Producer and Consumer classes, extend |
| * MessageAgent and inherit the following tag attributes: |
| * </p> |
| * <pre> |
| * <mx:<i>tagname</i><br> |
| * <b>Properties</b> |
| * channelSet="<i>No default.</i>" |
| * clientId="<i>No default.</i>" |
| * connected="<i>false</i>" |
| * destination="<i>No default.</i>" |
| * requestTimeout="<i>-1</i>" |
| * subtopic="<i>No default.</i>" |
| * |
| * |
| * <b>Events</b> |
| * acknowledge="<i>No default.</i>" |
| * channelConnect="<i>No default.</i>" |
| * channelDisconnect="<i>No default.</i>" |
| * channelFault="<i>No default.</i>" |
| * fault="<i>No default.</i>" |
| * propertyChange="<i>No default.</i>" |
| * /> |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public class MessageAgent extends EventDispatcher implements IMXMLObject |
| { |
| //-------------------------------------------------------------------------- |
| // |
| // Internal Static Constants |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Indicates that the MessageAgent is used an automatically configured ChannelSet |
| * obtained from ServerConfig. |
| */ |
| mx_internal static const AUTO_CONFIGURED_CHANNELSET:int = 0; |
| |
| /** |
| * @private |
| * Indicates that the MessageAgent is using a manually assigned ChannelSet. |
| */ |
| mx_internal static const MANUALLY_ASSIGNED_CHANNELSET:int = 1; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function MessageAgent() |
| { |
| super(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * The type of MessageAgent. |
| * This variable is used for logging and MUST be assigned by subclasses. |
| */ |
| protected var _agentType:String = "mx.messaging.MessageAgent"; |
| |
| /** |
| * @private |
| * The Base64 encoded credentials that will be passed through to |
| * the ChannelSet. |
| */ |
| protected var _credentials:String; |
| |
| /** |
| * @private |
| * The character set encoding used to create the credentials String. |
| */ |
| protected var _credentialsCharset:String; |
| |
| /** |
| * @private |
| * Indicates whether the agent is explicitly disconnected. |
| * This allows agents to supress processing of acks/faults that return |
| * after the client has issued an explicit disconnect(). |
| */ |
| protected var _disconnectBarrier:Boolean; |
| |
| /** |
| * @private |
| * This helps in the runtime configuration setup by delaying the connect |
| * event until the configuration has been setup. See acknowledge(). |
| */ |
| private var _pendingConnectEvent:ChannelEvent; |
| |
| /** |
| * @private |
| * The Base64 encoded credentials that are passed through to a |
| * 3rd party. |
| */ |
| private var _remoteCredentials:String = ""; |
| |
| /** |
| * @private |
| * The character set encoding used to create the remoteCredentials String. |
| */ |
| private var _remoteCredentialsCharset:String; |
| |
| /** |
| * @private |
| * Indicates that the remoteCredentials value has changed and should |
| * be sent to the server. |
| */ |
| private var _sendRemoteCredentials:Boolean; |
| |
| /** |
| * @private |
| * The logger MUST be assigned by subclasses, for example |
| * Consumer and Producer. |
| */ |
| protected var _log:ILogger; |
| |
| /** |
| * @private |
| * A queue to store pending outbound messages while waiting for a server response |
| * that contains a server-generated clientId. |
| * Serializing messages from a MessageAgent to the server is essential until we |
| * receive a response containing a server-generated clientId; otherwise the server |
| * will treat each message as if it was sent by a different, "new" MessageAgent instance. |
| */ |
| private var _clientIdWaitQueue:Array; |
| |
| /** |
| * @private |
| * Flag being set to true denotes that we should skip remaining fault |
| * processing logic because the fault has already been handled. |
| * Currently used during an automatic resend of a faulted message if the fault |
| * was due to a server session timeout and is authentication/authorization related. |
| */ |
| protected var _ignoreFault:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var resourceManager:IResourceManager = |
| ResourceManager.getInstance(); |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // authenticated |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _authenticated:Boolean; |
| |
| [Bindable(event="propertyChange")] |
| /** |
| * Indicates if this MessageAgent is using an authenticated connection to |
| * its destination. |
| */ |
| public function get authenticated():Boolean |
| { |
| return _authenticated; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function setAuthenticated(value:Boolean, creds:String):void |
| { |
| if (_authenticated != value) |
| { |
| var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "authenticated", _authenticated, value); |
| _authenticated = value; |
| dispatchEvent(event); |
| |
| if (value) |
| assertCredentials(creds); |
| } |
| } |
| |
| //---------------------------------- |
| // channelSet |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _channelSet:ChannelSet; |
| |
| [Bindable(event="propertyChange")] |
| /** |
| * Provides access to the ChannelSet used by the MessageAgent. The |
| * ChannelSet can be manually constructed and assigned, or it will be |
| * dynamically initialized to use the configured Channels for the |
| * destination for this MessageAgent. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function get channelSet():ChannelSet |
| { |
| return _channelSet; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set channelSet(value:ChannelSet):void |
| { |
| internalSetChannelSet(value); |
| _channelSetMode = MANUALLY_ASSIGNED_CHANNELSET; |
| } |
| |
| /** |
| * @private |
| * This method is called by ChannelSet.connect(agent) to set up the bidirectional |
| * relationship between the MessageAgent and the ChannelSet. |
| * It also handles the case of customer code calling channelSet.connect(agent) |
| * directly rather than assigning the ChannelSet to the MessageAgent's channelSet |
| * property. |
| */ |
| mx_internal function internalSetChannelSet(value:ChannelSet):void |
| { |
| if (_channelSet != value) |
| { |
| if (_channelSet != null) |
| _channelSet.disconnect(this); |
| |
| var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "channelSet", _channelSet, value); |
| _channelSet = value; |
| |
| if (_channelSet != null) |
| { |
| if (_credentials) |
| _channelSet.setCredentials(_credentials, this, _credentialsCharset); |
| |
| _channelSet.connect(this); |
| } |
| |
| dispatchEvent(event); |
| } |
| } |
| |
| //---------------------------------- |
| // clientId |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _clientId:String; |
| |
| [Bindable(event="propertyChange")] |
| /** |
| * Provides access to the client id for the MessageAgent. |
| * MessageAgents are assigned their client id by the remote destination |
| * and this value is used to route messages from the remote destination to |
| * the proper MessageAgent. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function get clientId():String |
| { |
| return _clientId; |
| } |
| |
| /** |
| * @private |
| * This method is used to assign a server-generated client id to the MessageAgent |
| * in the common scenario. |
| * It may also be used by the framework to sync up cooperating MessageAgents under |
| * a single client id value so that they appear as a single MessageAgent to the server. |
| * Assigning a client id value will flush any messages that have been queued while we |
| * were waiting for a server-generated client id value to be returned. |
| * Queued messages are sent to the server in order. |
| */ |
| mx_internal function setClientId(value:String):void |
| { |
| if (_clientId != value) |
| { |
| var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "clientId", _clientId, value); |
| _clientId = value; |
| flushClientIdWaitQueue(); |
| dispatchEvent(event); |
| } |
| } |
| |
| //---------------------------------- |
| // connected |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _connected:Boolean = false; |
| |
| [Bindable(event="propertyChange")] |
| /** |
| * Indicates whether this MessageAgent is currently connected to its |
| * destination via its ChannelSet. The <code>propertyChange</code> event is dispatched when |
| * this property changes. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function get connected():Boolean |
| { |
| return _connected; |
| } |
| |
| /** |
| * @private |
| */ |
| protected function setConnected(value:Boolean):void |
| { |
| if (_connected != value) |
| { |
| var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "connected", _connected, value); |
| _connected = value; |
| dispatchEvent(event); |
| setAuthenticated(value && channelSet && channelSet.authenticated, _credentials); |
| } |
| } |
| |
| //---------------------------------- |
| // destination |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _destination:String = ""; |
| |
| [Bindable(event="propertyChange")] |
| /** |
| * Provides access to the destination for the MessageAgent. |
| * Changing the destination will disconnect the MessageAgent if it is |
| * currently connected. |
| * |
| * @throws mx.messaging.errors.InvalidDestinationError If the destination is null or |
| * zero-length. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function get destination():String |
| { |
| return _destination; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set destination(value:String):void |
| { |
| if ((value == null) || value.length == 0) |
| return; // empty/null destination is checked in internalSend. |
| |
| if (_destination != value) |
| { |
| // If we're using an automatically configured ChannelSet, |
| // disconnect from it and null out our ref so we look up the |
| // proper configured ChannelSet for the new destination on our next send(). |
| if ((_channelSetMode == AUTO_CONFIGURED_CHANNELSET) && (channelSet != null)) |
| { |
| channelSet.disconnect(this); |
| channelSet = null; |
| } |
| |
| var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "destination", _destination, value); |
| _destination = value; |
| dispatchEvent(event); |
| |
| if (Log.isInfo()) |
| _log.info("'{0}' {2} set destination to '{1}'.", id, _destination, _agentType); |
| } |
| } |
| |
| //---------------------------------- |
| // id |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _id:String = UIDUtil.createUID(); |
| |
| [Bindable(event="propertyChange")] |
| /** |
| * @private |
| * The id of this agent. |
| */ |
| public function get id():String |
| { |
| return _id; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set id(value:String):void |
| { |
| if (_id != value) |
| { |
| var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "id", _id, value); |
| _id = value; |
| dispatchEvent(event); |
| } |
| } |
| |
| //---------------------------------- |
| // requestTimeout |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _requestTimeout:int = -1; |
| |
| [Bindable(event="propertyChange")] |
| /** |
| * Provides access to the request timeout in seconds for sent messages. |
| * If an acknowledgement, response or fault is not received from the |
| * remote destination before the timeout is reached the message is faulted on the client. |
| * A value less than or equal to zero prevents request timeout. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function get requestTimeout():int |
| { |
| return _requestTimeout; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set requestTimeout(value:int):void |
| { |
| if (_requestTimeout != value) |
| { |
| var event:PropertyChangeEvent = PropertyChangeEvent.createUpdateEvent(this, "requestTimeout", _requestTimeout, value); |
| _requestTimeout = value; |
| dispatchEvent(event); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Internal Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // channelSetMode |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _channelSetMode:int = AUTO_CONFIGURED_CHANNELSET; |
| |
| mx_internal function get channelSetMode():int |
| { |
| return _channelSetMode; |
| } |
| |
| //---------------------------------- |
| // configRequested |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Indicates whether the agent has requested configuration from the server. |
| */ |
| mx_internal var configRequested:Boolean = false; |
| |
| //---------------------------------- |
| // needsConfig |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _needsConfig:Boolean; |
| |
| /** |
| * Indicates if this MessageAgent needs to request configuration from the |
| * server. |
| */ |
| mx_internal function get needsConfig():Boolean |
| { |
| return _needsConfig; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function set needsConfig(value:Boolean):void |
| { |
| if (_needsConfig == value) |
| return; |
| |
| _needsConfig = value; |
| if (_needsConfig) |
| { |
| var cs:ChannelSet = channelSet; |
| try |
| { |
| disconnect(); |
| } |
| finally |
| { |
| internalSetChannelSet(cs); |
| } |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Invoked by a MessageResponder upon receiving a result for a sent |
| * message. Subclasses may override this method if they need to perform |
| * custom acknowledgement processing, but must invoke |
| * <code>super.acknowledge()</code> as well. This method dispatches a |
| * MessageAckEvent. |
| * |
| * @param ackMsg The AcknowledgMessage returned. |
| * |
| * @param msg The original sent message. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function acknowledge(ackMsg:AcknowledgeMessage, msg:IMessage):void |
| { |
| if (Log.isInfo()) |
| _log.info("'{0}' {2} acknowledge of '{1}'.", id, msg.messageId, _agentType); |
| |
| if (Log.isDebug() && isCurrentChannelNotNull() && getCurrentChannel().mpiEnabled) |
| { |
| try |
| { |
| var mpiutil:MessagePerformanceUtils = new MessagePerformanceUtils(ackMsg); |
| _log.debug(mpiutil.prettyPrint()); |
| } |
| catch (e:Error) |
| { |
| _log.debug("Could not get message performance information for: " + msg.toString()); |
| } |
| } |
| |
| if (configRequested) |
| { |
| configRequested = false; |
| ServerConfig.updateServerConfigData(ackMsg.body as ConfigMap); |
| needsConfig = false; |
| if (_pendingConnectEvent) |
| channelConnectHandler(_pendingConnectEvent); |
| |
| _pendingConnectEvent = null; |
| } |
| |
| if (clientId == null) |
| { |
| if (ackMsg.clientId != null) |
| setClientId(ackMsg.clientId); // Triggers a call to flush the clientId wait queue. |
| else |
| flushClientIdWaitQueue(); |
| } |
| |
| dispatchEvent(MessageAckEvent.createEvent(ackMsg, msg)); |
| monitorRpcMessage(ackMsg,msg); |
| } |
| |
| /** |
| * Disconnects the MessageAgent's network connection. |
| * This method does not wait for outstanding network operations to complete. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function disconnect():void |
| { |
| if (!_disconnectBarrier) |
| { |
| // Ensure wait queue for client id value is destroyed. |
| _clientIdWaitQueue = null; |
| |
| // Only set the barrier used to discard post-disconnect results/faults |
| // if the agent is currently connected (otherwise, if this is invoked before |
| // connecting and the client fails to connect to the server, no faults will be |
| // dispatched). |
| if (connected) |
| _disconnectBarrier = true; |
| |
| if (_channelSetMode == AUTO_CONFIGURED_CHANNELSET) |
| internalSetChannelSet(null); |
| else if (_channelSet != null) |
| _channelSet.disconnect(this); |
| } |
| } |
| |
| /** |
| * Invoked by a MessageResponder upon receiving a fault for a sent message. |
| * Subclasses may override this method if they need to perform custom fault |
| * processing, but must invoke <code>super.fault()</code> as well. This |
| * method dispatchs a MessageFaultEvent. |
| * |
| * @param errMsg The ErrorMessage. |
| * |
| * @param msg The original sent message that caused this fault. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function fault(errMsg:ErrorMessage, msg:IMessage):void |
| { |
| if (Log.isError()) |
| _log.error("'{0}' {2} fault for '{1}'.", id, msg.messageId, _agentType); |
| |
| _ignoreFault = false; |
| configRequested = false; |
| |
| // Remove retryable hint. |
| if (errMsg.headers[ErrorMessage.RETRYABLE_HINT_HEADER]) |
| delete errMsg.headers[ErrorMessage.RETRYABLE_HINT_HEADER]; |
| |
| if (clientId == null) |
| { |
| if (errMsg.clientId != null) |
| setClientId(errMsg.clientId); // Triggers a call to flush the clientId wait queue. |
| else |
| flushClientIdWaitQueue(); |
| } |
| |
| dispatchEvent(MessageFaultEvent.createEvent(errMsg)); |
| monitorRpcMessage(errMsg,msg); |
| |
| handleAuthenticationFault(errMsg, msg); |
| } |
| |
| /** |
| * Handles a CONNECT ChannelEvent. Subclasses that need to perform custom |
| * processing should override this method, and invoke |
| * <code>super.channelConnectHandler()</code>. |
| * |
| * @param event The ChannelEvent. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function channelConnectHandler(event:ChannelEvent):void |
| { |
| _disconnectBarrier = false; |
| // If we are waiting on config to come in we can't be connected until |
| // we get it. See acknowledge(). |
| if (needsConfig) |
| { |
| if (Log.isInfo()) |
| _log.info("'{0}' {1} waiting for configuration information.", id, _agentType); |
| |
| _pendingConnectEvent = event; |
| } |
| else |
| { |
| if (Log.isInfo()) |
| _log.info("'{0}' {1} connected.", id, _agentType); |
| setConnected(true); |
| dispatchEvent(event); |
| } |
| } |
| |
| /** |
| * Handles a DISCONNECT ChannelEvent. Subclasses that need to perform |
| * custom processing should override this method, and invoke |
| * <code>super.channelDisconnectHandler()</code>. |
| * |
| * @param event The ChannelEvent. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function channelDisconnectHandler(event:ChannelEvent):void |
| { |
| if (Log.isWarn()) |
| _log.warn("'{0}' {1} channel disconnected.", id, _agentType); |
| setConnected(false); |
| // If we have remoteCredentials we need to send them on reconnect. |
| if (_remoteCredentials != null) |
| { |
| _sendRemoteCredentials = true; |
| } |
| dispatchEvent(event); |
| } |
| |
| /** |
| * Handles a ChannelFaultEvent. Subclasses that need to perform custom |
| * processing should override this method, and invoke |
| * <code>super.channelFaultHandler()</code>. |
| * |
| * @param The ChannelFaultEvent |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function channelFaultHandler(event:ChannelFaultEvent):void |
| { |
| if (Log.isWarn()) |
| _log.warn("'{0}' {1} channel faulted with {2} {3}", id, _agentType, event.faultCode, event.faultDetail); |
| |
| if (!event.channel.connected) |
| { |
| setConnected(false); |
| // If we have remoteCredentials we need to send them on reconnect. |
| if (_remoteCredentials != null) |
| { |
| _sendRemoteCredentials = true; |
| } |
| } |
| dispatchEvent(event); |
| } |
| |
| /** |
| * Called after the implementing object has been created |
| * and all properties specified on the tag have been assigned. |
| * |
| * @param document MXML document that created this object. |
| * |
| * @param id id used by the document to refer to this object. |
| * If the object is a deep property on the document, id is null. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function initialized(document:Object, id:String):void |
| { |
| this.id = id; |
| } |
| |
| /** |
| * Logs the MessageAgent out from its remote destination. |
| * Logging out of a destination applies to everything connected using the same ChannelSet |
| * as specified in the server configuration. For example, if several DataService components |
| * are connected over an RTMP channel and <code>logout()</code> is invoked on one of them, |
| * all other client components that are connected using the same ChannelSet are also logged out. |
| * |
| * <p><b>Note:</b> Adobe recommends that you use the mx.messaging.ChannelSet.logout() method |
| * rather than this method. </p> |
| * |
| * @see mx.messaging.ChannelSet#logout() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function logout():void |
| { |
| _credentials = null; |
| if (channelSet) |
| channelSet.logout(this); |
| } |
| |
| /** |
| * Sets the credentials that the MessageAgent uses to authenticate to |
| * destinations. |
| * The credentials are applied to all services connected over the same ChannelSet. |
| * |
| * @param username The username. |
| * @param password The password. |
| * @param charset The character set encoding to use while encoding the |
| * credentials. The default is null, which implies the legacy charset of |
| * ISO-Latin-1. The only other supported charset is "UTF-8". |
| * |
| * @throws flash.errors.IllegalOperationError in two situations; if credentials |
| * have already been set and an authentication is in progress with the remote |
| * detination, or if authenticated and the credentials specified don't match |
| * the currently authenticated credentials. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function setCredentials(username:String, password:String, charset:String=null):void |
| { |
| if (username == null && password == null) |
| { |
| _credentials = null; |
| _credentialsCharset = null; |
| } |
| else |
| { |
| var cred:String = username + ":" + password; |
| var encoder:Base64Encoder = new Base64Encoder(); |
| if (charset == Base64Encoder.CHARSET_UTF_8) |
| encoder.encodeUTFBytes(cred); |
| else |
| encoder.encode(cred); |
| _credentials = encoder.drain(); |
| _credentialsCharset = charset; |
| } |
| |
| if (channelSet != null) |
| channelSet.setCredentials(_credentials, this, _credentialsCharset); |
| } |
| |
| /** |
| * Sets the remote credentials that will be passed through to the remote destination |
| * for authenticating to secondary systems. |
| * |
| * @param username The username. |
| * @param password The password. |
| * @param charset The character set encoding to use while encoding the |
| * remote credentials. The default is null, which implies the legacy |
| * charset of ISO-Latin-1. The only other currently supported option is |
| * "UTF-8". |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function setRemoteCredentials(username:String, password:String, charset:String=null):void |
| { |
| if (username == null && password == null) |
| { |
| _remoteCredentials = ""; |
| _remoteCredentialsCharset = null; |
| } |
| else |
| { |
| var cred:String = username + ":" + password; |
| var encoder:Base64Encoder = new Base64Encoder(); |
| if (charset == Base64Encoder.CHARSET_UTF_8) |
| encoder.encodeUTFBytes(cred); |
| else |
| encoder.encode(cred); |
| _remoteCredentials = encoder.drain(); |
| _remoteCredentialsCharset = charset; |
| } |
| _sendRemoteCredentials = true; |
| } |
| |
| /** |
| * Returns true if there are any pending requests for the passed in message. |
| * This method should be overriden by subclasses |
| * |
| * @param msg The message for which the existence of pending requests is checked. |
| * |
| * @return Returns <code>true</code> if there are any pending requests for the |
| * passed in message. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| public function hasPendingRequestForMessage(msg:IMessage):Boolean |
| { |
| return false; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Internal Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Internal hook for ChannelSet to assign credentials when it has authenticated |
| * successfully via a direct <code>login(...)</code> call to the server or logged |
| * out directly. |
| */ |
| mx_internal function internalSetCredentials(credentials:String):void |
| { |
| _credentials = credentials; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Protected Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| final protected function assertCredentials(value:String):void |
| { |
| if (_credentials != null && (_credentials != value)) |
| { |
| var errMsg:ErrorMessage = new ErrorMessage(); |
| errMsg.faultCode = "Client.Authentication.Error"; |
| errMsg.faultString = "Credentials specified do not match those used on underlying connection."; |
| errMsg.faultDetail = "Channel was authenticated with a different set of credentials than those used for this agent."; |
| dispatchEvent(MessageFaultEvent.createEvent(errMsg)); |
| } |
| } |
| |
| /** |
| * @private |
| * Utility method to flush any pending queued messages to send once we have |
| * received a clientId from the remote destination. |
| */ |
| final protected function flushClientIdWaitQueue():void |
| { |
| if (_clientIdWaitQueue != null) |
| { |
| // If we have a valid clientId, flush all pending messages. |
| if (clientId != null) |
| { |
| while (_clientIdWaitQueue.length > 0) |
| { |
| internalSend(_clientIdWaitQueue.shift() as IMessage); |
| } |
| } |
| |
| if (clientId == null) |
| { |
| // If we still don't have a clientId, remove the first queued message and send it. |
| // Leave the queue intact to buffer subsequent sends until we get a response/fault |
| // back for this one. |
| if (_clientIdWaitQueue.length > 0) |
| { |
| var saveQueue:Array = _clientIdWaitQueue; |
| // Make sure we don't just put it back into the queue - we let the first |
| // one through if this is null. |
| _clientIdWaitQueue = null; |
| internalSend(saveQueue.shift() as IMessage); |
| _clientIdWaitQueue = saveQueue; |
| } |
| else |
| { |
| // Regardless of whether the clientId is defined or not, if the wait queue |
| // is empty set it to null to allow the next message to be processed by the |
| // send code path rather than being routed to the queue. |
| _clientIdWaitQueue = null; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Handles the authentication fault on the server. If the authenticated flag is true, |
| * the authentication fault must have been caused by a session expiration on the server. |
| * Set the authenticated state to false and if loginAfterDisconnect flag is enabled, |
| * resend credentials to the server by disconnecting and resending the message again. |
| * |
| * @param errMsg The Error Message. |
| * @param msg The message that caused the fault and should be resent once we have |
| * disconnected/connected causing re-authentication. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| protected function handleAuthenticationFault(errMsg:ErrorMessage, msg:IMessage):void |
| { |
| if (errMsg.faultCode == "Client.Authentication" && authenticated && isCurrentChannelNotNull()) |
| { |
| var currentChannel:Channel = getCurrentChannel(); |
| currentChannel.setAuthenticated(false); |
| |
| if (currentChannel is PollingChannel && (currentChannel as PollingChannel).loginAfterDisconnect) |
| { |
| reAuthorize(msg); |
| _ignoreFault = true; |
| } |
| } |
| } |
| |
| /** |
| * Used to automatically initialize the <code>channelSet</code> property for the |
| * MessageAgent before it connects for the first time. |
| * Subtypes may override to perform custom initialization. |
| * |
| * @param message The message that needs to be sent. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| protected function initChannelSet(message:IMessage):void |
| { |
| if (_channelSet == null) |
| { |
| _channelSetMode = AUTO_CONFIGURED_CHANNELSET; |
| internalSetChannelSet(ServerConfig.getChannelSet(destination)); |
| } |
| |
| if (_channelSet.connected && needsConfig && !configRequested) |
| { |
| message.headers[CommandMessage.NEEDS_CONFIG_HEADER] = true; |
| configRequested = true; |
| } |
| |
| _channelSet.connect(this); |
| |
| if (_credentials != null) |
| channelSet.setCredentials(_credentials, this, _credentialsCharset); |
| } |
| |
| /** |
| * Sends a Message from the MessageAgent to its destination using the |
| * agent's ChannelSet. MessageAgent subclasses must use this method to |
| * send their messages. |
| * |
| * @param message The message to send. |
| * |
| * @param waitForClientId If true the message may be queued until a clientId has been |
| * assigned to the agent. In general this is the desired behavior. |
| * For special behavior (automatic reconnect and resubscribe) the |
| * agent may pass false to override the default queuing behavior. |
| * |
| * @throws mx.messaging.errors.InvalidDestinationError If no destination is set. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| protected function internalSend(message:IMessage, waitForClientId:Boolean = true):void |
| { |
| // If we don't have a client or server assigned clientId, we |
| // need to send a single message and then store any subsequent messages |
| // in a buffer to be sent once we've gotten back a server-generated |
| // clientId. Otherwise, N outbound messages sent before receiving an ack for |
| // the first will result in the generation of N different clientIds in the |
| // response/fault messages from the server. |
| if ((message.clientId == null) && waitForClientId && (clientId == null)) |
| { |
| if (_clientIdWaitQueue == null) |
| { |
| _clientIdWaitQueue = []; |
| // Current message will be sent but subsequent messages sent before |
| // its ack/fault will be queued. |
| } |
| else |
| { |
| _clientIdWaitQueue.push(message); |
| return; // We've queued the message and will send it once we get a clientId or the outstanding send fails. |
| } |
| } |
| |
| if (message.clientId == null) |
| message.clientId = clientId; |
| |
| if (requestTimeout > 0) |
| message.headers[AbstractMessage.REQUEST_TIMEOUT_HEADER] = requestTimeout; |
| |
| if (_sendRemoteCredentials) |
| { |
| if (! ((message is CommandMessage) && |
| (CommandMessage(message).operation == CommandMessage.TRIGGER_CONNECT_OPERATION))) |
| { |
| message.headers[AbstractMessage.REMOTE_CREDENTIALS_HEADER] = _remoteCredentials; |
| message.headers[AbstractMessage.REMOTE_CREDENTIALS_CHARSET_HEADER] = _remoteCredentialsCharset; |
| _sendRemoteCredentials = false; |
| } |
| } |
| |
| if (channelSet != null) |
| { |
| if (!connected && (_channelSetMode == MANUALLY_ASSIGNED_CHANNELSET)) |
| _channelSet.connect(this); |
| |
| if (channelSet.connected && needsConfig && !configRequested) |
| { |
| message.headers[CommandMessage.NEEDS_CONFIG_HEADER] = true; |
| configRequested = true; |
| } |
| |
| channelSet.send(this, message); |
| monitorRpcMessage(message,message); |
| } |
| else if (destination != null && destination.length > 0) |
| { |
| initChannelSet(message); |
| if (channelSet != null) |
| { |
| channelSet.send(this, message); |
| monitorRpcMessage(message,message); |
| } |
| } |
| else |
| { |
| var msg:String = resourceManager.getString( |
| "messaging", "destinationNotSet"); |
| throw new InvalidDestinationError(msg); |
| } |
| } |
| |
| /** |
| * This function should be overriden by sublasses to implement reauthentication due to |
| * server session time-out behavior specific to them. In general, it should follow disconnect, |
| * connect, resend message pattern. |
| * |
| * @param msg The message that caused the fault and should be resent once we have |
| * disconnected/connected causing reauthentication. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion BlazeDS 4 |
| * @productversion LCDS 3 |
| */ |
| protected function reAuthorize(msg:IMessage):void |
| { |
| // Disconnect all message agents from the Channel to make sure the Channel |
| // is fully disconnected and Channel#internalConnect gets called which |
| // sends the login command to reauthenticate the Channel. |
| if (channelSet != null) |
| channelSet.disconnectAll(); |
| internalSend(msg); |
| } |
| |
| /** |
| * @private |
| */ |
| private function getCurrentChannel():Channel |
| { |
| return channelSet != null? channelSet.currentChannel : null; |
| } |
| |
| /** |
| * @private |
| */ |
| private function isCurrentChannelNotNull():Boolean |
| { |
| return getCurrentChannel() != null; |
| } |
| |
| /** |
| * Monitor a rpc message that is being send |
| */ |
| private function monitorRpcMessage(message:IMessage,actualMessage:IMessage):void |
| { |
| if (NetworkMonitor.isMonitoring()) |
| { |
| if (message is ErrorMessage) |
| { |
| NetworkMonitor.monitorFault(actualMessage, MessageFaultEvent.createEvent(ErrorMessage(message))); |
| } |
| else if (message is AcknowledgeMessage) |
| { |
| NetworkMonitor.monitorResult(message, MessageEvent.createEvent(MessageEvent.RESULT, actualMessage)); |
| } |
| else |
| { |
| NetworkMonitor.monitorInvocation(getNetmonId(), message, this); |
| } |
| } |
| } |
| |
| /** |
| * Return the id for the NetworkMonitor. |
| * @private |
| */ |
| mx_internal function getNetmonId():String |
| { |
| return null; |
| } |
| } |
| |
| } |