blob: ae4531b2a06ef7b5767bc829e36a9836a75571c5 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.rpc.http
{
import mx.core.mx_internal;
import mx.logging.ILogger;
import mx.logging.Log;
import mx.rpc.AbstractService;
import mx.rpc.AsyncRequest;
import mx.rpc.AsyncToken;
import mx.utils.URLUtil;
import mx.messaging.errors.ArgumentError;
use namespace mx_internal;
/**
* An Operation used specifically by an HTTPMultiService. An Operation is an
* individual operation on a service usually corresponding to a single operation on the server
* side. An Operation can be called either by invoking the
* function of the same name on the service or by accessing the Operation as a property on the service and
* calling the <code>send(param1, param2)</code> method. HTTP services also support a sendBody
* method which allows you to directly specify the body of the HTTP response. If you use the
* send(param1, param2) method, the body is typically formed by combining the argumentNames
* property of the operation with the parameters sent. An Object is created which uses the
* argumentNames[i] as the key and the corresponding parameter as the value.
*
* <p>The exact way in which the HTTP operation arguments is put into the HTTP body is determined
* by the serializationFilter used.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class Operation extends AbstractOperation
{
//---------------------------------
// Constructor
//---------------------------------
/**
* Creates a new Operation.
*
* @param service The HTTPMultiService object defining the service.
*
* @param name The name of the service.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
* Creates a new Operation.
*
* @param service The HTTPMultiService object defining the service.
*
* @param name The name of the service.
*/
public function Operation(service:HTTPMultiService = null, name:String = null)
{
super(service, name);
// Set this to false even if the super constructor initialized concurrency to the default.
_concurrencySet = false;
_multiService = service;
_log = Log.getLogger("mx.rpc.http.HTTPMultiService");
}
/**
* Stores the parent service which controls this operation.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
private var _multiService:HTTPMultiService;
/**
* @private
*/
private var _concurrency:String;
/**
* @private
*/
private var _concurrencySet:Boolean;
[Inspectable(enumeration="multiple,single,last", defaultValue="multiple", category="General")]
/**
* Value that indicates how to handle multiple calls to the same service operation. The default
* value is <code>multiple</code>. The following values are permitted:
* <ul>
* <li><code>multiple</code> Existing requests are not cancelled, and the developer is
* responsible for ensuring the consistency of returned data by carefully
* managing the event stream. This is the default value.</li>
* <li><code>single</code> Only a single request at a time is allowed on the operation;
* multiple requests generate a fault.</li>
* <li><code>last</code> Making a request cancels any existing request.</li>
* </ul>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get concurrency():String
{
// This override is necessary because unlike the old-style HttpService HttpOperation setup
// each HttpMultiService can have many http.Operations and concurrency settings don't have
// to be the same for the service and all its operations. If concurrency is not set on this
// operation, the setting from HttpMultiService is used. This code cannot be in AbstractOperation
// because the old HttpService doesn't hold a value for concurrency (and doesn't need to). It
// simply gets/sets concurrency for its only operation.
if (_concurrencySet)
{
return _concurrency;
}
//else
return _multiService.concurrency;
}
override public function set concurrency(c:String):void
{
_concurrency = c;
_concurrencySet = true;
}
/**
* Keep track of whether or not this has been set explicitly on the
* operation. If not, we'll inherit this value from the service level.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
private var _makeObjectsBindableSet:Boolean;
[Inspectable(defaultValue="true", category="General")]
/**
* When this value is true, anonymous objects returned are forced to bindable objects.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get makeObjectsBindable():Boolean
{
if (_makeObjectsBindableSet)
return _makeObjectsBindable;
return _multiService.makeObjectsBindable;
}
override public function set makeObjectsBindable(b:Boolean):void
{
_makeObjectsBindable = b;
_makeObjectsBindableSet = true;
}
private var _methodSet:Boolean = false;
private var _method:String;
/**
* @inheritDoc
*/
override public function get method():String
{
if (_methodSet)
return _method;
return _multiService.method;
}
/**
* @private
*/
override public function set method(m:String):void
{
_method = m;
_methodSet = m != null;
}
override mx_internal function setService(s:AbstractService):void
{
super.setService(s);
if (s is HTTPMultiService)
_multiService = s as HTTPMultiService;
}
private var _showBusyCursorSet:Boolean = false;
/**
* @inheritDoc
*/
override public function get showBusyCursor():Boolean
{
if (_showBusyCursorSet)
return super.showBusyCursor;
return _multiService.showBusyCursor;
}
/**
* @private
*/
override public function set showBusyCursor(b:Boolean):void
{
super.showBusyCursor = b;
_showBusyCursorSet = true;
}
/**
* The rootURL is used to compute the URL for an HTTP service operation when the
* a relative URL is specified for the operation. The directory name of the
* rootURL is prepended to any relative URLs for the operation. It is typically
* more convenient to set the baseURL since baseURL specifies the directory name
* directly whereas rootURL specifies the name of a file whose directory name is
* prepended. If neither rootURL nor baseURL are set explicitly, the directory name
* of the .swf file is prepended to relative paths.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get rootURL():String
{
if (_rootURL == null)
{
var useRootURL:String = _multiService.baseURL;
if (useRootURL != null && useRootURL.length > 0)
{
if (useRootURL.charAt(useRootURL.length - 1) != '/')
useRootURL = useRootURL + "/";
// If this is relative to the
if (useRootURL.charAt(0) == "/")
useRootURL = URLUtil.getFullURL(super.rootURL, useRootURL);
return useRootURL;
}
else
return super.rootURL; // defaults to SWF's URL
}
return _rootURL;
}
private var _useProxySet:Boolean = false;
/**
* @inheritDoc
*/
override public function get useProxy():Boolean
{
if (_useProxySet)
return super.useProxy;
return _multiService.useProxy;
}
/**
* @private
*/
override public function set useProxy(value:Boolean):void
{
_useProxySet = true;
super.useProxy = value;
}
private var _contentTypeSet:Boolean = false;
/**
* @inheritDoc
*/
override public function get contentType():String
{
if (_contentTypeSet)
return super.contentType;
return _multiService.contentType;
}
/**
* @private
*/
override public function set contentType(ct:String):void
{
_contentTypeSet = ct != null;
super.contentType = ct;
}
//---------------------------------
// Methods
//---------------------------------
/**
* Executes the http operation. Any arguments passed in are passed along as part of
* the operation call. If there are no arguments passed, the arguments property of
* class is used as the source of parameters. HTTP operations commonly take named
* parameters, not positional parameters. To supply the names for these parameters,
* you can also set the argumentNames property to an array of the property names.
*
* @param args Optional arguments passed in as part of the method call. If there
* are no arguments passed, the arguments object is used as the source of
* parameters.
*
* @return AsyncToken Call using the asynchronous completion token pattern.
* The same object is available in the <code>result</code> and
* <code>fault</code> events from the <code>token</code> property.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function send(... args:Array):AsyncToken
{
if (_multiService != null)
_multiService.initialize();
if (operationManager != null)
return operationManager(args);
var params:Object;
var filter:SerializationFilter = getSerializationFilter();
if (filter != null)
{
params = filter.serializeParameters(this, args);
}
else
{
params = args;
if (!params || (params.length == 0 && this.request))
{
params = this.request;
}
if (params is Array && argumentNames != null)
{
args = params as Array;
params = new Object();
if (args.length != argumentNames.length)
{
throw new ArgumentError("HTTPMultiService operation called with " + argumentNames.length + " argumentNames and " + args.length + " number of parameters. When argumentNames is specified, it must match the number of arguments passed to the invocation");
}
else
{
// Special case for XML content type when only one parameter is provided.
// This gets rid of the need for a serializationFilter for this simple case.
// If there is more than one parameter though, we do not have a reliable way
// to turn the arguments into a body.
if (argumentNames.length == 1 && contentType == CONTENT_TYPE_XML)
{
params = args[0];
}
else
{
for (var i:int = 0; i < argumentNames.length; i++)
params[argumentNames[i]] = args[i];
}
}
}
// Also do the XML content type special case when no argument names is set
else if (args.length == 1)
{
params = args[0];
}
else if (args.length != 0)
{
throw new ArgumentError("HTTPMultiService - you must set argumentNames to an array of parameter names if you use more than one parameter.");
}
}
return sendBody(params);
}
private var _resultFormatSet:Boolean = false;
/**
* @inheritDoc
*/
override public function get resultFormat():String
{
if (_resultFormatSet)
return super.resultFormat;
return _multiService.resultFormat;
}
/**
* @private
*/
override public function set resultFormat(rf:String):void
{
_resultFormatSet = rf != null;
super.resultFormat = rf;
}
/**
* @private
*/
override protected function getSerializationFilter():SerializationFilter
{
var sf:SerializationFilter = serializationFilter;
if (sf == null)
return _multiService.serializationFilter;
return sf;
}
/**
* @private
*/
override protected function getHeaders():Object
{
// TODO: support combining the headers maps if both are specified
if (headers != null)
return headers;
else
return _multiService.headers;
}
/**
* Use the asyncRequest from the parent service
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override mx_internal function get asyncRequest():AsyncRequest
{
// TODO: is this safe? should we do this in RemoteObject etc. Minimally we
// need to propagate the multiService.destination to the AsyncRequest if we
// go with the default implementation of per-operation asyncRequest instances.
return _multiService.asyncRequest;
}
}
}