////////////////////////////////////////////////////////////////////////////////
//
// ADOBE SYSTEMS INCORPORATED
// Copyright 2006-2007 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package mx.rpc.soap
{
import flash.events.Event;
import mx.core.mx_internal;
import mx.logging.ILogger;
import mx.logging.Log;
import mx.messaging.ChannelSet;
import mx.messaging.channels.DirectHTTPChannel;
import mx.messaging.config.LoaderConfig;
import mx.rpc.AbstractOperation;
import mx.rpc.AbstractService;
import mx.rpc.AsyncRequest;
import mx.rpc.Fault;
import mx.rpc.events.FaultEvent;
import mx.rpc.http.HTTPService;
import mx.utils.URLUtil;
import mx.utils.XMLUtil;
use namespace mx_internal;
/**
* AbstractWebService is an abstract base class for implementations
* that provide RPC access to SOAP-based web services. This class does not
* load WSDL descriptions at runtime.
*/
public class AbstractWebService extends AbstractService
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Creates a new WebService. The destination, if specified, should match an
* entry in services-config.xml. If unspecified, the WebService uses the
* DefaultHTTP destination. The rootURL is required if you
* intend to use a relative URL find the WSDL document for this WebService.
*/
public function AbstractWebService(destination:String = null, rootURL:String = null)
{
super(destination);
_useProxy = false;
_headers = [];
_log = Log.getLogger("mx.rpc.soap.AbstractWebService");
if (destination == null)
{
// If the SWF was loaded via HTTPS, we'll use the DefaultHTTPS
// destination by default
if (URLUtil.isHttpsURL(LoaderConfig.url))
super.destination = DEFAULT_DESTINATION_HTTPS;
else
super.destination = DEFAULT_DESTINATION_HTTP;
destinationSet = false;
}
else
{
destinationSet = true;
}
makeObjectsBindable = true;
_ready = true;
}
//-------------------------------------------------------------------------
//
// Properties
//
//-------------------------------------------------------------------------
//----------------------------------
// description
//----------------------------------
/**
* The description of the service for the currently active port.
*/
public function get description():String
{
return _description;
}
public function set description(value:String):void
{
_description = value;
}
//----------------------------------
// destination
//----------------------------------
/**
* @inheritDoc
*/
override public function get destination():String
{
return super.destination;
}
override public function set destination(value:String):void
{
super.destination = value;
useProxy = true;
destinationSet = true;
}
//----------------------------------
// endpointURI
//----------------------------------
/**
* The location of the WebService. Normally, the WSDL document specifies
* the location of the services, but you can set this property to override
* that location.
*/
public function get endpointURI():String
{
return (endpointOverride) ? endpointOverride : _endpointURI;
}
public function set endpointURI(value:String):void
{
endpointOverride = value;
}
//----------------------------------
// headers
//----------------------------------
/**
* Returns the array of SOAPHeaders registered for the WebService.
*/
public function get headers():Array
{
return _headers;
}
//----------------------------------
// httpHeaders
//----------------------------------
private var _httpHeaders:Object;
[Inspectable(defaultValue="undefined", category="General")]
/**
* Custom HTTP headers to be sent to the SOAP endpoint. If multiple
* headers need to be sent with the same name the value should be specified
* as an Array.
*/
public function get httpHeaders():Object
{
return _httpHeaders;
}
public function set httpHeaders(value:Object):void
{
_httpHeaders = value;
}
//----------------------------------
// makeObjectsBindable
//----------------------------------
[Inspectable(defaultValue="true", category="General")]
/**
* When this value is true, anonymous objects returned are forced to
* bindable objects.
*/
public function get makeObjectsBindable():Boolean
{
return _makeObjectsBindable;
}
public function set makeObjectsBindable(value:Boolean):void
{
_makeObjectsBindable = value;
}
//----------------------------------
// port
//----------------------------------
/**
* Specifies the port within the WSDL document that this WebService should
* use.
*/
public function get port():String
{
return _port;
}
public function set port(value:String):void
{
_port = value;
}
//----------------------------------
// ready
//----------------------------------
/**
* Specifies whether the WebService is ready to make requests.
*/
public function get ready():Boolean
{
return _ready;
}
//----------------------------------
// rootURL
//----------------------------------
/**
* The URL that the WebService should use when computing relative URLs. This
* property is only used when going through the proxy. When the
* useProxy property is set to false the relative
* URL is computed automatically based on the location of the SWF running
* this application. If not set explicitly rootURL is
* automatically set to the URL of mx.messaging.config.LoaderConfig.url.
*/
public function get rootURL():String
{
if (_rootURL == null)
{
_rootURL = LoaderConfig.url;
}
return _rootURL;
}
public function set rootURL(value:String):void
{
_rootURL = value;
}
//----------------------------------
// service
//----------------------------------
/**
* Specifies the service within the WSDL document that this WebService
* should use.
*/
public function get service():String
{
return _service;
}
public function set service(value:String):void
{
_service = value;
}
//----------------------------------
// useProxy
//----------------------------------
[Inspectable(defaultValue="false", category="General")]
/**
* Specifies whether to use the Flex proxy service. The default value is
* false. If you do not specify true to proxy
* requests though the Flex server, you must ensure that Flash Player can
* reach the target URL. You also cannot use destinations defined in the
* services-config.xml file if the useProxy property is set
* to false.
*
* @default false
*/
public function get useProxy():Boolean
{
return _useProxy;
}
public function set useProxy(value:Boolean):void
{
if (value != _useProxy)
{
_useProxy = value;
var dcs:ChannelSet = getDirectChannelSet();
if (!useProxy)
{
if (dcs != asyncRequest.channelSet)
asyncRequest.channelSet = dcs;
}
else
{
if (asyncRequest.channelSet == dcs)
asyncRequest.channelSet = null;
}
}
}
//----------------------------------
// xmlSpecialCharsFilter
//----------------------------------
private var _xmlSpecialCharsFilter:Function;
[Inspectable(defaultValue="undefined", category="General")]
/**
* Custom function to be used to escape XML special characters before
* encoding any simple content. Valid for all operations on the web
* service unless specifically overwritten on the operation level.
* If none is provided, the defaults to whatever is set by the particular
* implementation of IXMLEncoder
*/
public function get xmlSpecialCharsFilter():Function
{
return _xmlSpecialCharsFilter;
}
public function set xmlSpecialCharsFilter(func:Function):void
{
_xmlSpecialCharsFilter = func;
}
//-------------------------------------------------------------------------
//
// Methods
//
//-------------------------------------------------------------------------
/**
* Adds a header that will be applied to all operations of this web service.
* The header can be provided in a pre-encoded form as an XML instance, or
* as a SOAPHeader instance which leaves the encoding up to the internal
* SOAP encoder.
*
* @param header The SOAP header to add to all operations.
*/
public function addHeader(header:Object):void
{
_headers.push(header);
}
/**
* Add a header that will be applied to all operations of this WebService.
*
* @param qnameLocal The localname for the header QName.
* @param qnameNamespace The namespace for the header QName.
* @param headerName The name of the header.
* @param headerValue The value of the header.
*/
public function addSimpleHeader(qnameLocal:String, qnameNamespace:String, headerName:String, headerValue:String):void
{
var obj:Object = {};
obj[headerName] = headerValue;
addHeader(new SOAPHeader(new QName(qnameNamespace, qnameLocal), obj));
}
/**
* Clears the headers that applied to all operations.
*/
public function clearHeaders():void
{
_headers.length = 0;
}
/**
* Returns a header if a match is found based on QName, localName, and URI.
*
* @param qname QName of the SOAPHeader.
* @param headerName (Optional) Name of a header in the SOAPHeader content.
*
* @return Returns a header if a match is found based on QName, localName, and URI.
*/
public function getHeader(qname:QName, headerName:String = null):SOAPHeader
{
var length:uint = _headers.length;
for (var i:uint = 0; i < length; i++)
{
var header:SOAPHeader = SOAPHeader(_headers[i]);
if (XMLUtil.qnamesEqual(header.qname, qname))
{
if (headerName)
{
if (header.content && header.content[headerName])
{
return header;
}
}
else
{
return header;
}
}
}
return null;
}
/**
* Removes the header with the given QName from all operations.
*
* @param qname QName of the SOAPHeader.
* @param headerName (Optional) Name of a header in the SOAPHeader content.
*/
public function removeHeader(qname:QName, headerName:String = null):void
{
var length:uint = _headers.length;
for (var i:uint = 0; i < length; i++)
{
var header:SOAPHeader = SOAPHeader(_headers[i]);
if (XMLUtil.qnamesEqual(header.qname, qname))
{
if (headerName)
{
if (header.content && header.content[headerName])
{
_headers.splice(i, 1);
return; // Got it
}
}
else
{
_headers.splice(i, 1);
return; // Got it
}
}
}
}
/**
* The username and password to authenticate a user when accessing
* the webservice. These will be passed as part of the HTTP Authorization
* header from the proxy to the endpoint. If useProxy is false this property
* will be ignored.
*
* @param remoteUsername The username to pass to the remote endpoint.
* @param remotePassword The password to pass to the remote endpoint.
* @param charset The character set encoding to use while encoding the
* remote credentials. The default is null, which implies the legacy charset
* of ISO-Latin-1. The only other supported charset is "UTF-8".
*/
override public function setRemoteCredentials(remoteUsername:String, remotePassword:String, charset:String=null):void
{
super.setRemoteCredentials(remoteUsername, remotePassword, charset);
}
//---------------------------------
// Helper methods
//---------------------------------
/**
* Returns a JSESSIOND found in the URL. This should be attached
* to any communication back with the server where session state needs to
* be preserved
* @private
*/
mx_internal static function findJSessionID():String
{
var jsessionid:String = null;
var args:Object = LoaderConfig.parameters;
if (args)
{
var js:Object = args.jsessionid;
if (!js)
{
js = args.JSESSIONID;
if (!js)
{
var u:String = LoaderConfig.url;
if (u)
{
var ind:Number = u.lastIndexOf(";")
if (ind != -1)
{
var arr:Array = u.substring(ind + 1).split("=");
if (arr[0] == "jsessionid" || arr[0] == "JSESSIONID")
{
jsessionid = arr[1];
}
}
}
}
else
{
jsessionid = String(js);
}
}
else
{
jsessionid = String(js);
}
}
return jsessionid;
}
/**
* @private
*/
mx_internal function getDirectChannelSet():ChannelSet
{
if (_directChannelSet == null)
{
var dcs:ChannelSet = new ChannelSet();
dcs.addChannel(new DirectHTTPChannel("direct_http_channel"));
_directChannelSet = dcs;
}
return _directChannelSet;
}
// Deals with the queued calls - if a fault is passed, we'll simply dispatch
// a FaultEvent for each call. Otherwise we invoke each one.
/**
* @private
*/
protected function unEnqueueCalls(fault:Fault = null):void
{
var op:Operation;
for (var opName:String in _operations)
{
op = _operations[opName];
if (op.hasPendingInvocations())
{
if (fault != null)
{
_log.info("Faulting previously queued operation calls {0}", op.name);
while (op.hasPendingInvocations())
{
op.cancel();
op.dispatchEvent(FaultEvent.createEvent(fault));
}
}
else
{
_log.info("Invoking previously queued calls {0}", op.name);
op.invokeAllPending();
}
}
}
}
//-------------------------------------------------------------------------
//
// Variables
//
//-------------------------------------------------------------------------
//--------------------------------------------
// Backing variables for public getter/setters
//--------------------------------------------
/**
* @private
*/
protected var _endpointURI:String;
private var _description:String;
private var endpointOverride:String;
private var _headers:Array;
private var _makeObjectsBindable:Boolean;
/**
* @private
*/
protected var _port:String;
private var _rootURL:String;
/**
* @private
*/
protected var _service:String;
private var _useProxy:Boolean;
//---------------------------------
// Internal properties
//---------------------------------
//private var _activePort:Object;
/**
* @private
*/
protected var destinationSet:Boolean;
/**
* @private
*/
protected var _ready:Boolean;
private var _log:ILogger;
/**
* @private
*/
private static var _directChannelSet:ChannelSet = null;
//--------------------------------------------------------------------------
//
// Static Constants
//
//--------------------------------------------------------------------------
/**
* The default destination to use for HTTP connections when invoking a webservice through a proxy.
* If you don't provide a destination and you set the useProxy property to true,
* the default destinations will be used to route the requests to the webservice endpoint.
*
*
Note that if the default destinations are used, you must specify the WSDL and endpointURI on the client. * If you use a non-default proxy destination, you can have the WSDL and endpointURI specified in the * destination configuration.
*/ public static const DEFAULT_DESTINATION_HTTP:String = "DefaultHTTP"; /** * The default destination to use for HTTPS connections when invoking a webservice through a proxy. * If you don't provide a destination and you set theuseProxy property to true,
* the default destinations will be used to route the requests to the webservice endpoint.
*
* Note that if the default destinations are used, you must specify the WSDL and endpointURI on the client. * If you use a non-default proxy destination, you can have the WSDL and endpointURI specified in the * destination configuration.
*/ public static const DEFAULT_DESTINATION_HTTPS:String = "DefaultHTTPS"; } }