View Javadoc

1   // ========================================================================
2   // Copyright 2006 Mort Bay Consulting Pty. Ltd.
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   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  
15  package org.mortbay.cometd.ext;
16  
17  import java.util.Map;
18  
19  import org.cometd.Client;
20  import org.cometd.Extension;
21  import org.cometd.Message;
22  import org.mortbay.cometd.ClientImpl;
23  import org.mortbay.util.ajax.JSON;
24  
25  
26  
27  /* ------------------------------------------------------------ */
28  /**
29   * Timesync extension (server side).
30   * 
31   * With each handshake or connect, the extension sends timestamps within the 
32   * ext field like: <code>{ext:{timesync:{tc:12345567890,l:23,o:4567},...},...}</code>
33   * where:<ul>
34   *  <li>tc is the client timestamp in ms since 1970 of when the message was sent.
35   *  <li>l is the network lag that the client has calculated.
36   *  <li>o is the clock offset that the client has calculated.
37   * </ul>
38   * The accuracy of the offset and lag may be calculated with tc-now-l-o,
39   * which should be zero if the calculated offset and lag are perfectly
40   * accurate.
41   * <p>
42   * A cometd server that supports timesync, should respond only if the
43   * measured accuracy value is greater than accuracy target. The response
44   * will be an ext field like: <code>{ext:{timesync:{tc:12345567890,ts:1234567900,p:123,a:3},...},...}</code>
45   * where:<ul>
46   *  <li>tc is the client timestamp of when the message was sent,
47   *  <li>ts is the server timestamp of when the message was received
48   *  <li>p is the poll duration in ms - ie the time the server took before sending the response.
49   *  <li>a is the measured accuracy of the calculated offset and lag sent by the client
50   * </ul>
51   * 
52   * On receipt of the response, the client is able to use current time to determine
53   * the total trip time, from which p is subtracted to determine an approximate
54   * two way network traversal time. The measured accuracy is used to adjust the assumption 
55   * that the network is symmetric for traversal time, so: <ul>
56   * <li>lag = (now-tc-p)/2-a
57   * <li>offset = ts-tc-lag
58   * </ul>
59   *
60   */
61  public class TimesyncExtension implements Extension
62  {
63      private int _accuracyTarget=25;
64      
65      public TimesyncExtension()
66      {
67      }
68      
69      /* ------------------------------------------------------------ */
70      /**
71       * timesync responses are not set if the measured accuracy is
72       * less than the accuracyTarget.
73       * @return accuracy target in ms (default 25ms)
74       */
75      public int getAccuracyTarget()
76      {
77          return _accuracyTarget;
78      }
79  
80      /* ------------------------------------------------------------ */
81      /**
82       * timesync responses are not set if the measured accuracy is
83       * less than the accuracyTarget.
84       * @param target accuracy target in ms
85       */
86      public void setAccuracyTarget(int target)
87      {
88          _accuracyTarget = target;
89      }
90  
91      public Message rcv(Client from, Message message)
92      {
93          return message;
94      }
95  
96      public Message rcvMeta(Client from, Message message)
97      {
98          Map<String,Object> ext=message.getExt(false);
99          if (ext!=null)
100         {
101             Map<String,Object> sync=(Map<String,Object>)ext.get("timesync");
102             if (sync!=null)
103             {
104                 sync.put("ts",new Long(System.currentTimeMillis()));
105                 Number lag=(Number)sync.get("l");
106                 if (lag!=null && from !=null)
107                     ((ClientImpl)from).setLag(lag.intValue());
108             }
109         }
110         return message;
111     }
112 
113     public Message send(Client from, Message message)
114     {
115         return message;
116     }
117 
118     public Message sendMeta(Client from, Message message)
119     {
120         Message associated = message.getAssociated();
121         if (associated!=null)
122         {
123             Map<String,Object> extIn=associated.getExt(false);
124             
125             if (extIn!=null)
126             {
127                 Map<String,Object> sync=(Map<String,Object>)extIn.get("timesync");
128                 if (sync!=null)
129                 {
130                     final long tc=((Number)sync.get("tc")).longValue();
131                     final long ts=((Number)sync.get("ts")).longValue();
132                     
133                     Number lag=(Number)sync.get("l");
134                     if (lag==null)
135                     {
136                         // old style timesync
137                         Map<String,Object> extOut=(Map<String,Object>)message.getExt(true);
138                         JSON.Literal timesync=new JSON.Literal("{\"tc\":"+tc+",\"ts\":"+ts+",\"p\":"+(System.currentTimeMillis()-ts)+"}");
139                         extOut.put("timesync",timesync);
140                     }
141                     else
142                     {
143                         final int l=lag.intValue();
144                         final int o=((Number)sync.get("o")).intValue();
145                         final int a=(int)(tc-ts)+o+l;
146 
147                         // is a OK ?
148                         if (a>=_accuracyTarget||a<=-_accuracyTarget)
149                         {
150                             Map<String,Object> extOut=(Map<String,Object>)message.getExt(true);
151                             JSON.Literal timesync=new JSON.Literal("{\"tc\":"+tc+",\"ts\":"+ts+",\"p\":"+(System.currentTimeMillis()-ts)+",\"a\":"+a+"}");
152                             extOut.put("timesync",timesync);
153                         }
154                     }
155                 }
156             }
157         }
158         return message;
159     }
160 }