1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2005-2007 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.rpc.http
13{
14
15import mx.core.mx_internal;
16import mx.logging.ILogger;
17import mx.logging.Log;
18import mx.messaging.ChannelSet;
19import mx.messaging.channels.DirectHTTPChannel;
20import mx.messaging.config.LoaderConfig;
21import mx.messaging.messages.HTTPRequestMessage;
22import mx.resources.IResourceManager;
23import mx.resources.ResourceManager;
24import mx.rpc.AbstractService;
25import mx.rpc.events.FaultEvent;
26import mx.rpc.mxml.Concurrency;
27import mx.utils.URLUtil;
28
29use namespace mx_internal;
30
31/**
32 *  Dispatched when an HTTPMultiService call returns successfully.
33 * @eventType mx.rpc.events.ResultEvent.RESULT
34 *
35 *  @langversion 3.0
36 *  @playerversion Flash 9
37 *  @playerversion AIR 1.1
38 *  @productversion Flex 3
39 */
40[Event(name="result", type="mx.rpc.events.ResultEvent")]
41
42/**
43 *  Dispatched when an HTTPMultiService call fails.
44 * @eventType mx.rpc.events.FaultEvent.FAULT
45 *
46 *  @langversion 3.0
47 *  @playerversion Flash 9
48 *  @playerversion AIR 1.1
49 *  @productversion Flex 3
50 */
51[Event(name="fault", type="mx.rpc.events.FaultEvent")]
52
53/**
54 *  The invoke event is fired when an HTTPMultiService call is invoked so long as
55 *  an Error is not thrown before the Channel attempts to send the message.
56 * @eventType mx.rpc.events.InvokeEvent.INVOKE
57 *
58 *  @langversion 3.0
59 *  @playerversion Flash 9
60 *  @playerversion AIR 1.1
61 *  @productversion Flex 3
62 */
63[Event(name="invoke", type="mx.rpc.events.InvokeEvent")]
64
65[ResourceBundle("rpc")]
66
67[DefaultProperty("operationList")]
68/**
69 *  You use the <code>&lt;mx:HTTPMultiService&gt;</code> tag to represent a
70 *  collection of http operations.  Each one has a URL, method, parameters and
71 *  return type.
72 *
73 *  <p>You can set attributes such as the URL and method on the
74 *  HTTPMultiService tag to act as defaults for values set on each individual
75 *  operation tag.  The URL of the HTTPMultiService serves as the base url (meaning the prefix)
76 *  for any relative urls set on the http operation tags.
77 *  Each http operation has a <code>send()</code> method, which makes an HTTP request to the
78 *  specified URL, and an HTTP response is returned. </p>
79 *
80 *  <p>You can pass parameters to the specified URL which are used to put data into the HTTP request.
81 *  The contentType property specifies a mime-type which is used to determine the over-the-wire
82 *  data format (such as HTTP form encoding or XML).  </p>
83 *
84 *  <p>You can also use a serialization filter to
85 *  implement a custom resultFormat such as JSON.
86 *  When you do not go through the server-based
87 *  proxy service, you can use only HTTP GET or POST methods. However, when you set
88 *  the <code>useProxy </code> property to true and you use the server-based proxy service, you
89 *  can also use the HTTP HEAD, OPTIONS, TRACE, and DELETE methods.</p>
90 *
91 *  <p><b>Note:</b> Unlike the HTTPService class, the HTTPMultiService class does not
92 *  define a <code>request</code> property.</p>
93 *
94 *  <p><b>Note:</b> Due to a software limitation, like HTTPService, the HTTPMultiService does
95 *  not generate user-friendly error messages when using GET and not using a proxy.</p>
96 *
97 *  @langversion 3.0
98 *  @playerversion Flash 9
99 *  @playerversion AIR 1.1
100 *  @productversion Flex 3
101 *
102 *  @see mx.rpc.http.HTTPService
103 */
104public dynamic class HTTPMultiService extends AbstractService
105{
106    //--------------------------------------------------------------------------
107    //
108    // Constructor
109    //
110    //--------------------------------------------------------------------------
111
112    /**
113     *  Creates a new HTTPService. If you expect the service to send using relative URLs you may
114     *  wish to specify the <code>baseURL</code> that will be the basis for determining the full URL (one example
115     *  would be <code>Application.application.url</code>).
116     *
117     *  @param baseURL The URL the HTTPService should use when computing relative URLS.
118     *
119     *  @langversion 3.0
120     *  @playerversion Flash 9
121     *  @playerversion AIR 1.1
122     *  @productversion Flex 3
123     */
124    public function HTTPMultiService(baseURL:String = null, destination:String = null)
125    {
126        super();
127
128        makeObjectsBindable = true;
129
130        if (destination == null)
131        {
132            if (URLUtil.isHttpsURL(LoaderConfig.url))
133                asyncRequest.destination = HTTPService.DEFAULT_DESTINATION_HTTPS;
134            else
135                asyncRequest.destination = HTTPService.DEFAULT_DESTINATION_HTTP;
136        }
137        else
138            asyncRequest.destination = destination;
139
140        _log = Log.getLogger("mx.rpc.http.HTTPMultiService");
141
142        this.baseURL = baseURL;
143
144        concurrency = Concurrency.MULTIPLE;
145    }
146
147    //--------------------------------------------------------------------------
148    //
149    // Variables
150    //
151    //--------------------------------------------------------------------------
152
153    /**
154     *  @private
155     *  A shared direct Http channelset used for service instances that do not use the proxy.
156     */
157    private static var _directChannelSet:ChannelSet;
158
159    /**
160     *  @private
161     *  Logger
162     */
163    private var _log:ILogger;
164
165    /**
166     *  @private
167     */
168    private var resourceManager:IResourceManager = ResourceManager.getInstance();
169
170    /**
171     *  @private
172     */
173    private var _showBusyCursor:Boolean = false;
174
175    /**
176     *  @private
177     */
178    private var _concurrency:String;
179
180    //--------------------------------------------------------------------------
181    //
182    // Properties
183    //
184    //--------------------------------------------------------------------------
185
186    //----------------------------------
187    //  contentType
188    //----------------------------------
189
190    [Inspectable(enumeration="application/x-www-form-urlencoded,application/xml", defaultValue="application/x-www-form-urlencoded", category="General")]
191    /**
192     *  Type of content for service requests.
193     *  The default is <code>application/x-www-form-urlencoded</code> which sends requests
194     *  like a normal HTTP POST with name-value pairs. <code>application/xml</code> send
195     *  requests as XML.
196     */
197    public var contentType:String = AbstractOperation.CONTENT_TYPE_FORM;
198
199    //----------------------------------
200    //  concurrency
201    //----------------------------------
202
203    [Inspectable(enumeration="multiple,single,last", defaultValue="multiple", category="General")]
204    /**
205     * Value that indicates how to handle multiple calls to the same operation within the service.
206     * The concurrency setting set here will be used for operations that do not specify concurrecny.
207     * Individual operations that have the concurrency setting set directly will ignore the value set here.
208     * The default value is <code>multiple</code>. The following values are permitted:
209     * <ul>
210     * <li><code>multiple</code> Existing requests are not cancelled, and the developer is
211     * responsible for ensuring the consistency of returned data by carefully
212     * managing the event stream. This is the default value.</li>
213     * <li><code>single</code> Only a single request at a time is allowed on the operation;
214     * multiple requests generate a fault.</li>
215     * <li><code>last</code> Making a request cancels any existing request.</li>
216     * </ul>
217     *
218     *  @langversion 3.0
219     *  @playerversion Flash 9
220     *  @playerversion AIR 1.1
221     *  @productversion Flex 3
222     */
223    public function get concurrency():String
224    {
225        return _concurrency;
226    }
227    public function set concurrency(c:String):void
228    {
229        _concurrency = c;
230    }
231
232    //----------------------------------
233    //  showBusyCursor
234    //----------------------------------
235
236    [Inspectable(defaultValue="false", category="General")]
237    /**
238    * If <code>true</code>, a busy cursor is displayed while a service is executing. The default
239    * value is <code>false</code>.
240    *
241    *  @langversion 3.0
242    *  @playerversion Flash 9
243    *  @playerversion AIR 1.1
244    *  @productversion Flex 3
245    */
246    public function get showBusyCursor():Boolean
247    {
248        return _showBusyCursor;
249    }
250
251    public function set showBusyCursor(sbc:Boolean):void
252    {
253        _showBusyCursor = sbc;
254    }
255
256    //----------------------------------
257    //  headers
258    //----------------------------------
259
260    [Inspectable(defaultValue="undefined", category="General")]
261    /**
262     *  Custom HTTP headers to be sent to the third party endpoint. If multiple headers need to
263     *  be sent with the same name the value should be specified as an Array.  These headers are sent
264     *  to all operations.  You can also set headers at the operation level.
265     *
266     *  @langversion 3.0
267     *  @playerversion Flash 9
268     *  @playerversion AIR 1.1
269     *  @productversion Flex 3
270     */
271    public var headers:Object = {};
272
273    //----------------------------------
274    //  makeObjectsBindable
275    //----------------------------------
276
277    [Inspectable(defaultValue="true", category="General")]
278    /**
279     *  When <code>true</code>, the objects returned support data binding to UI controls.
280     *  That means  they send PropertyChangeEvents when their property values are being changed.
281     *  This is the default value for any operations whose makeObjectsBindable property
282     *  is not set explicitly.
283     *
284     *  @langversion 3.0
285     *  @playerversion Flash 9
286     *  @playerversion AIR 1.1
287     *  @productversion Flex 3
288     */
289    public var makeObjectsBindable:Boolean = true;
290
291    //----------------------------------
292    //  method
293    //----------------------------------
294
295    [Inspectable(enumeration="GET,get,POST,post,HEAD,head,OPTIONS,options,PUT,put,TRACE,trace,DELETE,delete", defaultValue="GET", category="General")]
296    /**
297     *  HTTP method for sending the request if a method is not set explicit on the operation.
298     *  Permitted values are <code>GET</code>, <code>POST</code>, <code>HEAD</code>,
299     *  <code>OPTIONS</code>, <code>PUT</code>, <code>TRACE</code> and <code>DELETE</code>.
300     *  Lowercase letters are converted to uppercase letters. The default value is <code>GET</code>.
301     *
302     *  @langversion 3.0
303     *  @playerversion Flash 9
304     *  @playerversion AIR 1.1
305     *  @productversion Flex 3
306     */
307    public var method:String = HTTPRequestMessage.GET_METHOD;
308
309    //----------------------------------
310    //  resultFormat
311    //----------------------------------
312
313    /**
314     *  @private
315     */
316    private var _resultFormat:String = AbstractOperation.RESULT_FORMAT_OBJECT;
317
318    [Inspectable(enumeration="object,array,xml,flashvars,text,e4x", defaultValue="object", category="General")]
319    /**
320     *  Value that indicates how you want to deserialize the result
321     *  returned by the HTTP call. The value for this is based on the following:
322     *  <ul>
323     *  <li>Whether you are returning XML or name/value pairs.</li>
324     *  <li>How you want to access the results; you can access results as an object,
325     *    text, or XML.</li>
326     *  </ul>
327     *
328     *  <p>The default value is <code>object</code>. The following values are permitted:</p>
329     *  <ul>
330     *  <li><code>object</code> The value returned is XML and is parsed as a tree of ActionScript
331     *    objects. This is the default.</li>
332     *  <li><code>array</code> The value returned is XML and is parsed as a tree of ActionScript
333     *    objects however if the top level object is not an Array, a new Array is created and the result
334     *    set as the first item. If makeObjectsBindable is true then the Array
335     *    will be wrapped in an ArrayCollection.</li>
336     *  <li><code>xml</code> The value returned is XML and is returned as literal XML in an
337     *    ActionScript XMLnode object.</li>
338     *  <li><code>flashvars</code> The value returned is text containing
339     *    name=value pairs separated by ampersands, which
340     *  is parsed into an ActionScript object.</li>
341     *  <li><code>text</code> The value returned is text, and is left raw.</li>
342     *  <li><code>e4x</code> The value returned is XML and is returned as literal XML
343     *    in an ActionScript XML object, which can be accessed using ECMAScript for
344     *    XML (E4X) expressions.</li>
345     *  </ul>
346     *
347     *  @langversion 3.0
348     *  @playerversion Flash 9
349     *  @playerversion AIR 1.1
350     *  @productversion Flex 3
351     */
352    public function get resultFormat():String
353    {
354        return _resultFormat;
355    }
356
357    /**
358     *  @private
359     */
360    public function set resultFormat(value:String):void
361    {
362        switch (value)
363        {
364            case AbstractOperation.RESULT_FORMAT_OBJECT:
365            case AbstractOperation.RESULT_FORMAT_ARRAY:
366            case AbstractOperation.RESULT_FORMAT_XML:
367            case AbstractOperation.RESULT_FORMAT_E4X:
368            case AbstractOperation.RESULT_FORMAT_TEXT:
369            case AbstractOperation.RESULT_FORMAT_FLASHVARS:
370            {
371                break;
372            }
373
374            default:
375            {
376                if (value != null && (serializationFilter = SerializationFilter.filterForResultFormatTable[value]) == null)
377                {
378                    var message:String = resourceManager.getString(
379                        "rpc", "invalidResultFormat",
380                        [ value, AbstractOperation.RESULT_FORMAT_OBJECT, AbstractOperation.RESULT_FORMAT_ARRAY,
381                          AbstractOperation.RESULT_FORMAT_XML, AbstractOperation.RESULT_FORMAT_E4X,
382                          AbstractOperation.RESULT_FORMAT_TEXT, AbstractOperation.RESULT_FORMAT_FLASHVARS ]);
383                    throw new ArgumentError(message);
384                }
385            }
386        }
387        _resultFormat = value;
388    }
389
390    /** Default serializationFilter used by all operations which do not set one explicitly */
391    public var serializationFilter:SerializationFilter;
392
393    //----------------------------------
394    //  rootURL
395    //----------------------------------
396
397    /**
398     *  The URL that the HTTPService object should use when computing relative URLs.
399     *  This contains a prefix which is prepended onto any URLs when it is set.
400     *  It defaults to null in which case the URL for the SWF is used to compute
401     *  relative URLs.
402     *
403     *  @langversion 3.0
404     *  @playerversion Flash 9
405     *  @playerversion AIR 1.1
406     *  @productversion Flex 3
407     */
408    public var baseURL:String;
409
410    /**
411     *  @private
412     */
413    override public function set destination(value:String):void
414    {
415        useProxy = true;
416        super.destination = value;
417    }
418
419    /**
420     *  @private
421     */
422    private var _useProxy:Boolean = false;
423
424    [Inspectable(defaultValue="false", category="General")]
425    /**
426     *  Specifies whether to use the Flex proxy service. The default value is <code>false</code>. If you
427     *  do not specify <code>true</code> to proxy requests though the Flex server, you must ensure that the player
428     *  can reach the target URL. You also cannot use destinations defined in the services-config.xml file if the
429     *  <code>useProxy</code> property is set to <code>false</code>.
430     *
431     *  @default false
432     *
433     *  @langversion 3.0
434     *  @playerversion Flash 9
435     *  @playerversion AIR 1.1
436     *  @productversion Flex 3
437     */
438    public function get useProxy():Boolean
439    {
440        return _useProxy;
441    }
442
443    /**
444     *  @private
445     */
446    public function set useProxy(value:Boolean):void
447    {
448        if (value != _useProxy)
449        {
450            _useProxy = value;
451            var dcs:ChannelSet = getDirectChannelSet();
452            if (!useProxy)
453            {
454                if (dcs != asyncRequest.channelSet)
455                    asyncRequest.channelSet = dcs;
456            }
457            else
458            {
459                if (asyncRequest.channelSet == dcs)
460                    asyncRequest.channelSet = null;
461            }
462        }
463    }
464
465    /**
466     * This serves as the default property for this instance so that we can
467     * define a set of operations as direct children of the HTTPMultiService
468     * tag in MXML.
469     *
470     *  @langversion 3.0
471     *  @playerversion Flash 9
472     *  @playerversion AIR 1.1
473     *  @productversion Flex 3
474     */
475    public function set operationList(ol:Array):void
476    {
477        if (ol == null)
478            operations = null;
479        else
480        {
481            var op:AbstractOperation;
482            var ops:Object = new Object();
483            for (var i:int = 0; i < ol.length; i++)
484            {
485                op = AbstractOperation(ol[i]);
486                var name:String = op.name;
487                if (!name)
488                    throw new ArgumentError("Operations must have a name property value for HTTPMultiService");
489                ops[name] = op;
490            }
491            operations = ops;
492        }
493    }
494
495    public function get operationList():Array
496    {
497        // Note: does not preserve order of the elements
498        if (operations == null)
499            return null;
500        var ol:Array = new Array();
501        for (var i:String in operations)
502        {
503            var op:AbstractOperation = operations[i];
504            ol.push(op);
505        }
506        return ol
507    }
508
509    //--------------------------------------------------------------------------
510    //
511    // Internal Methods
512    //
513    //--------------------------------------------------------------------------
514
515    mx_internal function getDirectChannelSet():ChannelSet
516    {
517        if (_directChannelSet == null)
518        {
519            var dcs:ChannelSet = new ChannelSet();
520            var dhc:DirectHTTPChannel = new DirectHTTPChannel("direct_http_channel");
521            dhc.requestTimeout = requestTimeout;
522            dcs.addChannel(dhc);
523            _directChannelSet = dcs;
524        }
525        return _directChannelSet;
526    }
527}
528
529}
530