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 }