////////////////////////////////////////////////////////////////////////////////
//
// 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 flash.utils.ByteArray;
import flash.utils.IDataInput;
import flash.utils.IDataOutput;
import flash.utils.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 headers
property provides access to specialized meta
* information for a specific message instance.
* The headers
property is an associative array with the specific
* header name as the key.
*
* The body of a message contains the instance specific data that needs to be
* delivered and processed by the remote destination.
* The body
is an object and is the payload for a message.
*
timestamp
value.
* Time to live is the number of milliseconds that this message remains
* valid starting from the specified timestamp
value.
* For example, if the timestamp
value is 04/05/05 1:30:45 PST
* and the timeToLive
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 ByteArray;
clientId = RPCUIDUtil.fromByteArray(clientIdBytes);
}
if ((flags & MESSAGE_ID_BYTES_FLAG) != 0)
{
messageIdBytes = input.readObject() as ByteArray;
messageId = RPCUIDUtil.fromByteArray(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.toByteArray(_clientId);
if (messageIdBytes == null)
messageIdBytes = RPCUIDUtil.toByteArray(_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();
for (var i:uint = 0; i < propertyNames.length; i++)
{
var name:String = String(propertyNames[i]);
var value:String = RPCObjectUtil.toString(attributes[name]);
result += RPCStringUtil.substitute("\n {0}={1}", name, value);
}
return result;
}
/**
* @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);
}
}
}