506329830f9d486ceeb38a90b7054bfbf28d1dc3
[vnfsdk/refrepo.git] / openo-portal / portal-common / src / main / webapp / common / thirdparty / cometd / cometd / TimeSyncExtension.js
1 /*
2  * Copyright (c) 2010 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 (function()
18 {
19     function bind(org_cometd)
20     {
21         /**
22          * With each handshake or connect, the extension sends timestamps within the
23          * ext field like: <code>{ext:{timesync:{tc:12345567890,l:23,o:4567},...},...}</code>
24          * where:<ul>
25          *  <li>tc is the client timestamp in ms since 1970 of when the message was sent.
26          *  <li>l is the network lag that the client has calculated.
27          *  <li>o is the clock offset that the client has calculated.
28          * </ul>
29          *
30          * <p>
31          * A cometd server that supports timesync, can respond with an ext
32          * field like: <code>{ext:{timesync:{tc:12345567890,ts:1234567900,p:123,a:3},...},...}</code>
33          * where:<ul>
34          *  <li>tc is the client timestamp of when the message was sent,
35          *  <li>ts is the server timestamp of when the message was received
36          *  <li>p is the poll duration in ms - ie the time the server took before sending the response.
37          *  <li>a is the measured accuracy of the calculated offset and lag sent by the client
38          * </ul>
39          *
40          * <p>
41          * The relationship between tc, ts & l is given by <code>ts=tc+o+l</code> (the
42          * time the server received the messsage is the client time plus the offset plus the
43          * network lag).   Thus the accuracy of the o and l settings can be determined with
44          * <code>a=(tc+o+l)-ts</code>.
45          * </p>
46          * <p>
47          * When the client has received the response, it can make a more accurate estimate
48          * of the lag as <code>l2=(now-tc-p)/2</code> (assuming symmetric lag).
49          * A new offset can then be calculated with the relationship on the client
50          * that <code>ts=tc+o2+l2</code>, thus <code>o2=ts-tc-l2</code>.
51          * </p>
52          * <p>
53          * Since the client also receives the a value calculated on the server, it
54          * should be possible to analyse this and compensate for some asymmetry
55          * in the lag. But the current client does not do this.
56          * </p>
57          *
58          * @param configuration
59          */
60         return org_cometd.TimeSyncExtension = function(configuration)
61         {
62             var _cometd;
63             var _maxSamples = configuration && configuration.maxSamples || 10;
64             var _lags = [];
65             var _offsets = [];
66             var _lag = 0;
67             var _offset = 0;
68
69             function _debug(text, args)
70             {
71                 _cometd._debug(text, args);
72             }
73
74             this.registered = function(name, cometd)
75             {
76                 _cometd = cometd;
77                 _debug('TimeSyncExtension: executing registration callback');
78             };
79
80             this.unregistered = function()
81             {
82                 _debug('TimeSyncExtension: executing unregistration callback');
83                 _cometd = null;
84                 _lags = [];
85                 _offsets = [];
86             };
87
88             this.incoming = function(message)
89             {
90                 var channel = message.channel;
91                 if (channel && channel.indexOf('/meta/') === 0)
92                 {
93                     if (message.ext && message.ext.timesync)
94                     {
95                         var timesync = message.ext.timesync;
96                         _debug('TimeSyncExtension: server sent timesync', timesync);
97
98                         var now = new Date().getTime();
99                         var l2 = (now - timesync.tc - timesync.p) / 2;
100                         var o2 = timesync.ts - timesync.tc - l2;
101
102                         _lags.push(l2);
103                         _offsets.push(o2);
104                         if (_offsets.length > _maxSamples)
105                         {
106                             _offsets.shift();
107                             _lags.shift();
108                         }
109
110                         var samples = _offsets.length;
111                         var lagsSum = 0;
112                         var offsetsSum = 0;
113                         for (var i = 0; i < samples; ++i)
114                         {
115                             lagsSum += _lags[i];
116                             offsetsSum += _offsets[i];
117                         }
118                         _lag = parseInt((lagsSum / samples).toFixed());
119                         _offset = parseInt((offsetsSum / samples).toFixed());
120                         _debug('TimeSyncExtension: network lag', _lag, 'ms, time offset with server', _offset, 'ms', _lag, _offset);
121                     }
122                 }
123                 return message;
124             };
125
126             this.outgoing = function(message)
127             {
128                 var channel = message.channel;
129                 if (channel && channel.indexOf('/meta/') === 0)
130                 {
131                     if (!message.ext)
132                     {
133                         message.ext = {};
134                     }
135                     message.ext.timesync = {
136                         tc: new Date().getTime(),
137                         l: _lag,
138                         o: _offset
139                     };
140                     _debug('TimeSyncExtension: client sending timesync', org_cometd.JSON.toJSON(message.ext.timesync));
141                 }
142                 return message;
143             };
144
145             /**
146              * Get the estimated offset in ms from the clients clock to the
147              * servers clock.  The server time is the client time plus the offset.
148              */
149             this.getTimeOffset = function()
150             {
151                 return _offset;
152             };
153
154             /**
155              * Get an array of multiple offset samples used to calculate
156              * the offset.
157              */
158             this.getTimeOffsetSamples = function()
159             {
160                 return _offsets;
161             };
162
163             /**
164              * Get the estimated network lag in ms from the client to the server.
165              */
166             this.getNetworkLag = function()
167             {
168                 return _lag;
169             };
170
171             /**
172              * Get the estimated server time in ms since the epoch.
173              */
174             this.getServerTime = function()
175             {
176                 return new Date().getTime() + _offset;
177             };
178
179             /**
180              *
181              * Get the estimated server time as a Date object
182              */
183             this.getServerDate = function()
184             {
185                 return new Date(this.getServerTime());
186             };
187
188             /**
189              * Set a timeout to expire at given time on the server.
190              * @param callback The function to call when the timer expires
191              * @param atServerTimeOrDate a js Time or Date object representing the
192              * server time at which the timeout should expire
193              */
194             this.setTimeout = function(callback, atServerTimeOrDate)
195             {
196                 var ts = (atServerTimeOrDate instanceof Date) ? atServerTimeOrDate.getTime() : (0 + atServerTimeOrDate);
197                 var tc = ts - _offset;
198                 var interval = tc - new Date().getTime();
199                 if (interval <= 0)
200                 {
201                     interval = 1;
202                 }
203                 return org_cometd.Utils.setTimeout(_cometd, callback, interval);
204             };
205         };
206     }
207
208     if (typeof define === 'function' && define.amd)
209     {
210         define(['org/cometd'], bind);
211     }
212     else
213     {
214         bind(org.cometd);
215     }
216 })();