This is an automated email from the ASF dual-hosted git repository. aharui pushed a commit to branch webservice in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
commit bab0a204c68831522523df105c7c3da1f0e7a188 Author: Alex Harui <[email protected]> AuthorDate: Mon Sep 30 11:03:32 2019 -0700 beginnings of implementation of WebService --- .../MXRoyale/src/main/royale/MXRoyaleClasses.as | 1 + .../royale/mx/messaging/channels/HTTPChannel.as | 1268 ++++++++++++++++++++ .../mx/messaging/channels/SecureHTTPChannel.as | 80 ++ .../mx/messaging/channels/amfx/AMFXContext.as | 304 +++++ .../mx/messaging/channels/amfx/AMFXDecoder.as | 638 ++++++++++ .../mx/messaging/channels/amfx/AMFXEncoder.as | 510 ++++++++ .../mx/messaging/channels/amfx/AMFXHeader.as | 45 + .../mx/messaging/channels/amfx/AMFXResult.as | 41 + .../royale/mx/messaging/config/ServerConfig.as | 28 +- .../MXRoyale/src/main/royale/mx/net/URLLoader.as | 5 + .../src/main/royale/mx/rpc/soap/LoadEvent.as | 6 +- .../src/main/royale/mx/rpc/soap/Operation.as | 71 +- .../src/main/royale/mx/rpc/soap/SOAPDecoder.as | 29 +- .../src/main/royale/mx/rpc/soap/SOAPEncoder.as | 8 +- .../src/main/royale/mx/rpc/soap/SOAPFault.as | 1 - .../src/main/royale/mx/rpc/soap/WebService.as | 43 +- .../src/main/royale/mx/rpc/soap/mxml/Operation.as | 2 +- .../src/main/royale/mx/rpc/soap/mxml/WebService.as | 29 +- .../MXRoyale/src/main/royale/mx/rpc/wsdl/WSDL.as | 2 +- .../src/main/royale/mx/rpc/wsdl/WSDLLoader.as | 2 +- 20 files changed, 3046 insertions(+), 67 deletions(-) diff --git a/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as b/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as index 9619309..3c14ebe 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as +++ b/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as @@ -198,6 +198,7 @@ internal class MXRoyaleClasses import mx.rpc.http.HTTPMultiService; HTTPMultiService; import mx.messaging.messages.HTTPRequestMessage; HTTPRequestMessage; import mx.messaging.channels.DirectHTTPChannel; DirectHTTPChannel; + import mx.messaging.channels.HTTPChannel; HTTPChannel; import mx.messaging.errors.MessageSerializationError; MessageSerializationError; import mx.rpc.http.SerializationFilter; SerializationFilter; import mx.rpc.http.AbstractOperation; AbstractOperation; diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/HTTPChannel.as b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/HTTPChannel.as new file mode 100644 index 0000000..e520f07 --- /dev/null +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/HTTPChannel.as @@ -0,0 +1,1268 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.channels +{ + +import mx.events.ErrorEvent; +import org.apache.royale.events.Event; +import mx.events.IOErrorEvent; +import mx.events.SecurityErrorEvent; +import mx.net.URLLoader; +import org.apache.royale.net.URLRequest; + +import mx.core.mx_internal; +import mx.messaging.RoyaleClient; +import mx.messaging.MessageAgent; +import mx.messaging.MessageResponder; +import mx.messaging.channels.amfx.AMFXDecoder; +import mx.messaging.channels.amfx.AMFXEncoder; +import mx.messaging.channels.amfx.AMFXHeader; +import mx.messaging.channels.amfx.AMFXResult; +import mx.messaging.config.ConfigMap; +import mx.messaging.config.LoaderConfig; +import mx.messaging.config.ServerConfig; +import mx.messaging.errors.MessageSerializationError; +import mx.messaging.events.ChannelFaultEvent; +import mx.messaging.messages.AbstractMessage; +import mx.messaging.messages.AcknowledgeMessage; +import mx.messaging.messages.AsyncMessage; +import mx.messaging.messages.CommandMessage; +import mx.messaging.messages.ErrorMessage; +import mx.messaging.messages.HTTPRequestMessage; +import mx.messaging.messages.IMessage; +import mx.messaging.messages.MessagePerformanceInfo; +import mx.messaging.messages.MessagePerformanceUtils; +import mx.netmon.NetworkMonitor; +import mx.utils.ObjectUtil; +import mx.utils.StringUtil; + +use namespace mx_internal; + +/** + * The HTTPChannel class provides the HTTP support for messaging. + * You can configure this Channel to poll the server at an interval + * to approximate server push. + * You can also use this Channel with polling disabled to send RPC messages + * to remote destinations to invoke their methods. + * + * <p> + * The HTTPChannel relies on network services native to Flash Player and AIR, + * and exposed to ActionScript by the URLLoader class. + * This channel uses URLLoader exclusively, and creates a new URLLoader + * per request. + * </p> + * + * <p> + * Channels are created within the framework using the + * <code>ServerConfig.getChannel()</code> method. Channels can be constructed + * directly and assigned to a ChannelSet if desired. + * </p> + * + * <p> + * Channels represent a physical connection to a remote endpoint. + * Channels are shared across destinations by default. + * This means that a client targetting different destinations may use + * the same Channel to communicate with these destinations. + * </p> + * + * <p> + * When used in polling mode, this Channel polls the server for new messages + * based on the <code>polling-interval-seconds</code> property in the configuration file, + * and this can be changed by setting the <code>pollingInterval</code> property. + * The default value is 3 seconds. + * To enable polling, the channel must be connected and the <code>polling-enabled</code> + * property in the configuration file must be set to <code>true</code>, or the + * <code>pollingEnabled</code> property of the Channel must be set to <code>true</code>. + * </p> + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ +public class HTTPChannel extends PollingChannel +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param id The id of this Channel. + * @param uri The uri for this Channel. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function HTTPChannel(id:String = null, uri:String = null) + { + super(id, uri); + + _encoder = new AMFXEncoder(); + _appendToURL = ""; + _messageQueue = []; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var _appendToURL:String; + + /** + * @private + * The loader used to ping the server in internalConnect. We need to hang onto a reference + * in order to time out a connect attempt. + */ + private var _connectLoader:ChannelRequestLoader; + + /** + * @private + */ + private var _encoder:AMFXEncoder; + + /** + * @private + * Records the request that needs to be completed before other + * requests can be sent. + */ + private var _pendingRequest:ChannelRequestLoader = null; + + /** + * @private + * This queue contains the messages from send requests that + * occurred while an authentication attempt is underway. + */ + private var _messageQueue:Array; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // polling + //---------------------------------- + + /** + * Reports whether the channel is actively polling. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function get polling():Boolean + { + return pollOutstanding; + } + + //---------------------------------- + // piggybackingEnabled + //---------------------------------- + + /** + * Indicates whether this channel will piggyback poll requests along + * with regular outbound messages when an outstanding poll is not in + * progress. This allows the server to piggyback data for the client + * along with its response to client's message. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function get piggybackingEnabled():Boolean + { + return internalPiggybackingEnabled; + } + + /** + * @private + */ + public function set piggybackingEnabled(value:Boolean):void + { + internalPiggybackingEnabled = value; + } + + //---------------------------------- + // pollingEnabled + //---------------------------------- + + /** + * Indicates whether this channel is enabled to poll. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function get pollingEnabled():Boolean + { + return internalPollingEnabled; + } + + /** + * @private + */ + public function set pollingEnabled(value:Boolean):void + { + internalPollingEnabled = value; + } + + //---------------------------------- + // pollingInterval + //---------------------------------- + + /** + * Provides access to the polling interval for this Channel. + * The value is in milliseconds. + * This value determines how often this Channel requests messages from + * the server, to approximate server push. + * + * @throws ArgumentError If the pollingInterval is assigned a value of 0 or + * less. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function get pollingInterval():Number + { + return internalPollingInterval; + } + + /** + * @private + */ + public function set pollingInterval(value:Number):void + { + internalPollingInterval = value; + } + + //---------------------------------- + // protocol + //---------------------------------- + + /** + * Returns the protocol for this channel (http). + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + override public function get protocol():String + { + return "http"; + } + + //-------------------------------------------------------------------------- + // + // Internal Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // appendToURL + //---------------------------------- + + /** + * @private + */ + mx_internal function get appendToURL():String + { + return _appendToURL; + } + + /** + * @private + */ + mx_internal function set appendToURL(value:String):void + { + if (value && endpoint) + { + _appendToURL = value; + } + } + + //-------------------------------------------------------------------------- + // + // Overridden Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Processes polling related configuration settings. + */ + override public function applySettings(settings:XML):void + { + super.applySettings(settings); + applyPollingSettings(settings); + } + + //-------------------------------------------------------------------------- + // + // Overridden Protected Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function connectTimeoutHandler(event:Event):void + { + _connectLoader.close(); + super.connectTimeoutHandler(event); + } + + /** + * @private + */ + override protected function getDefaultMessageResponder(agent:MessageAgent, msg:IMessage):MessageResponder + { + return new HTTPMessageResponder(agent, msg, this); + } + + /** + * @private + * Attempts to connect to the remote destination with the current endpoint + * specified for this channel. + * This will determine if a connection can be established. + */ + override protected function internalConnect():void + { + // Ping the server to make sure that it is reachable. + var msg:CommandMessage = new CommandMessage(); + if (credentials != null) + { + msg.operation = CommandMessage.LOGIN_OPERATION; + msg.body = credentials; + } + else + { + msg.operation = CommandMessage.CLIENT_PING_OPERATION; + } + + // Report the messaging version for this Channel. + // msg.headers[CommandMessage.MESSAGING_VERSION] = messagingVersion; + + // Indicate if requesting the dynamic configuration from the server. + if (ServerConfig.needsConfig(this)) + msg.headers[CommandMessage.NEEDS_CONFIG_HEADER] = true; + + // Add the FlexClient id header. + setRoyaleClientIdOnMessage(msg); + + var urlRequest:URLRequest = createURLRequest(msg); + _connectLoader = new ChannelRequestLoader(); + _connectLoader.setErrorCallbacks(pingErrorHandler); + _connectLoader.completeCallback = pingCompleteHandler; + _connectLoader.load(urlRequest); + } + + /** + * @private + * Disconnects from the remote destination. + */ + override protected function internalDisconnect(rejected:Boolean = false):void + { + // Attempt to notify the server of the disconnect. + if (!rejected && !shouldBeConnected) + { + var msg:CommandMessage = new CommandMessage(); + msg.operation = CommandMessage.DISCONNECT_OPERATION; + internalSend(new MessageResponder(null, msg, null)); + } + // Shutdown locally. + setConnected(false); + super.internalDisconnect(rejected); + disconnectSuccess(rejected); // make sure to notify everyone that we have disconnected. + } + + /** + * @private + */ + override protected function internalSend(msgResp:MessageResponder):void + { + if (_pendingRequest != null) + { + _messageQueue.push(msgResp); + } + else + { + // Set the global FlexClient Id. + setRoyaleClientIdOnMessage(msgResp.message); + + try + { + // If MPI is enabled initialize MPI object and stamp it with client send time + if (mpiEnabled) + { + var mpii:MessagePerformanceInfo = new MessagePerformanceInfo(); + if (recordMessageTimes) + mpii.sendTime = new Date().getTime(); + msgResp.message.headers[MessagePerformanceUtils.MPI_HEADER_IN] = mpii; + } + + // Finally, if "Small Messages" are enabled, send this form instead of + // the normal message where possible. + /* + if (useSmallMessages && msgResp.message is ISmallMessage) + { + var smallMessage:IMessage = ISmallMessage(msgResp.message).getSmallMessage(); + if (smallMessage != null) + msgResp.message = smallMessage; + } + */ + + var urlLoader:ChannelRequestLoader; + var urlRequest:URLRequest = createURLRequest(msgResp.message); + if (msgResp is HTTPMessageResponder) + { + var httpMsgResp:HTTPMessageResponder = + HTTPMessageResponder(msgResp); + urlLoader = httpMsgResp.urlLoader; + urlLoader.completeCallback = httpMsgResp.completeHandler; + urlLoader.errorCallback = httpMsgResp.errorHandler; + urlLoader.ioErrorCallback = httpMsgResp.ioErrorHandler; + urlLoader.securityErrorCallback = + httpMsgResp.securityErrorHandler; + } + else + { + var responderWrapper:HTTPWrapperResponder = + new HTTPWrapperResponder(msgResp); + urlLoader = new ChannelRequestLoader(); + urlLoader.completeCallback = + responderWrapper.completeHandler; + urlLoader.setErrorCallbacks(responderWrapper.errorHandler); + } + urlLoader.requestProcessedCallback = requestProcessedHandler; + + // Do not consider poll requests as pending requests to allow + // clients to send messages while waiting for poll response. + if (!(msgResp.message is CommandMessage && CommandMessage(msgResp.message).operation == CommandMessage.POLL_OPERATION)) + _pendingRequest = urlLoader; + + urlLoader.load(urlRequest); + } + catch(e:MessageSerializationError) + { + msgResp.agent.fault(e.fault, msgResp.message); + } + } + } + + //-------------------------------------------------------------------------- + // + // Internal Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Utility function to handle a connection related ErrorMessage. + * + * @param msg The ErrorMessage returned during a connect attempt. + */ + mx_internal function connectionError(msg:ErrorMessage):void + { + var faultEvent:ChannelFaultEvent = ChannelFaultEvent.createEvent(this, false, + "Channel.Connect.Failed", "error", msg.faultDetail + " url: '" + endpoint + + (_appendToURL != null ? _appendToURL : "") + "'"); + faultEvent.rootCause = msg; + connectFailed(faultEvent); + } + + /** + * @private + * This method will serialize the specified message into a new instance of + * a URLRequest and return it. + * + * @param message Message to serialize + * @return URLRequest + */ + mx_internal function createURLRequest(message:IMessage):URLRequest + { + var result:URLRequest = new URLRequest(); + if (_appendToURL) + result.url = endpoint + _appendToURL; + else + result.url = endpoint; + + // Propagate our requestTimeout for those platforms + // supporting the idleTimeout property on URLRequest. + if ("idleTimeout" in result && requestTimeout > 0) + result["idleTimeout"] = requestTimeout * 1000; + + monitorRpcMessage(message, result); + + result.contentType = HTTPRequestMessage.CONTENT_TYPE_XML; + + var packet:XML = _encoder.encode(message, null); + result.data = packet.toString(); + result.method = "POST"; + + return result; + } + + /** + * change the result url to redirect request to Network Monitor + */ + + private function monitorRpcMessage(message:IMessage, result:URLRequest):void + { + if (NetworkMonitor.isMonitoring()) + { + var redirectedUrl:String = NetworkMonitor.adjustNetConnectionURL(LoaderConfig.url, result.url); + if(redirectedUrl != null){ + result.url = redirectedUrl; + } + } + } + + //-------------------------------------------------------------------------- + // + // Protected Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + protected function internalPingComplete(msg:AsyncMessage):void + { + if (msg != null) + { + ServerConfig.updateServerConfigData(msg.body as ConfigMap, endpoint); + + // Set the server assigned FlexClient Id. + if (RoyaleClient.getInstance().id == null && msg.headers[AbstractMessage.FLEX_CLIENT_ID_HEADER] != null) + RoyaleClient.getInstance().id = msg.headers[AbstractMessage.FLEX_CLIENT_ID_HEADER]; + } + + // Process the features advertised by the server endpoint. + /* + if (msg.headers[CommandMessage.MESSAGING_VERSION] != null) + { + var serverVersion:Number = msg.headers[CommandMessage.MESSAGING_VERSION] as Number; + handleServerMessagingVersion(serverVersion); + } + */ + + connectSuccess(); + if (credentials != null && !(msg is ErrorMessage)) + setAuthenticated(true); + } + + //-------------------------------------------------------------------------- + // + // Private Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Special handler for AMFX packet level header "AppendToGatewayUrl". + * When we receive this header we assume the server detected that a session + * was created but it believed the client could not accept its session + * cookie, so we need to decorate the channel endpoint with the session id. + * + * We do not modify the underlying endpoint property, however, as this + * session is transient and should not apply if the channel is disconnected + * and re-connected at some point in the future. + */ + private function AppendToGatewayUrl(value:String):void + { + if (value != null) + appendToURL = value; + } + + private function decodePacket(event:Event):AMFXResult + { + var raw:String = String(URLLoader(event.target).data); + var xmlData:XML = new XML(raw); + var _decoder:AMFXDecoder = new AMFXDecoder(); + var packet:AMFXResult = _decoder.decode(xmlData); + return packet; + } + + /** + * @private + * Attempts to replicate the packet-level header functionality that AMFChannel + * uses for response headers such as AppendToGatewayUrl for session id tracking. + */ + private function processHeaders(packet:AMFXResult):void + { + if (packet.headers != null) + { + try + { + for (var i:uint = 0; i < packet.headers.length; i++) + { + var header:AMFXHeader = packet.headers[i]; + if (header != null && header.name == APPEND_TO_URL_HEADER) + { + AppendToGatewayUrl(String(header.content)); + } + } + } + catch(e:Error) + { + } + } + } + + /** + * @private + * This method indicates that we successfully connected to the endpoint. + * Called as a result of the ping operation performed in the + * internalConnect() method. + */ + private function pingCompleteHandler(event:Event):void + { + var packet:AMFXResult = decodePacket(event); + processHeaders(packet); + var msg:AsyncMessage = packet.result as AsyncMessage; + if (msg != null && (msg is ErrorMessage) && + ErrorMessage(msg).faultCode == "Client.Authentication") + { + internalPingComplete(msg); + var faultEvent:ChannelFaultEvent = ChannelFaultEvent.createEvent(this, false, "Channel.Authentication.Error", "warn"); + faultEvent.rootCause = ErrorMessage(msg); + dispatchEvent(faultEvent); + } + else + { + internalPingComplete(msg); + } + } + + /** + * @private + * This method dispatches the appropriate error to any message agents, and + * is called as a result of the ping operation performed in the + * internalConnect() method. + */ + private function pingErrorHandler(event:Event):void + { + _log.debug("'{0}' fault handler called. {1}", id, event.toString()); + var faultEvent:ChannelFaultEvent = ChannelFaultEvent.createEvent(this, false, + "Channel.Ping.Failed", + "error", + " url: '" + endpoint + + (_appendToURL == null ? "" : _appendToURL + "'") + "'"); + faultEvent.rootCause = event; + connectFailed(faultEvent); + } + + /** + * @private + * Chains sends for pending messages. + */ + private function requestProcessedHandler + (loader:ChannelRequestLoader, event:Event):void + { + if (_pendingRequest == loader) + { + _pendingRequest = null; + } + // TODO: we should do these in a batch for more efficiency and + // better session maintenance + while ((_messageQueue.length > 0) && (_pendingRequest == null)) + { + internalSend(MessageResponder(_messageQueue.shift())); + } + } + + //-------------------------------------------------------------------------- + // + // Static Constants + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private static const APPEND_TO_URL_HEADER:String = "AppendToGatewayUrl"; +} +} + +//------------------------------------------------------------------------------ +// +// Private Classes +// +//------------------------------------------------------------------------------ + +import mx.events.ErrorEvent; +import org.apache.royale.events.Event; +import mx.events.IOErrorEvent; +import mx.events.SecurityErrorEvent; +import mx.net.URLLoader; +import org.apache.royale.net.URLRequest; +import mx.core.mx_internal; +import mx.messaging.MessageAgent; +import mx.messaging.MessageResponder; +import mx.messaging.channels.HTTPChannel; +import mx.messaging.channels.amfx.AMFXDecoder; +import mx.messaging.channels.amfx.AMFXHeader; +import mx.messaging.channels.amfx.AMFXResult; +import mx.messaging.messages.AcknowledgeMessage; +import mx.messaging.messages.AsyncMessage; +import mx.messaging.messages.CommandMessage; +import mx.messaging.messages.ErrorMessage; +import mx.messaging.messages.HTTPRequestMessage; +import mx.messaging.messages.IMessage; +import mx.resources.IResourceManager; +import mx.resources.ResourceManager; +import mx.utils.StringUtil; + +use namespace mx_internal; + +[ResourceBundle("messaging")] + +/** + * @private + * This responder wraps another MessageResponder with HTTP functionality. + */ +class HTTPWrapperResponder +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * Constructs a HTTPWrappedResponder. + * + * @param wrappedResponder The responder to wrap. + */ + public function HTTPWrapperResponder(wrappedResponder:MessageResponder) + { + super(); + _wrappedResponder = wrappedResponder; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var _wrappedResponder:MessageResponder; + + + /** + * @private + */ + private var resourceManager:IResourceManager = + ResourceManager.getInstance(); + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Handles a result returned from the remote destination. + * + * @param event The completion event from the associated URLLoader. + */ + public function completeHandler(event:Event):void + { + var raw:String = String(URLLoader(event.target).data); + var xmlData:XML = new XML(raw); + var _decoder:AMFXDecoder = new AMFXDecoder(); + var packet:AMFXResult = _decoder.decode(xmlData); + if (packet.result is ErrorMessage) + { + _wrappedResponder.status(ErrorMessage(packet.result)); + } + else if (packet.result is AsyncMessage) + { + _wrappedResponder.result(AsyncMessage(packet.result)); + } + } + + /** + * @private + * Handles an error for an outbound request. + * + * @param event The error event from the associated URLLoader. + */ + public function errorHandler(event:Event):void + { + var msg:ErrorMessage = new ErrorMessage(); + msg.correlationId = _wrappedResponder.message.messageId; + msg.faultCode = "Server.Error.Request"; + msg.faultString = resourceManager.getString( + "messaging", "httpRequestError"); + var details:String = event.toString(); + if (_wrappedResponder.message is HTTPRequestMessage) + { + details += ". URL: "; + details += HTTPRequestMessage(_wrappedResponder.message).url; + } + msg.faultDetail = resourceManager.getString( + "messaging", "httpRequestError.details", [ details ]); + msg.rootCause = event; + _wrappedResponder.status(msg); + } +} + + +[ResourceBundle("messaging")] + +/** + * @private + * This is an adapter for url loader that is used by the HTTPChannel. + */ +class HTTPMessageResponder extends MessageResponder +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * Constructs an HTTPMessageResponder. + * + * @param agent The associated MessageAgent. + * + * @param msg The message to send. + * + * @param channel The Channel to send the message over. + */ + public function HTTPMessageResponder + (agent:MessageAgent, msg:IMessage, channel:HTTPChannel) + { + super(agent, msg, channel); + decoder = new AMFXDecoder(); + urlLoader = new ChannelRequestLoader(); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var decoder:AMFXDecoder; + + /** + * @private + */ + private var resourceManager:IResourceManager = + ResourceManager.getInstance(); + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * The loader associated with this responder. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public var urlLoader:ChannelRequestLoader; + + //-------------------------------------------------------------------------- + // + // Overridden Protected Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override protected function resultHandler(response:IMessage):void + { + var errorMsg:ErrorMessage; + + if (response is AsyncMessage) + { + if (response is ErrorMessage) + { + agent.fault(ErrorMessage(response), message); + } + else if (AsyncMessage(response).correlationId == message.messageId) + { + agent.acknowledge(AcknowledgeMessage(response), message); + } + else + { + errorMsg = new ErrorMessage(); + errorMsg.faultCode = "Server.Acknowledge.Failed"; + errorMsg.faultString = resourceManager.getString( + "messaging", "ackFailed"); + errorMsg.faultDetail = resourceManager.getString( + "messaging", "ackFailed.details", + [ message.messageId, AsyncMessage(response).correlationId ]); + agent.fault(errorMsg, message); + } + } + else if (response != null) + { + errorMsg = new ErrorMessage(); + errorMsg.faultCode = "Server.Acknowledge.Failed"; + errorMsg.faultString = resourceManager.getString( + "messaging", "noAckMessage"); + errorMsg.faultDetail = resourceManager.getString( + "messaging", "noAckMessage.details", + [ mx.utils.ObjectUtil.toString(response) ]); + agent.fault(errorMsg, message); + } + } + + /** + * @private + * Handle a request timeout by closing our associated URLLoader and + * faulting the message to the agent. + */ + override protected function requestTimedOut():void + { + urlLoader.close(); + + status(null); + // send the ack + var ack:AcknowledgeMessage = new AcknowledgeMessage(); + ack.correlationId = message.messageId; + ack.headers[AcknowledgeMessage.ERROR_HINT_HEADER] = true; // hint there was an error + agent.acknowledge(ack, message); + // send the fault + agent.fault(createRequestTimeoutErrorMessage(), message); + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + final public function completeHandler(event:Event):void + { + result(null); + + var raw:String = String(URLLoader(event.target).data); + var xmlData:XML = new XML(raw); + var packet:AMFXResult = decoder.decode(xmlData); + + if (packet.result is IMessage) + { + resultHandler(IMessage(packet.result)); + } + } + + /** + * @private + */ + public function errorHandler(event:Event):void + { + status(null); + // send the ack + var ack:AcknowledgeMessage = new AcknowledgeMessage(); + ack.correlationId = message.messageId; + ack.headers[AcknowledgeMessage.ERROR_HINT_HEADER] = true; // hint there was an error + agent.acknowledge(ack, message); + // send fault + var msg:ErrorMessage = new ErrorMessage(); + msg.correlationId = message.messageId; + msg.faultCode = "Server.Error.Request"; + msg.faultString = resourceManager.getString( + "messaging", "httpRequestError"); + var details:String = event.toString(); + if (message is HTTPRequestMessage) + { + details += ". URL: "; + details += HTTPRequestMessage(message).url; + } + msg.faultDetail = resourceManager.getString( + "messaging", "httpRequestError.details", [ details ]); + msg.rootCause = event; + agent.fault(msg, message); + } + + /** + * @private + */ + public function ioErrorHandler(event:Event):void + { + status(null); + // send the ack + var ack:AcknowledgeMessage = new AcknowledgeMessage(); + ack.correlationId = message.messageId; + ack.headers[AcknowledgeMessage.ERROR_HINT_HEADER] = true; // hint there was an error + agent.acknowledge(ack, message); + // send fault + var msg:ErrorMessage = new ErrorMessage(); + msg.correlationId = message.messageId; + msg.faultCode = "Server.Error.Request"; + msg.faultString = resourceManager.getString( + "messaging", "httpRequestError"); + var details:String = event.toString(); + if (message is HTTPRequestMessage) + { + details += ". URL: "; + details += HTTPRequestMessage(message).url; + } + msg.faultDetail = resourceManager.getString( + "messaging", "httpRequestError.details", [ details ]); + msg.rootCause = event; + + (channel as HTTPChannel).connectionError(msg); + + // already disconnected, now let the agent know the the message faulted + // this is similar to the disconnect() and fault() in the NetConnectionChannel statusHandler + agent.fault(msg, message); + } + + /** + * @private + */ + public function securityErrorHandler(event:Event):void + { + status(null); + // send the ack + var ack:AcknowledgeMessage = new AcknowledgeMessage(); + ack.correlationId = message.messageId; + ack.headers[AcknowledgeMessage.ERROR_HINT_HEADER] = true; // hint there was an error + agent.acknowledge(ack, message); + // send fault + var msg:ErrorMessage = new ErrorMessage(); + msg.correlationId = message.messageId; + msg.faultCode = "Channel.Security.Error"; + msg.faultString = resourceManager.getString( + "messaging", "securityError"); + msg.faultDetail = resourceManager.getString( + "messaging", "securityError.details", [ message.destination ]); + msg.rootCause = event; + agent.fault(msg, message); + } +} + +/** + * @private + * Wraps an URLLoader and manages dispatching its events to the proper handlers. + */ +class ChannelRequestLoader +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * Constructs a ChannelRequestLoader. + */ + public function ChannelRequestLoader() + { + super(); + _urlLoader = new URLLoader(); + _urlLoader.addEventListener(ErrorEvent.ERROR, errorHandler); + _urlLoader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); + _urlLoader.addEventListener + (SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); + _urlLoader.addEventListener(Event.COMPLETE, completeHandler); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * The wrapped URLLoader. + */ + private var _urlLoader:URLLoader; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public var errorCallback:Function; + + /** + * @private + */ + public var ioErrorCallback:Function; + + /** + * @private + */ + public var securityErrorCallback:Function; + + /** + * @private + */ + public var completeCallback:Function; + + /** + * @private + */ + public var requestProcessedCallback:Function; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public function load(request:URLRequest):void + { + _urlLoader.load(request); + } + + /** + * @private + */ + public function close():void + { + _urlLoader.removeEventListener(ErrorEvent.ERROR, errorHandler); + _urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); + _urlLoader.removeEventListener + (SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); + _urlLoader.removeEventListener(Event.COMPLETE, completeHandler); + _urlLoader.close(); + } + + /** + * @private + */ + public function setErrorCallbacks(callback:Function):void + { + errorCallback = callback; + ioErrorCallback = callback; + securityErrorCallback = callback; + } + + //-------------------------------------------------------------------------- + // + // Private Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function callRequestProcessedCallback(event:Event):void + { + if (requestProcessedCallback != null) + requestProcessedCallback(this, event); + } + + /** + * @private + */ + private function callEventCallback(callback:Function, event:Event):void + { + if (callback != null) + callback(event); + } + + /** + * @private + */ + private function errorHandler(event:Event):void + { + callRequestProcessedCallback(event); + callEventCallback(requestProcessedCallback, event); + } + + /** + * @private + */ + private function ioErrorHandler(event:Event):void + { + callRequestProcessedCallback(event); + callEventCallback(ioErrorCallback, event); + } + + /** + * @private + */ + private function securityErrorHandler(event:Event):void + { + callRequestProcessedCallback(event); + callEventCallback(securityErrorCallback, event); + } + + /** + * @private + */ + private function completeHandler(event:Event):void + { + callRequestProcessedCallback(event); + callEventCallback(completeCallback, event); + } +} \ No newline at end of file diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/SecureHTTPChannel.as b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/SecureHTTPChannel.as new file mode 100644 index 0000000..87e4a83 --- /dev/null +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/SecureHTTPChannel.as @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.channels +{ + +/** + * The SecureHTTPChannel class is identical to the HTTPChannel class except that it uses a + * secure protocol, HTTPS, to send messages to an HTTP endpoint. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ +public class SecureHTTPChannel extends HTTPChannel +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param id The id of this Channel. + * + * @param uri The uri for this Channel. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function SecureHTTPChannel(id:String = null, uri:String = null) + { + super(id, uri); + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * Returns the protocol for this channel (https). + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + override public function get protocol():String + { + return "https"; + } +} + +} diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXContext.as b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXContext.as new file mode 100644 index 0000000..fe4caf9 --- /dev/null +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXContext.as @@ -0,0 +1,304 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.channels.amfx +{ + +import mx.logging.ILogger; + +[ExcludeClass] + +/** + * Holds a list of complex object references, object trait info references, + * or string references generated while encoding or decoding and AMFX packet. + * Note that a new set of reference tables should be used per AMFX packet. + * Calling reset() will create new tables for each of these types of references. + * @private + */ +public class AMFXContext +{ + /** + * Constructor. + * Initializes object, trait info and string reference tables. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function AMFXContext() + { + super(); + reset(); + } + + /** + * Resets the trait info, object and string reference tables. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function reset():void + { + traits = []; + objects = []; + strings = []; + } + + /** + * Check whether the trait info reference table + * already contains this list of traits. If found, + * the index of the trait info is returned, starting + * from 0. If not found -1 is returned. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function findTraitInfo(traitInfo:Object):int + { + for (var i:uint = 0; i < traits.length; i++) + { + var ti:Object = traits[i]; + + if (ti.alias == traitInfo.alias + && ti.properties.length == traitInfo.properties.length) + { + var j:uint = 0; + for (; j < ti.properties.length; j++) + { + if (ti.properties[i] != traitInfo.properties[j]) + { + break; + } + } + + if (j == traitInfo.properties.length) + { + //Match found + return i; + } + } + } + + return -1; + } + + /** + * Check whether the object reference table + * already contains this object. If found, the index + * of the object is returned, starting from 0. If + * not found -1 is returned. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function findObject(object:Object):int + { + for (var i:uint = 0; i < objects.length; i++) + { + var o:Object = objects[i]; + if (o === object) + { + return i; + } + } + + return -1; + } + + /** + * Check whether the string reference table + * already contains this string. If found, the index + * of the string is returned, starting from 0. If + * not found (or if the value passed is the empty string) + * -1 is returned. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function findString(str:String):int + { + if (str != "") + { + for (var i:uint = 0; i < strings.length; i++) + { + var s:String = strings[i]; + if (s == str) + { + return i; + } + } + } + + return -1; + } + + /** + * Remember the trait info for an object in this context + * for an encoding or decoding session. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function addTraitInfo(traitInfo:Object):void + { + traits.push(traitInfo); + } + + /** + * Remember an object in this context for an encoding + * or decoding session. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function addObject(obj:Object):void + { + objects.push(obj); + } + + /** + * Remember a string in this context for an encoding + * or decoding session. Note that the empty string + * is not remembered as it should not be serialized + * by reference. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function addString(str:String):void + { + if (str != "") + { + strings.push(str); + } + } + + + /** + * Retrieve trait info for an object by its reference + * table index. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function getTraitInfo(ref:uint):* + { + return traits[ref]; + } + + /** + * Retrieve an object by its reference table index. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function getObject(ref:uint):* + { + return objects[ref]; + } + + /** + * Retrieve a string by its reference table index. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public function getString(ref:uint):String + { + return strings[ref]; + } + + /** + * Trait Info reference table + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + internal var traits:Array; + + /** + * Object reference table + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + internal var objects:Array; + + /** + * Strings reference table + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + internal var strings:Array; + + /** + * Log for the current encoder/decoder context. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion BlazeDS 4 + * @productversion LCDS 3 + */ + public var log:ILogger; +} + +} diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXDecoder.as b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXDecoder.as new file mode 100644 index 0000000..7772695 --- /dev/null +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXDecoder.as @@ -0,0 +1,638 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.channels.amfx +{ + +import org.apache.royale.reflection.getClassByAlias; +import mx.utils.ByteArray; +//import flash.utils.Dictionary; +import org.apache.royale.utils.net.IExternalizable; +import org.apache.royale.reflection.getDefinitionByName; + +import mx.logging.Log; +import mx.messaging.errors.ChannelError; +import mx.resources.IResourceManager; +import mx.resources.ResourceManager; +import mx.utils.HexDecoder; + +[ResourceBundle("messaging")] + +[ExcludeClass] + +/** + * Decodes an AMFX packet into an ActionScript Object graph. + * Headers and the result body are accessed from the returned + * AMFXResult. + * @private + */ +public class AMFXDecoder +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + public function AMFXDecoder() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Storage for the resourceManager getter. + * This gets initialized on first access, + * not at static initialization time, in order to ensure + * that the Singleton registry has already been initialized. + */ + private static var _resourceManager:IResourceManager; + + /** + * @private + * A reference to the object which manages + * all of the application's localized resources. + * This is a singleton instance which implements + * the IResourceManager interface. + */ + private static function get resourceManager():IResourceManager + { + if (!_resourceManager) + _resourceManager = ResourceManager.getInstance(); + + return _resourceManager; + } + + //-------------------------------------------------------------------------- + // + // Public Methods + // + //-------------------------------------------------------------------------- + + public function decode(xml:XML):AMFXResult + { + if (xml) + { + var context:AMFXContext = new AMFXContext(); + context.log = Log.getLogger("mx.messaging.channels.amfx.AMFXDecoder"); + XML.ignoreWhitespace = false; + return decodePacket(xml, context); + } + else + { + return null; + } + } + + //-------------------------------------------------------------------------- + // + // Static Methods + // + //-------------------------------------------------------------------------- + + private static function decodePacket(xml:XML, context:AMFXContext):AMFXResult + { + var result:AMFXResult = new AMFXResult(); + + var message:String; + + var name:String = xml.localName() as String; + if (name == "amfx") + { + var v:String = xml.attribute("ver").toString(); + var version:uint = uint(v); + + if (supportedVersion(version)) + { + var h:XMLList = xml.child("header"); + if (h) + { + result.headers = decodeHeaders(h, context); + } + + var body:XML = xml.body[0]; + + if (body) + { + result.result = decodeBody(body, context); + } + else + { + message = resourceManager.getString( + "messaging", "noAMFXBody"); + throw new ChannelError(message); + } + } + else + { + message = resourceManager.getString( + "messaging", "unsupportedAMFXVersion", [ version ]); + throw new ChannelError(message); + } + } + else + { + message = resourceManager.getString( + "messaging", "noAMFXNode") + throw new ChannelError(message); + } + + return result; + } + + private static function decodeHeaders(xmlList:XMLList, context:AMFXContext):Array + { + var headers:Array = []; + + for (var i:uint = 0; i < xmlList.length(); i++) + { + var h:XML = xmlList[i]; + var name:String = h.attribute("name").toString(); + + var header:AMFXHeader = new AMFXHeader(); + header.name = name; + + var temp:String = h.attribute("mustUnderstand").toString(); + header.mustUnderstand = (temp == "true"); + + var children:XMLList = h.children(); + if (children.length() > 0) + { + header.content = decodeValue(children[0], context); + } + else + { + header.content = null; + } + + headers[i] = header; + } + + return headers; + } + + private static function decodeBody(xml:XML, context:AMFXContext):Object + { + var result:Object; + var children:XMLList = xml.children(); + + if (children.length() == 1) + { + result = decodeValue(children[0], context); + } + else if (children.length() > 1) + { + result = []; + for (var i:uint = 0; i < children.length(); i++) + { + result[i] = decodeValue(children[i], context); + } + } + + return result; + } + + public static function decodeValue(xml:XML, context:AMFXContext):Object + { + var result:Object; + var name:String = xml.localName() as String; + + if (name == "string") + { + result = decodeString(xml, context); + } + else if (name == "true") + { + result = true; + } + else if (name == "false") + { + result = false; + } + else if (name == "array") + { + result = decodeArray(xml, context); + } + else if (name == "object") + { + result = decodeObject(xml, context); + } + else if (name == "ref") + { + result = decodeRef(xml, context); + } + /* + else if (name == "dictionary") + { + result = decodeDictionary(xml, context); + }*/ + else if (name == "double") + { + var n:String = xml.text().toString(); + result = Number(n); + } + else if (name == "int") + { + var i:String = xml.text().toString(); + result = int(i); + } + else if (name == "null") + { + result = null; + } + else if (name == "date") + { + result = decodeDate(xml, context); + } + else if (name == "xml") + { + var x:String = xml.text().toString(); + // If we had CDATA, restore any escaped close CDATA "]]>" tokens + if (hasEscapedCloseCDATA(xml)) + { + x = x.replace(REGEX_CLOSE_CDATA, "]]>"); + } + + result = new XML(x); + } + else if (name == "bytearray") + { + result = decodeByteArray(xml); + } + else if (name == "undefined") + { + result = undefined; + } + + return result; + } + + private static function decodeArray(xml:XML, context:AMFXContext):Array + { + var array:Array = []; + + // Remember Array + context.addObject(array); + + var entries:XMLList = xml.*; + if (entries) + { + for (var i:uint = 0; i < entries.length(); i++) + { + var x:XML = entries[i]; + var prop:Object; + if (x.localName() == "item") + { + var propName:String = x.attribute("name").toString(); + prop = decodeValue(x.*[0], context); + array[propName] = prop; + } + else + { + prop = decodeValue(x, context); + array.push(prop); + } + } + } + + return array; + } + + /* + private static function decodeDictionary(xml:XML, context:AMFXContext):Dictionary + { + var dictionary:Dictionary = new Dictionary(); + + context.addObject(dictionary); // Remember the dictionary. + + var entries:XMLList = xml.*; + if (entries == null) + return dictionary; + + for (var i:uint = 0; i < entries.length(); i = i + 2) + { + var keyXml:XML = entries[i]; + var valueXml:XML = entries[i + 1]; + var key:Object= decodeValue(keyXml, context); + var value:Object = decodeValue(valueXml, context); + dictionary[key] = value; + } + + return dictionary; + } + */ + + private static function decodeByteArray(xml:XML):ByteArray + { + var str:String = xml.text().toString(); + + var decoder:HexDecoder = new HexDecoder(); + decoder.decode(str); + return decoder.drain(); + } + + private static function decodeDate(xml:XML, context:AMFXContext):Date + { + var d:String = xml.text().toString(); + var time:Number = new Number(d); + var result:Date = new Date(); + result.setTime(time); + + // Remember Date object + context.addObject(result); + + return result; + } + + private static function decodeObject(xml:XML, context:AMFXContext):Object + { + var result:Object; + var className:String = xml.attribute("type").toString(); + + if (className) + { + try + { + var classType:Class = null; + try + { + classType = getClassByAlias(className); + } + catch(e1:Error) + { + if (!classType) + classType = getDefinitionByName(className) as Class; + } + + result = new classType(); + } + catch(e:Error) + { + if (context.log) + context.log.warn("Error instantiating class: {0}. Reverting to anonymous Object.", className); + + result = {}; + } + } + else + { + className = "Object"; + result = {}; + } + + // Remember Object + context.addObject(result); + + var entries:XMLList = xml.*; + + if (entries && entries.length() > 0) + { + var traits:Object; + var tx:XML = entries[0]; + var message:String; + + if (tx.localName() == "traits") + { + traits = decodeTraits(tx, className, context); + delete entries[0]; + } + + if (!traits) + { + message = resourceManager.getString( + "messaging", "AMFXTraitsNotFirst") + throw new ChannelError(message); + } + + if (traits.externalizable) + { + if (result is IExternalizable) + { + var ext:IExternalizable = IExternalizable(result); + tx = entries[0]; + if (tx.localName() == "bytearray") + { + var ba:ByteArray = decodeByteArray(tx); + + try + { + ext.readExternal(ba); + } + catch(e:Error) + { + message = resourceManager.getString( + "messaging", "errorReadingIExternalizable", + [ (e.message/*+e.getStackTrace()*/) ]); + throw new ChannelError(message); + } + } + } + else + { + message = resourceManager.getString( + "messaging", "notImplementingIExternalizable", + [ className ]); + throw new ChannelError(message); + } + } + else + { + for (var i:uint = 0; i < entries.length(); i++) + { + var propName:String = traits.properties[i]; + var propValue:* = decodeValue(entries[i], context); + + try + { + result[propName] = propValue; + } + catch(e2:Error) + { + if (context.log != null) + context.log.warn("Cannot set property '{0}' on type '{1}'.", propName, className); + } + } + } + } + return result; + } + + private static function decodeRef(xml:XML, context:AMFXContext):Object + { + var result:*; + + var message:String; + + var idAttr:String = xml.attribute("id").toString(); + if (idAttr) + { + var ref:int = parseInt(idAttr); + + if (!isNaN(ref)) + { + result = context.getObject(ref); + } + + if (result === undefined) + { + message = resourceManager.getString( + "messaging", "unknownReference", [ idAttr ]); + throw new ChannelError(message); + } + } + else + { + message = resourceManager.getString( + "messaging", "referenceMissingId"); + throw new ChannelError(message); + } + + return result; + } + + private static function decodeString(xml:XML, context:AMFXContext, isTrait:Boolean = false):String + { + var str:String; + + var refAttr:String = xml.attribute("id").toString(); + if (refAttr) + { + var ref:uint = uint(refAttr); + if (!isNaN(ref)) + { + str = context.getString(ref); + } + + if (!str) + { + var message:String = resourceManager.getString( + "messaging", "unknownStringReference", [ refAttr ]); + throw new ChannelError(message); + } + } + else + { + str = xml.text().toString(); + + // If we had CDATA, restore any escaped close CDATA "]]>" tokens + // Note that trait names won't have CDATA sections... so no need to check them + if (!isTrait && hasEscapedCloseCDATA(xml)) + { + str = str.replace(REGEX_CLOSE_CDATA, "]]>"); + } + + //Remember string + context.addString(str); + } + + return str; + } + + private static function decodeTraits(xml:XML, className:String, context:AMFXContext):Object + { + var traits:Object; + + var refAttr:String = xml.attribute("id").toString(); + if (refAttr) + { + var ref:uint = uint(refAttr); + if (!isNaN(ref)) + { + traits = context.getTraitInfo(ref); + } + + if (!traits) + { + var message:String = resourceManager.getString( + "messaging", "unknownTraitReference", [ refAttr ]); + throw new ChannelError(message); + } + } + else + { + traits = {}; + traits.name = className; + traits.alias = className; + traits.properties = []; + traits.externalizable = false; + + var ext:String = xml.attribute("externalizable").toString(); + if (ext == "true") + { + traits.externalizable = true; + } + + var nodes:XMLList = xml.*; + if (nodes) + { + for (var i:uint = 0; i < nodes.length(); i++) + { + traits.properties[i] = decodeString(nodes[i], context, true); + } + } + + //Remember traits + context.addTraitInfo(traits); + } + + return traits; + } + + private static function hasEscapedCloseCDATA(xml:XML):Boolean + { + var s:String = xml.toXMLString(); + + if (s.indexOf("]]>") != -1) + { + return s.indexOf("]]>") != -1; + } + else + { + return false; + } + } + + private static function supportedVersion(ver:uint):Boolean + { + for (var i:uint = 0; i < SUPPORTED_VERSIONS.length; i++) + { + if (ver == SUPPORTED_VERSIONS[i]) + return true; + } + + return false; + } + + //-------------------------------------------------------------------------- + // + // Static Constants + // + //-------------------------------------------------------------------------- + + private static const SUPPORTED_VERSIONS:Array = [3]; + private static const REGEX_CLOSE_CDATA:RegExp = new RegExp("]]>", "g"); +} + +} diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXEncoder.as b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXEncoder.as new file mode 100644 index 0000000..44cf733 --- /dev/null +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXEncoder.as @@ -0,0 +1,510 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.channels.amfx +{ + +import mx.utils.ByteArray; +//import flash.utils.Dictionary; +import org.apache.royale.utils.net.IExternalizable; +//import flash.utils.describeType; +//import flash.xml.XMLDocument; + +import mx.logging.Log; +import mx.utils.HexEncoder; +import mx.utils.ObjectProxy; +import mx.utils.ObjectUtil; + +[ExcludeClass] + +/** + * Serializes an arbitrary ActionScript object graph to an XML + * representation that is based on Action Message Format (AMF) + * version 3. + * @private + */ +public class AMFXEncoder +{ + public function AMFXEncoder() + { + super(); + settings = {}; + settings.prettyPrinting = false; + } + + public function encode(obj:Object, headers:Array = null):XML + { + XML.setSettings(settings); + var xml:XML = new XML("<amfx />"); + xml.setNamespace(NAMESPACE); + xml.@["ver"] = CURRENT_VERSION; + + var context:AMFXContext = new AMFXContext(); + context.log = Log.getLogger("mx.messaging.channels.amfx.AMFXEncoder"); + encodePacket(xml, obj, headers, context); + + return xml; + } + + private static function encodePacket(xml:XML, obj:Object, headers:Array = null, context:AMFXContext = null):void + { + if (headers) + encodeHeaders(xml, headers, context); + encodeBody(xml, obj, context); + } + + private static function encodeHeaders(xml:XML, headers:Array, context:AMFXContext):void + { + for (var i:uint = 0; i < headers.length; i++) + { + var header:Object = headers[i]; + var element:XML = <header />; + element.@["name"] = header.name; + element.@["mustUnderstand"] = (header.mustUnderstand == true); + encodeValue(element, header.content, context); + xml.appendChild(element); + } + } + + private static function encodeBody(xml:XML, obj:*, context:AMFXContext):void + { + var element:XML = <body />; + //element.@["targetURI"] = ""; //TODO: Support this attribute + encodeValue(element, obj, context); + xml.appendChild(element); + } + + public static function encodeValue(xml:XML, obj:*, context:AMFXContext):void + { + if (obj != null) + { + if (obj is String) + { + encodeString(xml, String(obj), context); + } + else if (obj is Number) + { + encodeNumber(xml, Number(obj)); + } + else if (obj is Boolean) + { + encodeBoolean(xml, Boolean(obj)); + } + else if (obj is ByteArray) + { + encodeByteArray(xml, ByteArray(obj)); + } + else if (obj is Array) + { + encodeArray(xml, obj as Array, context); + } + /* + else if (obj is XML || obj is XMLDocument) + { + encodeXML(xml, obj); + }*/ + else if (obj is Date) + { + encodeDate(xml, obj as Date, context); + } + else if (obj is Class) + { + //TODO: Throw errors for unsupported types? + if (context.log) + context.log.warn("Cannot serialize type Class"); + } + /* + else if (obj is Dictionary) + { + encodeDictionary(xml, obj as Dictionary, context); + }*/ + else + { + encodeObject(xml, obj, context); + } + } + else if (obj === undefined) + { + xml.appendChild(X_UNDEFINED.copy()); + } + else + { + xml.appendChild(X_NULL.copy()); + } + } + + + private static function encodeArray(xml:XML, array:Array, context:AMFXContext):void + { + var ref:int = context.findObject(array); + var element:XML; + if (ref >= 0) + { + element = <ref /> + element.@["id"] = String(ref); + } + else + { + rememberObject(context, array); + + element = <array />; + + var named:Object = {}; + var ordinal:Array = []; + var isECMAArray:Boolean = false; + + // Separate named and ordinal array members + for (var member:String in array) + { + if (isNaN(Number(member))) + { + named[member] = array[member]; + isECMAArray = true; + } + else + { + var num:int = parseInt(member); + ordinal[num] = array[num]; + } + } + + // Encode named items as early as possible + for (var n:String in named) + { + encodeArrayItem(element, n, named[n], context); + } + + var ordinalLength:uint = 0; + var dense:Boolean = true; + for (var i:uint = 0; i < ordinal.length; i++) + { + var o:* = ordinal[i]; + + // If we have an undefined slot remaining ordinal + // keys will be converted to named keys to preserve dense set + if (o !== undefined) + { + if (dense) + { + encodeValue(element, o, context); + ordinalLength++; + } + else + { + isECMAArray = true; + encodeArrayItem(element, String(i), o, context); + } + } + else + { + dense = false; + } + } + + element.@["length"] = String(ordinalLength); + + if (isECMAArray) + { + element.@["ecma"] = "true"; + } + } + + xml.appendChild(element); + } + + private static function encodeArrayItem(xml:XML, name:String, value:*, context:AMFXContext):void + { + var item:XML = <item />; + item.@["name"] = name; + encodeValue(item, value, context); + xml.appendChild(item); + } + + private static function encodeBoolean(xml:XML, bool:Boolean):void + { + if (bool) + xml.appendChild(X_TRUE.copy()); + else + xml.appendChild(X_FALSE.copy()); + } + + + private static function encodeByteArray(xml:XML, obj:ByteArray):void + { + var element:XML = <bytearray/>; + var encoder:HexEncoder = new HexEncoder(); + encoder.encode(obj); + var encoded:String = encoder.flush(); + element.appendChild(encoded); + xml.appendChild(element); + } + + private static function encodeDate(xml:XML, date:Date, context:AMFXContext):void + { + var ref:int = context.findObject(date); + var element:XML; + if (ref >= 0) + { + element = <ref /> + element.@["id"] = String(ref); + } + else + { + rememberObject(context, date); + + element = <date />; + element.appendChild(new XML(date.getTime().toString())); + } + xml.appendChild(element); + } + + private static function encodeNumber(xml:XML, num:Number):void + { + var element:XML = null; + if (num is int || num is uint) + { + element = <int />; + } + else + { + element = <double />; + } + element.appendChild(new XML(num.toString())); + xml.appendChild(element); + + } + + /* + private static function encodeDictionary(xml:XML, dict:Dictionary, context:AMFXContext):void + { + var ref:int = context.findObject(dict); + var element:XML; + if (ref >= 0) + { + element = <ref />; + element.@["id"] = String(ref); + } + else + { + rememberObject(context, dict); + + element = <dictionary />; + + var classInfo:Object = ObjectUtil.getClassInfo(dict, null, CLASS_INFO_OPTIONS); + var properties:Array = classInfo.properties; + var count:uint = properties.length; + + for (var i:uint = 0; i < count; i++) + { + var prop:Object = properties[i]; + encodeValue(element, prop, context); + encodeValue(element, dict[prop], context); + } + } + element.@["length"] = String(count); + xml.appendChild(element); + } + */ + + private static function rememberObject(context:AMFXContext, obj:*):void + { + context.addObject(obj); + } + + private static function encodeObject(xml:XML, obj:*, context:AMFXContext):void + { + var ref:int = context.findObject(obj); + var element:XML; + if (ref >= 0) + { + element = <ref /> + element.@["id"] = String(ref); + } + else + { + rememberObject(context, obj); + + element = <object />; + + var classInfo:Object = ObjectUtil.getClassInfo(obj, null, CLASS_INFO_OPTIONS); + var className:String = classInfo.name; + var classAlias:String = classInfo.alias; + var properties:Array = classInfo.properties; + var count:uint = properties.length; + + // We need to special case ObjectProxy as for serialization we actually need the + // remote alias of ObjectProxy, not the wrapped object. + /* + if (obj is ObjectProxy) + { + var cinfo:XML = describeType(obj); + className = [email protected](); + classAlias = [email protected](); + } + */ + + var remoteClassName:String = ((classAlias != null) ? classAlias : className); + + if (remoteClassName && remoteClassName != "Object" && remoteClassName != "Array") + { + element.@["type"] = remoteClassName.replace(REGEX_CLASSNAME, "."); + } + + if (obj is IExternalizable) + { + classInfo.externalizable = true; + encodeTraits(element, classInfo, context); + + var ext:IExternalizable = IExternalizable(obj); + var ba:ByteArray = new ByteArray(); + ext.writeExternal(ba); + encodeByteArray(element, ba); + } + else + { + classInfo.externalizable = false; + encodeTraits(element, classInfo, context); + + for (var i:uint = 0; i < count; i++) + { + var prop:String = properties[i]; + encodeValue(element, obj[prop], context); + } + } + } + + xml.appendChild(element); + } + + private static function encodeString(xml:XML, str:String, context:AMFXContext, isTrait:Boolean = false):void + { + var ref:int = context.findString(str); + var element:XML = <string />; + if (ref >= 0) + { + element.@["id"] = String(ref); + } + else + { + //Remember string + context.addString(str); + + if (str.length > 0) + { + // Traits won't contain chars that need escaping + if (!isTrait) + str = escapeXMLString(str); + + var x:XML = new XML(str); + element.appendChild(x); + } + } + xml.appendChild(element); + } + + private static function encodeTraits(xml:XML, classInfo:Object, context:AMFXContext):void + { + var element:XML = <traits />; + + var ref:int = context.findTraitInfo(classInfo); + if (ref >= 0) + { + element.@["id"] = String(ref); + } + else + { + //Remember trait info + context.addTraitInfo(classInfo) + + if (classInfo.externalizable) + { + element.@["externalizable"] = "true"; + } + else + { + var properties:Array = classInfo.properties; + if (properties != null) + { + var count:uint = properties.length; + for (var i:uint = 0; i < count; i++) + { + var prop:String = properties[i]; + encodeString(element, prop, context, true); + } + } + } + } + + xml.appendChild(element); + } + + private static function encodeXML(xml:XML, xmlObject:Object):void + { + var element:XML = <xml />; + var str:String; + if (xmlObject is XML) + str = XML(xmlObject).toXMLString(); + else + str = xmlObject.toString(); + + if (str.length > 0) + { + str = escapeXMLString(str); + var x:XML = new XML(str); + element.appendChild(x); + } + xml.appendChild(element); + } + + private static function escapeXMLString(str:String):String + { + if (str.length > 0) + { + if ((str.indexOf("<") != -1) || (str.indexOf("&") != -1)) + { + if (str.indexOf("]]>") != -1) + { + str = str.replace(REGEX_CLOSE_CDATA, "]]>"); + } + + str = "<![CDATA[" + str + "]]>"; + } + } + + return str; + } + + private var settings:Object; + + public static const CURRENT_VERSION:uint = 3; + public static const NAMESPACE_URI:String = "http://www.macromedia.com/2005/amfx"; + public static const NAMESPACE:Namespace = new Namespace("", NAMESPACE_URI); + + private static const REGEX_CLASSNAME:RegExp = new RegExp("\\:\\:", "g"); + private static const REGEX_CLOSE_CDATA:RegExp = new RegExp("]]>", "g"); + + private static const CLASS_INFO_OPTIONS:Object = {includeReadOnly:false, includeTransient:false}; + + private static const X_FALSE:XML = <false />; + private static const X_NULL:XML = <null />; + private static const X_TRUE:XML = <true />; + private static const X_UNDEFINED:XML = <undefined />; +} + +} diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXHeader.as b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXHeader.as new file mode 100644 index 0000000..8cbbb86 --- /dev/null +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXHeader.as @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.channels.amfx +{ + +[ExcludeClass] + +/** + * An AMFX request or response packet can contain headers. + * + * A Header must have a name, can be marked with a mustUnderstand + * boolean flag (the default is false), and the content can be any + * Object. + * @private + */ +public class AMFXHeader +{ + public var name:String; + public var mustUnderstand:Boolean; + public var content:Object; + + public function AMFXHeader() + { + super(); + } +} + +} diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXResult.as b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXResult.as new file mode 100644 index 0000000..9902c93 --- /dev/null +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/channels/amfx/AMFXResult.as @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.channels.amfx +{ + +[ExcludeClass] + +/** + * A simple context to hold the result of an AMFX request. + * @private + */ +public class AMFXResult +{ + public var version:uint; + public var headers:Array; + public var result:Object; + + public function AMFXResult() + { + super(); + } +} + +} diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/config/ServerConfig.as b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/config/ServerConfig.as index 2f60e3c..54afb04 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/config/ServerConfig.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/messaging/config/ServerConfig.as @@ -383,6 +383,8 @@ public class ServerConfig endpoint = channelConfig.endpoint; // uri might be undefined when client-load-balancing urls are specified. dsUri = endpoint.length() > 0? endpoint[0].attribute(URI_ATTR).toString() : null; + if (!dsUri) + dsUri = endpoint.length() > 0? endpoint[0].attribute("url").toString() : null; if (dsUri != null) dsUris.push(dsUri); } @@ -569,16 +571,24 @@ public class ServerConfig var channels:XMLList = xml.channels.channel.(@id == channelId); if (channels.length() == 0) { - message = resourceManager.getString( - "messaging", "unknownChannelWithId", [ channelId ]); - throw new InvalidChannelError(message); + channels = xml.channels["channel-definition"].(@id == channelId); + if (channels.length() == 0) + { + message = resourceManager.getString( + "messaging", "unknownChannelWithId", [ channelId ]); + throw new InvalidChannelError(message); + } } var channelConfig:XML = channels[0]; var className:String = channelConfig.attribute(CLASS_ATTR).toString(); + if (!className) + className = channelConfig.attribute("class").toString(); var endpoint:XMLList = channelConfig.endpoint; /// uri might be undefined when client-load-balancing urls are specified. var uri:String = endpoint.length() > 0? endpoint[0].attribute(URI_ATTR).toString() : null; + if (!uri) + uri = endpoint.length() > 0? endpoint[0].attribute("url").toString() : null; var channel:Channel = null; try { @@ -652,6 +662,9 @@ public class ServerConfig } } + /** + * @royaleignorecoercion XML + */ private static function getChannelIds(destinationConfig:XML):Array { var result:Array = []; @@ -661,6 +674,15 @@ public class ServerConfig { result.push(channels[i][email protected]()); } + if (n == 0) + { + channels = (destinationConfig.parent() as XML)["default-channels"].channel; + n = channels.length(); + for (i = 0; i < n; i++) + { + result.push(channels[i][email protected]()); + } + } return result; } diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/net/URLLoader.as b/frameworks/projects/MXRoyale/src/main/royale/mx/net/URLLoader.as index ad03b3f..08b85a5 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/net/URLLoader.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/net/URLLoader.as @@ -74,6 +74,11 @@ package mx.net } return false; } + + public function close():void + { + + } } } \ No newline at end of file diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/LoadEvent.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/LoadEvent.as index 8f6a857..b92eb3b 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/LoadEvent.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/LoadEvent.as @@ -22,7 +22,7 @@ package mx.rpc.soap import org.apache.royale.events.Event; import org.apache.royale.events.IRoyaleEvent; -import flash.xml.XMLDocument; +//import flash.xml.XMLDocument; import mx.rpc.events.WSDLLoadEvent; import mx.rpc.wsdl.WSDL; @@ -71,7 +71,6 @@ public class LoadEvent extends WSDLLoadEvent * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 - */ public function get document():XMLDocument { if (_document == null && xml != null) @@ -86,6 +85,7 @@ public class LoadEvent extends WSDLLoadEvent } return _document; } + */ /** * Returns a copy of this LoadEvent. @@ -150,7 +150,7 @@ public class LoadEvent extends WSDLLoadEvent */ public static const LOAD:String = "load"; - private var _document:XMLDocument; + //private var _document:XMLDocument; } } diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/Operation.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/Operation.as index 6243d69..45638dd 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/Operation.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/Operation.as @@ -20,12 +20,9 @@ package mx.rpc.soap { -import org.apache.royale.events.Event; -import flash.xml.XMLNode; - import mx.core.mx_internal; -import mx.logging.Log; import mx.logging.ILogger; +import mx.logging.Log; import mx.messaging.ChannelSet; import mx.messaging.events.MessageEvent; import mx.messaging.events.MessageFaultEvent; @@ -47,6 +44,8 @@ import mx.rpc.xml.SchemaConstants; import mx.utils.ObjectProxy; import mx.utils.XMLUtil; +import org.apache.royale.events.Event; + use namespace mx_internal; /** @@ -61,7 +60,7 @@ use namespace mx_internal; */ [Event(name="header", type="mx.rpc.events.HeaderEvent")] -[ResourceBundle("rpc")] +//[ResourceBundle("rpc")] /** * An Operation used specifically by WebServices. An Operation is an individual @@ -787,7 +786,7 @@ public class Operation extends AbstractOperation var argsToPass:Object = null; if (args && args.length > 0) { - if ((args.length == 1) && (args[0] is XMLNode || args[0] is XML)) + if ((args.length == 1) && (/*args[0] is XMLNode ||*/ args[0] is XML)) { // special case: handle xml node as single argument and drop // into literal mode. @@ -902,18 +901,24 @@ public class Operation extends AbstractOperation { soap = encoder.encodeRequest(pc.args, pc.headers); } - catch(fault:Fault) + catch(obj:Object) { - dispatchRpcEvent(FaultEvent.createEvent(fault)); - return; - } - catch(error:Error) - { - var errorMsg:String = error.message ? error.message : ""; - var fault2:Fault = new Fault("EncodingError", errorMsg); - var faultEvent:FaultEvent = FaultEvent.createEvent(fault2); - dispatchRpcEvent(faultEvent); - return; + if (obj is Fault) + { + var fault:Fault = obj as Fault; + dispatchRpcEvent(FaultEvent.createEvent(fault)); + return; + } + else if (obj is Error) + { + var error:Error = obj as Error; + var errorMsg:String = error.message ? error.message : ""; + var fault2:Fault = new Fault("EncodingError", errorMsg); + var faultEvent:FaultEvent = FaultEvent.createEvent(fault2); + dispatchRpcEvent(faultEvent); + return; + } + throw obj; } message.httpHeaders = httpHeaders; @@ -1007,20 +1012,26 @@ public class Operation extends AbstractOperation if (eventDispatchRequired) _result = soapResult.result; } - catch (fault:Fault) + catch(obj:Object) { - fault.content = body; - dispatchRpcEvent(FaultEvent.createEvent(fault, token, message)); - eventDispatchRequired = false; - } - catch (error:Error) - { - var errorMsg:String = error.message != null ? error.message : ""; - var fault2:Fault = new Fault("DecodingError", errorMsg); - fault2.content = body; - var faultEvent:FaultEvent = FaultEvent.createEvent(fault2, token, message); - dispatchRpcEvent(faultEvent); - eventDispatchRequired = false; + if (obj is Fault) + { + var fault:Fault = obj as Fault; + fault.content = body; + dispatchRpcEvent(FaultEvent.createEvent(fault, token, message)); + eventDispatchRequired = false; + } + else if (obj is Error) + { + var error:Error = obj as Error; + var errorMsg:String = error.message != null ? error.message : ""; + var fault2:Fault = new Fault("DecodingError", errorMsg); + fault2.content = body; + var faultEvent:FaultEvent = FaultEvent.createEvent(fault2, token, message); + dispatchRpcEvent(faultEvent); + eventDispatchRequired = false; + } + else throw obj; } return eventDispatchRequired; diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPDecoder.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPDecoder.as index 77aa7f6..33cd86b 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPDecoder.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPDecoder.as @@ -20,9 +20,11 @@ package mx.rpc.soap { +/* import flash.utils.getTimer; import flash.xml.XMLDocument; import flash.xml.XMLNode; +*/ import mx.logging.ILogger; import mx.logging.Log; @@ -228,7 +230,15 @@ public class SOAPDecoder extends XMLDecoder implements ISOAPDecoder else responseString = String(response); - var startTime:int = getTimer(); + var startTime:Number; + COMPILE::SWF + { + startTime = new Date().time; + } + COMPILE::JS + { + startTime = Date.now(); + } log.info("Decoding SOAP response"); @@ -261,7 +271,16 @@ public class SOAPDecoder extends XMLDecoder implements ISOAPDecoder } } - log.info("Decoded SOAP response into result [{0} millis]", getTimer() - startTime); + var stopTime:Number; + COMPILE::SWF + { + stopTime = new Date().time; + } + COMPILE::JS + { + stopTime = Date.now(); + } + log.info("Decoded SOAP response into result [{0} millis]", stopTime - startTime); return soapResult; } @@ -328,6 +347,7 @@ public class SOAPDecoder extends XMLDecoder implements ISOAPDecoder // Return the children as an XMLList. soapResult.result = bodyXML.children(); } + /* else if (resultFormat == "xml") { // Return the children as an Array of XMLNode @@ -349,7 +369,7 @@ public class SOAPDecoder extends XMLDecoder implements ISOAPDecoder } } soapResult.result = bodyArray; - } + }*/ } } } @@ -718,10 +738,11 @@ public class SOAPDecoder extends XMLDecoder implements ISOAPDecoder { headers.push(headerChild); } + /* else if (headerFormat == "xml") { headers.push(new XMLDocument(headerChild.toString())); - } + }*/ } return headers; diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPEncoder.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPEncoder.as index bc2880e..dab1610 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPEncoder.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPEncoder.as @@ -20,8 +20,8 @@ package mx.rpc.soap { -import flash.xml.XMLDocument; -import flash.xml.XMLNode; +//import flash.xml.XMLDocument; +//import flash.xml.XMLNode; import mx.core.mx_internal; import mx.logging.ILogger; @@ -40,7 +40,7 @@ import mx.rpc.xml.SchemaDatatypes; import mx.rpc.xml.SchemaMarshaller; import mx.rpc.xml.XMLEncoder; -[ResourceBundle("rpc")] +//[ResourceBundle("rpc")] [ExcludeClass] @@ -679,6 +679,7 @@ public class SOAPEncoder extends XMLEncoder implements ISOAPEncoder { preEncodedNode = value as XML; } + /* else if (value is XMLDocument) { var xmlDocument:XMLDocument = value as XMLDocument; @@ -689,6 +690,7 @@ public class SOAPEncoder extends XMLEncoder implements ISOAPEncoder var xmlNode:XMLNode = value as XMLNode; preEncodedNode = new XML(xmlNode.toString()); } + */ } return preEncodedNode; } diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPFault.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPFault.as index 900abf3..ffa68da 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPFault.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/SOAPFault.as @@ -20,7 +20,6 @@ package mx.rpc.soap { -import flash.xml.XMLNode; import mx.rpc.Fault; /** diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/WebService.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/WebService.as index e506674..1f06eba 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/WebService.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/WebService.as @@ -345,24 +345,31 @@ public dynamic class WebService extends AbstractWebService unEnqueueCalls(); } - catch(fault:Fault) + catch(obj:Object) { - var faultEvent:FaultEvent = FaultEvent.createEvent(fault); - dispatchEvent(faultEvent); - super.unEnqueueCalls(fault); // Jump straight to fault handling; ops cannot be initialized. - return; - } - catch(error:Error) - { - var errorMessage:String = error.message ? error.message : ""; - var message:String = /*resourceManager.getString( - "rpc",*/ "unexpectedException" /*, [ errorMessage ])*/; - var fault:Fault = new Fault("WSDLError", message); - fault.rootCause = error; - var faultEvent2:FaultEvent = FaultEvent.createEvent(fault); - dispatchEvent(faultEvent2); - super.unEnqueueCalls(fault); // Jump straight to fault handling; ops cannot be initialized. - return; + var fault:Fault; + if (obj is Fault) + { + fault = obj as Fault; + var faultEvent:FaultEvent = FaultEvent.createEvent(fault); + dispatchEvent(faultEvent); + super.unEnqueueCalls(fault); // Jump straight to fault handling; ops cannot be initialized. + return; + } + else if (obj is Error) + { + var error:Error = obj as Error; + var errorMessage:String = error.message ? error.message : ""; + var message:String = /*resourceManager.getString( + "rpc",*/ "unexpectedException" /*, [ errorMessage ])*/; + fault = new Fault("WSDLError", message); + fault.rootCause = error; + var faultEvent2:FaultEvent = FaultEvent.createEvent(fault); + dispatchEvent(faultEvent2); + super.unEnqueueCalls(fault); // Jump straight to fault handling; ops cannot be initialized. + return; + } + throw obj; } } @@ -377,7 +384,7 @@ public dynamic class WebService extends AbstractWebService if (destination) httpService.destination = destination; httpService.useProxy = useProxy; - httpService.resultFormat = HTTPService.RESULT_FORMAT_XML; + // httpService.resultFormat = HTTPService.RESULT_FORMAT_XML; AJH gets reset to E4X in XMLLoader httpService.rootURL = rootURL; httpService.headers = httpHeaders; return httpService; diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/mxml/Operation.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/mxml/Operation.as index 4f44b48..11ff73a 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/mxml/Operation.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/mxml/Operation.as @@ -200,7 +200,7 @@ public class Operation extends mx.rpc.soap.Operation implements IMXMLSupport return token; } - return super.send.apply(null, args); + return super.send.apply(this, args); } diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/mxml/WebService.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/mxml/WebService.as index 56d6bd0..403f2a0 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/mxml/WebService.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/soap/mxml/WebService.as @@ -23,9 +23,13 @@ package mx.rpc.soap.mxml import org.apache.royale.events.Event; import mx.events.ErrorEvent; import org.apache.royale.events.IEventDispatcher; +import org.apache.royale.core.IDocument; +import mx.core.IFlexModuleFactory; import mx.core.IMXMLObject; +import mx.core.IUIComponent; import mx.core.mx_internal; +import mx.managers.ISystemManager; import mx.resources.IResourceManager; import mx.resources.ResourceManager; import mx.rpc.AbstractOperation; @@ -34,10 +38,11 @@ import mx.rpc.mxml.Concurrency; import mx.rpc.mxml.IMXMLSupport; import mx.rpc.soap.mxml.Operation; import mx.rpc.soap.WebService; +import mx.messaging.config.ServerConfig; use namespace mx_internal; -[ResourceBundle("rpc")] +//[ResourceBundle("rpc")] /** * The <mx:WebService> tag gives you access to the operations of SOAP-compliant @@ -94,7 +99,7 @@ use namespace mx_internal; * @playerversion AIR 1.1 * @productversion Flex 3 */ -public dynamic class WebService extends mx.rpc.soap.WebService implements IMXMLSupport, IMXMLObject +public dynamic class WebService extends mx.rpc.soap.WebService implements IMXMLSupport, IMXMLObject, IDocument { //-------------------------------------------------------------------------- // @@ -357,6 +362,26 @@ public dynamic class WebService extends mx.rpc.soap.WebService implements IMXMLS initialize(); } + public function setDocument(document:Object, id:String = null):void + { + this.document = document; + this.id = id; + if (document is IEventDispatcher) + { + IEventDispatcher(document).addEventListener("creationComplete", creationComplete); + } + + COMPILE::JS + { + if (document is IUIComponent) + { + ServerConfig.xml = new XML(((document as IUIComponent).systemManager as IFlexModuleFactory).info()["servicesConfig"]); + } + ; + } + + initialize(); + } //-------------------------------------------------------------------------- // diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/wsdl/WSDL.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/wsdl/WSDL.as index b83cd85..329ff8c 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/wsdl/WSDL.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/wsdl/WSDL.as @@ -32,7 +32,7 @@ import mx.rpc.xml.SchemaConstants; import mx.rpc.xml.SchemaManager; import mx.rpc.xml.SchemaTypeRegistry; -[ResourceBundle("rpc")] +//[ResourceBundle("rpc")] [ExcludeClass] diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/wsdl/WSDLLoader.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/wsdl/WSDLLoader.as index fa7fca4..5789bed 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/wsdl/WSDLLoader.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/wsdl/WSDLLoader.as @@ -41,7 +41,7 @@ import mx.utils.URLUtil; [Event(name="fault", type="mx.rpc.events.FaultEvent")] [Event(name="wsdlLoad", type="mx.rpc.events.WSDLLoadEvent")] -[ResourceBundle("rpc")] +//[ResourceBundle("rpc")] [ExcludeClass]
