r25048: From the archives (patch found in one of my old working trees):
[jelmer/samba4-debian.git] / webapps / qooxdoo-0.6.5-sdk / frontend / framework / source / class / qx / io / remote / RequestQueue.js
1 /* ************************************************************************
2
3    qooxdoo - the new era of web development
4
5    http://qooxdoo.org
6
7    Copyright:
8      2004-2007 1&1 Internet AG, Germany, http://www.1and1.org
9      2006 Derrell Lipman
10
11    License:
12      LGPL: http://www.gnu.org/licenses/lgpl.html
13      EPL: http://www.eclipse.org/org/documents/epl-v10.php
14      See the LICENSE file in the project's top-level directory for details.
15
16    Authors:
17      * Sebastian Werner (wpbasti)
18      * Andreas Ecker (ecker)
19      * Derrell Lipman (derrell)
20
21 ************************************************************************ */
22
23 /* ************************************************************************
24
25 #module(io_remote)
26
27 ************************************************************************ */
28
29 /**
30  * Handles scheduling of requests to be sent to a server.
31  *
32  * This class is a singleton and is used by qx.io.remote.Request to schedule its
33  * requests. It should not be used directly.
34  */
35 qx.OO.defineClass("qx.io.remote.RequestQueue", qx.core.Target,
36 function()
37 {
38   qx.core.Target.call(this);
39
40   this._queue = [];
41   this._active = [];
42
43   this._totalRequests = 0;
44
45   // timeout handling
46   this._timer = new qx.client.Timer(500);
47   this._timer.addEventListener("interval", this._oninterval, this);
48 });
49
50
51
52
53 /*
54 ---------------------------------------------------------------------------
55   PROPERTIES
56 ---------------------------------------------------------------------------
57 */
58
59 /**
60  * @deprecated
61  */
62 qx.OO.addProperty({ name : "maxTotalRequests", type : "number" });
63
64 /**
65  * Maximum number of parallel requests.
66  */
67 qx.OO.addProperty({ name : "maxConcurrentRequests", type : "number", defaultValue : 3 });
68
69 /**
70  * Default timeout for remote requests in milliseconds.
71  */
72 qx.OO.addProperty({ name : "defaultTimeout", type : "number", defaultValue : 5000 });
73
74
75
76
77
78
79 /*
80 ---------------------------------------------------------------------------
81   QUEUE HANDLING
82 ---------------------------------------------------------------------------
83 */
84
85 qx.Proto._debug = function()
86 {
87   // Debug output
88   var vText = this._active.length + "/" + (this._queue.length+this._active.length);
89
90   if (qx.Settings.getValueOfClass("qx.io.remote.Exchange", "enableDebug"))
91   {
92     this.debug("Progress: " + vText);
93     window.status = "Request-Queue Progress: " + vText;
94   }
95 }
96
97 qx.Proto._check = function()
98 {
99   // Debug output
100   this._debug();
101
102   // Check queues and stop timer if not needed anymore
103   if (this._active.length == 0 && this._queue.length == 0) {
104     this._timer.stop();
105   }
106
107   // Checking if enabled
108   if (!this.getEnabled()) {
109     return;
110   }
111
112   // Checking active queue fill
113   if (this._active.length >= this.getMaxConcurrentRequests() || this._queue.length == 0) {
114     return;
115   }
116
117   // Checking number of total requests
118   if (this.getMaxTotalRequests() != null && this._totalRequests >= this.getMaxTotalRequests()) {
119     return;
120   }
121
122   var vRequest = this._queue.shift();
123   var vTransport = new qx.io.remote.Exchange(vRequest);
124
125   // Increment counter
126   this._totalRequests++;
127
128   // Add to active queue
129   this._active.push(vTransport);
130
131   // Debug output
132   this._debug();
133
134   // Establish event connection between qx.io.remote.Exchange instance and qx.io.remote.Request
135   vTransport.addEventListener("sending", vRequest._onsending, vRequest);
136   vTransport.addEventListener("receiving", vRequest._onreceiving, vRequest);
137   vTransport.addEventListener("completed", vRequest._oncompleted, vRequest);
138   vTransport.addEventListener("aborted", vRequest._onaborted, vRequest);
139   vTransport.addEventListener("timeout", vRequest._ontimeout, vRequest);
140   vTransport.addEventListener("failed", vRequest._onfailed, vRequest);
141
142   // Establish event connection between qx.io.remote.Exchange and me.
143   vTransport.addEventListener("sending", this._onsending, this);
144   vTransport.addEventListener("completed", this._oncompleted, this);
145   vTransport.addEventListener("aborted", this._oncompleted, this);
146   vTransport.addEventListener("timeout", this._oncompleted, this);
147   vTransport.addEventListener("failed", this._oncompleted, this);
148
149   // Store send timestamp
150   vTransport._start = (new Date).valueOf();
151
152   // Send
153   vTransport.send();
154
155   // Retry
156   if (this._queue.length > 0) {
157     this._check();
158   }
159 }
160
161 qx.Proto._remove = function(vTransport)
162 {
163   var vRequest = vTransport.getRequest();
164
165   // Destruct event connection between qx.io.remote.Exchange instance and qx.io.remote.Request
166   vTransport.removeEventListener("sending", vRequest._onsending, vRequest);
167   vTransport.removeEventListener("receiving", vRequest._onreceiving, vRequest);
168   vTransport.removeEventListener("completed", vRequest._oncompleted, vRequest);
169   vTransport.removeEventListener("aborted", vRequest._onaborted, vRequest);
170   vTransport.removeEventListener("timeout", vRequest._ontimeout, vRequest);
171   vTransport.removeEventListener("failed", vRequest._onfailed, vRequest);
172
173   // Destruct event connection between qx.io.remote.Exchange and me.
174   vTransport.removeEventListener("sending", this._onsending, this);
175   vTransport.removeEventListener("completed", this._oncompleted, this);
176   vTransport.removeEventListener("aborted", this._oncompleted, this);
177   vTransport.removeEventListener("timeout", this._oncompleted, this);
178   vTransport.removeEventListener("failed", this._oncompleted, this);
179
180   // Remove from active transports
181   qx.lang.Array.remove(this._active, vTransport);
182
183   // Dispose transport object
184   vTransport.dispose();
185
186   // Check again
187   this._check();
188 }
189
190
191
192
193
194
195
196 /*
197 ---------------------------------------------------------------------------
198   EVENT HANDLING
199 ---------------------------------------------------------------------------
200 */
201
202 qx.Proto._activeCount = 0;
203
204 qx.Proto._onsending = function(e)
205 {
206   if (qx.Settings.getValueOfClass("qx.io.remote.Exchange", "enableDebug"))
207   {
208     this._activeCount++;
209     e.getTarget()._counted = true;
210
211     this.debug("ActiveCount: " + this._activeCount);
212   }
213 }
214
215 qx.Proto._oncompleted = function(e)
216 {
217   if (qx.Settings.getValueOfClass("qx.io.remote.Exchange", "enableDebug"))
218   {
219     if (e.getTarget()._counted)
220     {
221       this._activeCount--;
222       this.debug("ActiveCount: " + this._activeCount);
223     }
224   }
225
226   this._remove(e.getTarget());
227 }
228
229
230
231
232
233
234
235 /*
236 ---------------------------------------------------------------------------
237   TIMEOUT HANDLING
238 ---------------------------------------------------------------------------
239 */
240
241 qx.Proto._oninterval = function(e)
242 {
243   var vActive = this._active;
244
245   if (vActive.length == 0) {
246     return;
247   }
248
249   var vCurrent = (new Date).valueOf();
250   var vTransport;
251   var vRequest;
252   var vDefaultTimeout = this.getDefaultTimeout();
253   var vTimeout;
254   var vTime;
255
256   for (var i=vActive.length-1; i>=0; i--)
257   {
258     vTransport = vActive[i];
259     vRequest = vTransport.getRequest();
260     if (vRequest.isAsynchronous()) {
261       vTimeout = vRequest.getTimeout();
262
263       // if timer is disabled...
264       if (vTimeout == 0) {
265         // then ignore it.
266         continue;
267       }
268
269       if (vTimeout == null) {
270         vTimeout = vDefaultTimeout;
271       }
272
273       vTime = vCurrent - vTransport._start;
274
275       if (vTime > vTimeout)
276       {
277         this.warn("Timeout: transport " + vTransport.toHashCode());
278         this.warn(vTime + "ms > " + vTimeout + "ms");
279         vTransport.timeout();
280       }
281     }
282   }
283 }
284
285
286
287
288 /*
289 ---------------------------------------------------------------------------
290   MODIFIERS
291 ---------------------------------------------------------------------------
292 */
293
294 qx.Proto._modifyEnabled = function(propValue, propOldValue, propData)
295 {
296   if (propValue) {
297     this._check();
298   }
299
300   this._timer.setEnabled(propValue);
301
302   return true;
303 }
304
305
306
307
308
309
310
311 /*
312 ---------------------------------------------------------------------------
313   CORE METHODS
314 ---------------------------------------------------------------------------
315 */
316 /*!
317   Add the request to the pending requests queue.
318 */
319 qx.Proto.add = function(vRequest)
320 {
321   vRequest.setState("queued");
322
323   this._queue.push(vRequest);
324   this._check();
325
326   if (this.getEnabled()) {
327     this._timer.start();
328   }
329 }
330
331 /*!
332   Remove the request from the pending requests queue.
333
334   The underlying transport of the request is forced into the aborted
335   state ("aborted") and listeners of the "aborted"
336   signal are notified about the event. If the request isn't in the
337   pending requests queue, this method is a noop.
338 */
339 qx.Proto.abort = function(vRequest)
340 {
341   var vTransport = vRequest.getTransport();
342
343   if (vTransport)
344   {
345     vTransport.abort();
346   }
347   else if (qx.lang.Array.contains(this._queue, vRequest))
348   {
349     qx.lang.Array.remove(this._queue, vRequest);
350   }
351 }
352
353
354
355
356
357
358
359 /*
360 ---------------------------------------------------------------------------
361   DISPOSER
362 ---------------------------------------------------------------------------
363 */
364
365 /**
366  * Disposer
367  */
368 qx.Proto.dispose = function()
369 {
370   if (this.getDisposed()) {
371     return true;
372   }
373
374   if (this._active)
375   {
376     for (var i=0, a=this._active, l=a.length; i<l; i++) {
377       this._remove(a[i]);
378     }
379
380     this._active = null;
381   }
382
383   if (this._timer)
384   {
385     this._timer.removeEventListener("interval", this._oninterval, this);
386     this._timer = null;
387   }
388
389   this._queue = null;
390
391   return qx.core.Target.prototype.dispose.call(this);
392 }
393
394
395
396
397
398
399
400 /*
401 ---------------------------------------------------------------------------
402   DEFER SINGLETON INSTANCE
403 ---------------------------------------------------------------------------
404 */
405
406 /**
407  * Singleton Instance Getter
408  */
409 qx.Class.getInstance = qx.lang.Function.returnInstance;