blob: d7f536c33aa21fa6fa2937eb341ec53476d46e69 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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.messages
{
import org.apache.royale.utils.BinaryData;
import org.apache.royale.net.utils.IDataInput;
import org.apache.royale.net.utils.IDataOutput;
import org.apache.royale.reflection.getQualifiedClassName;
import mx.core.mx_internal;
//import mx.utils.RPCObjectUtil;
import mx.utils.RPCStringUtil;
import mx.utils.RPCUIDUtil;
use namespace mx_internal;
/**
* Abstract base class for all messages.
* Messages have two customizable sections; headers and body.
* The <code>headers</code> property provides access to specialized meta
* information for a specific message instance.
* The <code>headers</code> property is an associative array with the specific
* header name as the key.
* <p>
* The body of a message contains the instance specific data that needs to be
* delivered and processed by the remote destination.
* The <code>body</code> is an object and is the payload for a message.
* </p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public class AbstractMessage implements IMessage
{
//--------------------------------------------------------------------------
//
// Static Constants
//
//--------------------------------------------------------------------------
/**
* Messages pushed from the server may arrive in a batch, with messages in the
* batch potentially targeted to different Consumer instances.
* Each message will contain this header identifying the Consumer instance that
* will receive the message.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public static const DESTINATION_CLIENT_ID_HEADER:String = "DSDstClientId";
/**
* Messages are tagged with the endpoint id for the Channel they are sent over.
* Channels set this value automatically when they send a message.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public static const ENDPOINT_HEADER:String = "DSEndpoint";
/**
* This header is used to transport the global FlexClient Id value in outbound
* messages once it has been assigned by the server.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public static const FLEX_CLIENT_ID_HEADER:String = "DSId";
/**
* Messages sent by a MessageAgent can have a priority header with a 0-9
* numerical value (0 being lowest) and the server can choose to use this
* numerical value to prioritize messages to clients.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public static const PRIORITY_HEADER:String = "DSPriority";
/**
* Messages that need to set remote credentials for a destination
* carry the Base64 encoded credentials in this header.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public static const REMOTE_CREDENTIALS_HEADER:String = "DSRemoteCredentials";
/**
* Messages that need to set remote credentials for a destination
* may also need to report the character-set encoding that was used to
* create the credentials String using this header.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public static const REMOTE_CREDENTIALS_CHARSET_HEADER:String = "DSRemoteCredentialsCharset";
/**
* Messages sent with a defined request timeout use this header.
* The request timeout value is set on outbound messages by services or
* channels and the value controls how long the corresponding MessageResponder
* will wait for an acknowledgement, result or fault response for the message
* before timing out the request.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public static const REQUEST_TIMEOUT_HEADER:String = "DSRequestTimeout";
/**
* A status code can provide context about the nature of a response
* message. For example, messages received from an HTTP based channel may
* need to report the HTTP response status code (if available).
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public static const STATUS_CODE_HEADER:String = "DSStatusCode";
//--------------------------------------------------------------------------
//
// Private Static Constants for Serialization
//
//--------------------------------------------------------------------------
private static const HAS_NEXT_FLAG:uint = 128;
private static const BODY_FLAG:uint = 1;
private static const CLIENT_ID_FLAG:uint = 2;
private static const DESTINATION_FLAG:uint = 4;
private static const HEADERS_FLAG:uint = 8;
private static const MESSAGE_ID_FLAG:uint = 16;
private static const TIMESTAMP_FLAG:uint = 32;
private static const TIME_TO_LIVE_FLAG:uint = 64;
private static const CLIENT_ID_BYTES_FLAG:uint = 1;
private static const MESSAGE_ID_BYTES_FLAG:uint = 2;
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructs an instance of an AbstractMessage with an empty body and header.
* This message type should not be instantiated or used directly.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function AbstractMessage()
{
super();
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// body
//----------------------------------
/**
* @private
*/
private var _body:Object = {};
/**
* The body of a message contains the specific data that needs to be
* delivered to the remote destination.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function get body():Object
{
return _body;
}
/**
* @private
*/
public function set body(value:Object):void
{
_body = value;
}
//----------------------------------
// clientId
//----------------------------------
/**
* @private
*/
private var _clientId:String;
/**
* @private
*/
private var clientIdBytes:BinaryData;
/**
* The clientId indicates which MessageAgent sent the message.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function get clientId():String
{
return _clientId;
}
/**
* @private
*/
public function set clientId(value:String):void
{
_clientId = value;
clientIdBytes = null;
}
//----------------------------------
// destination
//----------------------------------
/**
* @private
*/
private var _destination:String = "";
/**
* The message destination.
*
* @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
{
_destination = value;
}
//----------------------------------
// headers
//----------------------------------
/**
* @private
*/
private var _headers:Object;
/**
* The headers of a message are an associative array where the key is the
* header name and the value is the header value.
* This property provides access to the specialized meta information for the
* specific message instance.
* Core header names begin with a 'DS' prefix. Custom header names should start
* with a unique prefix to avoid name collisions.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function get headers():Object
{
if (_headers == null)
_headers = {};
return _headers;
}
/**
* @private
*/
public function set headers(value:Object):void
{
_headers = value;
}
//----------------------------------
// messageId
//----------------------------------
/**
* @private
*/
private var _messageId:String;
/**
* @private
*/
private var messageIdBytes:BinaryData;
/**
* The unique id for the message.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function get messageId():String
{
if (_messageId == null)
_messageId = RPCUIDUtil.createUID();
return _messageId;
}
/**
* @private
*/
public function set messageId(value:String):void
{
_messageId = value;
messageIdBytes = null;
}
//----------------------------------
// timestamp
//----------------------------------
/**
* @private
*/
private var _timestamp:Number = 0;
/**
* Provides access to the time stamp for the message.
* A time stamp is the date and time that the message was sent.
* The time stamp is used for tracking the message through the system,
* ensuring quality of service levels and providing a mechanism for
* message expiration.
*
* @see #timeToLive
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function get timestamp():Number
{
return _timestamp;
}
/**
* @private
*/
public function set timestamp(value:Number):void
{
_timestamp = value;
}
//----------------------------------
// timeToLive
//----------------------------------
/**
* @private
*/
private var _timeToLive:Number = 0;
/**
* The time to live value of a message indicates how long the message
* should be considered valid and deliverable.
* This value works in conjunction with the <code>timestamp</code> value.
* Time to live is the number of milliseconds that this message remains
* valid starting from the specified <code>timestamp</code> value.
* For example, if the <code>timestamp</code> value is 04/05/05 1:30:45 PST
* and the <code>timeToLive</code> value is 5000, then this message will
* expire at 04/05/05 1:30:50 PST.
* Once a message expires it will not be delivered to any other clients.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
*/
public function get timeToLive():Number
{
return _timeToLive;
}
/**
* @private
*/
public function set timeToLive(value:Number):void
{
_timeToLive = value;
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*
* While this class itself does not implement flash.utils.IExternalizable,
* ISmallMessage implementations will typically use IExternalizable to
* serialize themselves in a smaller form. This method supports this
* functionality by implementing IExternalizable.readExternal(IDataInput) to
* deserialize the properties for this abstract base class.
*/
public function readExternal(input:IDataInput):void
{
var flagsArray:Array = readFlags(input);
for (var i:uint = 0; i < flagsArray.length; i++)
{
var flags:uint = flagsArray[i] as uint;
var reservedPosition:uint = 0;
if (i == 0)
{
if ((flags & BODY_FLAG) != 0)
readExternalBody(input);
else
body = null; // default body is {} so need to set it here
if ((flags & CLIENT_ID_FLAG) != 0)
clientId = input.readObject();
if ((flags & DESTINATION_FLAG) != 0)
destination = input.readObject() as String;
if ((flags & HEADERS_FLAG) != 0)
headers = input.readObject();
if ((flags & MESSAGE_ID_FLAG) != 0)
messageId = input.readObject() as String;
if ((flags & TIMESTAMP_FLAG) != 0)
timestamp = input.readObject() as Number;
if ((flags & TIME_TO_LIVE_FLAG) != 0)
timeToLive = input.readObject() as Number;
reservedPosition = 7;
}
else if (i == 1)
{
if ((flags & CLIENT_ID_BYTES_FLAG) != 0)
{
clientIdBytes = input.readObject() as BinaryData;
clientId = RPCUIDUtil.fromBinary(clientIdBytes);
}
if ((flags & MESSAGE_ID_BYTES_FLAG) != 0)
{
messageIdBytes = input.readObject() as BinaryData;
messageId = RPCUIDUtil.fromBinary(messageIdBytes);
}
reservedPosition = 2;
}
// For forwards compatibility, read in any other flagged objects to
// preserve the integrity of the input stream...
if ((flags >> reservedPosition) != 0)
{
for (var j:uint = reservedPosition; j < 6; j++)
{
if (((flags >> j) & 1) != 0)
{
input.readObject();
}
}
}
}
}
/**
* Returns a string representation of the message.
*
* @return String representation of the message.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion BlazeDS 4
* @productversion LCDS 3
public function toString():String
{
return RPCObjectUtil.toString(this);
}
*/
/**
* @private
*
* While this class itself does not implement flash.utils.IExternalizable,
* ISmallMessage implementations will typically use IExternalizable to
* serialize themselves in a smaller form. This method supports this
* functionality by implementing IExternalizable.writeExternal(IDataOutput)
* to efficiently serialize the properties for this abstract base class.
*/
public function writeExternal(output:IDataOutput):void
{
var flags:uint = 0;
// Since we're using custom serialization, we have to invoke the
// messageId getter to ensure we have a valid id for the message.
var checkForMessageId:String = messageId;
if (clientIdBytes == null)
clientIdBytes = RPCUIDUtil.toBinary(_clientId);
if (messageIdBytes == null)
messageIdBytes = RPCUIDUtil.toBinary(_messageId);
if (body != null)
flags |= BODY_FLAG;
if (clientId != null && clientIdBytes == null)
flags |= CLIENT_ID_FLAG;
if (destination != null)
flags |= DESTINATION_FLAG;
if (headers != null)
flags |= HEADERS_FLAG;
if (messageId != null && messageIdBytes == null)
flags |= MESSAGE_ID_FLAG;
if (timestamp != 0)
flags |= TIMESTAMP_FLAG;
if (timeToLive != 0)
flags |= TIME_TO_LIVE_FLAG;
if (clientIdBytes != null || messageIdBytes != null)
flags |= HAS_NEXT_FLAG;
output.writeByte(flags);
flags = 0;
if (clientIdBytes != null)
flags |= CLIENT_ID_BYTES_FLAG;
if (messageIdBytes != null)
flags |= MESSAGE_ID_BYTES_FLAG;
// This is only read if the previous flag has HAS_NEXT_FLAG set
if (flags != 0)
output.writeByte(flags);
if (body != null)
writeExternalBody(output);
if (clientId != null && clientIdBytes == null)
output.writeObject(clientId);
if (destination != null)
output.writeObject(destination);
if (headers != null)
output.writeObject(headers);
if (messageId != null && messageIdBytes == null)
output.writeObject(messageId);
if (timestamp != 0)
output.writeObject(timestamp);
if (timeToLive != 0)
output.writeObject(timeToLive);
if (clientIdBytes != null)
output.writeObject(clientIdBytes);
if (messageIdBytes != null)
output.writeObject(messageIdBytes);
}
//--------------------------------------------------------------------------
//
// Protected Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
protected function addDebugAttributes(attributes:Object):void
{
attributes["body"] = body;
attributes["clientId"] = clientId;
attributes["destination"] = destination;
attributes["headers"] = headers;
attributes["messageId"] = messageId;
attributes["timestamp"] = timestamp;
attributes["timeToLive"] = timeToLive;
}
/**
* @private
*/
final protected function getDebugString():String
{
var result:String = "(" + getQualifiedClassName(this) + ")";
var attributes:Object = {};
addDebugAttributes(attributes);
var propertyNames:Array = [];
for (var propertyName:String in attributes)
{
propertyNames.push(propertyName);
}
propertyNames.sort();
var length:int = propertyNames.length;
for (var i:uint = 0; i < length; i++)
{
var name:String = String(propertyNames[i]);
//var value:String = RPCObjectUtil.toString(attributes[name]);
var attrValue:Object = attributes[name];
var value:String = attrValue == null ? "null" : attrValue.toString();
result += RPCStringUtil.substitute("\n {0}={1}", name, value);
}
return result;
}
public function toString():String
{
return getDebugString();
}
/**
* @private
*/
protected function readExternalBody(input:IDataInput):void
{
body = input.readObject();
}
/**
* @private
* To support efficient serialization for ISmallMessage implementations,
* this utility method reads in the property flags from an IDataInput
* stream. Flags are read in one byte at a time. Flags make use of
* sign-extension so that if the high-bit is set to 1 this indicates that
* another set of flags follows.
*
* @return The Array of property flags. Each flags byte is stored as a uint
* in the Array.
*/
protected function readFlags(input:IDataInput):Array
{
var hasNextFlag:Boolean = true;
var flagsArray:Array = [];
while (hasNextFlag && input.bytesAvailable > 0)
{
var flags:uint = input.readUnsignedByte();
flagsArray.push(flags);
if ((flags & HAS_NEXT_FLAG) != 0)
hasNextFlag = true;
else
hasNextFlag = false;
}
return flagsArray;
}
/**
* @private
*/
protected function writeExternalBody(output:IDataOutput):void
{
output.writeObject(body);
}
}
}