Mercurial > hg > RemoteEditor > REPSessionManager
annotate rep/handler/Editor.java @ 471:d2762d669617
sleep command to wait for singleton prevention
author | one |
---|---|
date | Tue, 12 Oct 2010 10:19:59 +0900 |
parents | e252d092b720 |
children | 596cc0a3beea 200166a494e9 |
rev | line source |
---|---|
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
1 package rep.handler; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
2 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
3 import java.io.IOException; |
468 | 4 import java.util.Comparator; |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
5 import java.util.LinkedList; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
6 import java.util.List; |
468 | 7 import java.util.TreeSet; |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
8 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
9 import rep.REP; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
10 import rep.REPCommand; |
384 | 11 import rep.ServerMainLoop; |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
12 import rep.SessionManager; |
468 | 13 import rep.channel.REPLogger; |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
14 import rep.channel.REPSelectionKey; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
15 import rep.channel.REPSocketChannel; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
16 import rep.optimizers.*; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
17 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
18 public class Editor extends Forwarder { |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
19 |
387 | 20 // REPCommands we are going to send to the next editor |
439 | 21 private LinkedList<REPCommand> sentList = new LinkedList<REPCommand>(); |
464 | 22 // Expected acknowledge list |
23 private LinkedList<REPCommand> ackList = new LinkedList<REPCommand>(); | |
460 | 24 public LinkedList<REPCommand> waitingCommandInMerge= new LinkedList<REPCommand>(); |
462 | 25 private REPCommand quit_2=null; |
442 | 26 private REPCommand preMergeCommand; |
468 | 27 |
28 public REPCommandOptimizer optimizer; | |
29 private LinkedList<REPCommand> unMergedCmds; | |
30 private LinkedList<REPCommand> sentMergedList; | |
31 private TreeSet<REPCommand> sortedEditCmds; | |
32 boolean mergeAgain; | |
33 public REPLogger logger = SessionManager.logger; | |
34 boolean merge_mode = false; | |
35 | |
387 | 36 private boolean merging; |
391 | 37 public static boolean noMergeMode=false; |
445 | 38 static final boolean doOptimize = false; |
460 | 39 private LinkedList<REPCommand> writeQueue = new LinkedList<REPCommand>(); |
464 | 40 static boolean slowMerge = true; |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
41 |
385 | 42 public Editor(SessionManager manager,int editorNo){ |
387 | 43 // no translator case |
44 super(manager, null); | |
45 } | |
46 | |
468 | 47 public Editor(int eid, REPCommandOptimizer optimizer) { |
48 super(null, null); | |
49 this.optimizer = optimizer; | |
50 } | |
51 | |
387 | 52 public Editor(int editorNo, SessionManager manager,REPSocketChannel<REPCommand> channel){ |
53 super(editorNo,manager,channel); | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
54 eid = editorNo; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
55 if (doOptimize) optimizer = new DeleteInsertOptimizer(); //タカノがつくったおぷてぃまいざ |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
56 else optimizer = new NullOptimizer(); //なにもしないけどOptimizer. |
468 | 57 |
58 unMergedCmds = new LinkedList<REPCommand>(); | |
59 mergeAgain = false; | |
60 sentMergedList = new LinkedList<REPCommand>(); | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
61 } |
387 | 62 |
427 | 63 /* |
64 * Merge Protocol | |
445 | 65 (0) Editor へのコマンドは、ack 以外は直接 Editor へ送られてしまう。(next.send(cmd)) |
66 Editor から返ってくるコマンドをtranslatorが処理する。 | |
427 | 67 (1) Editor CommandをSession Ring 上に流し、それが戻って来るまでに、他のEditorから |
68 受け取った Editor Command をキューに入れておく。 | |
69 (2) 戻って来たタイミングで、キュー上のEditor Commandを、eid とCommandの | |
70 順序を基にソートする。(self merge) | |
71 (3) 他のEditorにソートのタイミングを与えるために、Editor Command の | |
72 ack を、もう一周させる。 | |
73 (4) 他のEditorのCommandを受け取ってから、ack が来るまでのCommandをキューに | |
74 入れておき、ack が来たら、eid とCommandの順序を基にソートする。(other merge) | |
75 | |
76 Editor には、ソートした編集結果になるように、それまで行なった編集をUndo | |
77 して、ソートした編集結果を適用する。Undo が無駄な動作をしないように最適化する。 | |
78 | |
79 handle() | |
80 セッションの処理 | |
81 manage() | |
82 編集コマンドは translate() へ | |
83 一周して来た編集コマンドのACKは廃棄 (merge queue から削除) | |
84 一周して来た自分のコマンドならself merge | |
85 他のエディタの編集コマンドのACK->other merge | |
86 それ以外は、そのまま実行、merge queue へ格納 | |
87 merge は checkReturnedCommand() から | |
88 startMerge() へ | |
89 まず、接続されている Editor に START_MERGE を送る | |
90 邪魔されないように、他のcommand は block する | |
91 manager() | |
92 START_MERGE_ACK が来たら、translator.mergeAck() で教えて、 | |
93 merge()-> | |
94 translator.checkOwnCommand() へ | |
95 ここで、sort されて、Merge Command をEditorへ送信 | |
96 checkEndMerge()から | |
97 endMerge() が呼ばれる。 | |
98 自分のエディタにEND_MERGE で Merge終了を通知 | |
99 自分のコマンドは、ACKに変えて送信 (3) | |
100 それ以外は、そのまま送信 (一周させる) | |
101 | |
102 */ | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
103 |
468 | 104 |
397 | 105 public void translate(REPCommand command){ |
106 switch(command.cmd) { | |
107 case REPCMD_INSERT_ACK: | |
108 case REPCMD_DELETE_ACK: | |
109 if (command.eid==eid) { | |
462 | 110 if (slowMerge) { |
111 checkReturnedCommand(command); | |
112 checkQuit(); | |
113 return; | |
114 } | |
397 | 115 // Second Phase が終わって同期が終了。 |
442 | 116 // SessionManager.logger.writeLog("Complete "+command); |
452 | 117 checkAck(command); |
410 | 118 checkQuit(); |
397 | 119 return; |
120 } | |
121 checkReturnedCommand(command); | |
122 return; | |
400 | 123 case REPCMD_INSERT_USER: |
124 command.cmd = REP.REPCMD_INSERT; | |
125 userEditorCommand(command); | |
126 return; | |
127 case REPCMD_DELETE_USER: | |
406 | 128 command.cmd = REP.REPCMD_DELETE; |
400 | 129 userEditorCommand(command); |
130 return; | |
401 | 131 case REPCMD_INSERT: |
132 case REPCMD_DELETE: | |
465 | 133 case REPCMD_MERGE_MARK: |
401 | 134 if (command.eid == REP.MERGE_EID.id){ |
135 //マージコマンドが返ってきた | |
468 | 136 if(checkMergeConflict(command)){ |
401 | 137 //マージ中にエディタからの割り込みがあった場合 |
468 | 138 getMergeAgain(this); |
401 | 139 } |
140 checkEndMerge(); | |
141 return; | |
457 | 142 } |
143 if (command.eid == eid){ | |
401 | 144 // 編集コマンドが一周して来た |
462 | 145 if (slowMerge) { |
146 checkAck(command); | |
147 sendAck(command); | |
148 } else { | |
149 checkReturnedCommand(command); | |
150 } | |
400 | 151 return; |
152 } | |
401 | 153 |
154 //他のエディタからの編集コマンド | |
468 | 155 transReceiveCmd(next,command); |
431 | 156 |
401 | 157 sendEditorCommand(command); |
400 | 158 return; |
401 | 159 default: |
160 assert(false); | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
161 } |
400 | 162 } |
163 | |
164 private void userEditorCommand(REPCommand command) { | |
165 //エディタからの新たな編集コマンド | |
166 if (next==this) return; // singleton case | |
468 | 167 transSendCmd(command); |
400 | 168 sendEditorCommand(command); |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
169 return; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
170 } |
398 | 171 |
404 | 172 // private void checkDouble(List<REPCommand> sentList) { |
173 // if (sentList.size()==0) return; | |
174 // int count = 0; | |
175 // REPCommand f = sentList.get(0); | |
176 // for(REPCommand c:sentList) { | |
177 // if (c.eid==f.eid&&c.seq==f.seq) { | |
178 // count++; | |
179 // } | |
180 // } | |
181 // assert(count==1); | |
182 // if (true) return; | |
183 // count = 0; | |
184 // for(PacketSet c:waitingCommandInMerge) { | |
185 // for(REPCommand g:sentList) { | |
186 // if (c.command.eid==g.eid&&c.command.seq==g.seq) { | |
187 // count++; | |
188 // } | |
189 // } | |
190 // } | |
191 // assert(count==0); | |
192 // } | |
399 | 193 |
460 | 194 /** |
195 * Sending to Editor and waiting Queue | |
196 * +--------+ | |
197 * send() --> write() -> | Editor | -> handle() -> manager() | |
198 * +--------+ | |
199 * waitingQueue | |
200 * writeQueue | |
201 * | |
202 * send() は、他のEditor Node から呼ばれる | |
203 * write() は、内部で優先的に送信するのに用いる | |
204 * writeQueue は、waitingQueue よりも常に先に実行される必要がある | |
205 | |
206 * Manageの送信キューはここでは使わない | |
207 * send() manage | |
208 */ | |
209 @Override | |
210 public void send(REPCommand command) { | |
211 if (merging || isMerging() || waitingCommandInMerge.size()>0) { | |
212 waitingCommandInMerge.addLast(command); | |
213 ServerMainLoop.logger.writeLog("Editor eid:"+eid+" waitingCommandInMerge = "+waitingCommandInMerge); | |
214 return; | |
215 } | |
216 if (isMergeCommand(command)) { | |
217 merging = true; | |
218 ServerMainLoop.logger.writeLog("Editor"+eid+": merging=true (send)"+command); | |
219 } | |
220 writeQueue.add(command); | |
398 | 221 } |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
222 |
460 | 223 /** |
224 * Check waiting command in merge | |
225 * periodically called from manager | |
226 */ | |
227 public void checkWaitingCommandInMerge() { | |
228 if (writeQueue.size()>0) { | |
229 REPCommand command =writeQueue.pollFirst(); | |
230 ServerMainLoop.logger.writeLog("Editor"+eid+": write comand="+command); | |
231 super.write(command); | |
232 return; | |
233 } | |
468 | 234 if (merging || isMerging()) return; |
460 | 235 if (waitingCommandInMerge.size()>0) { |
236 REPCommand command = waitingCommandInMerge.pollFirst(); | |
237 ServerMainLoop.logger.writeLog("Editor"+eid+": send waiting comand="+command); | |
238 super.write(command); | |
239 if (isMergeCommand(command)) { | |
240 merging = true; | |
241 } | |
242 } | |
399 | 243 } |
431 | 244 /** |
245 * 他のエディタへのコマンドの送信 | |
246 * @param command | |
247 * | |
248 * sendList にキープする必要がある。 | |
249 */ | |
397 | 250 private void sendEditorCommand(REPCommand command) { |
251 REPCommand keep = new REPCommand(command); | |
252 sentList.add(keep); | |
464 | 253 ackList.add(keep); |
407 | 254 //ServerMainLoop.logger.writeLog("Editor eid:"+eid+" sentList = "+sentList); |
464 | 255 assert(ackList.size()<limit); |
431 | 256 if (command.cmd==REP.REPCMD_DELETE) { |
257 // delete のundo用の文字列は、外に出す意味はない | |
258 command.string=null; | |
259 } | |
397 | 260 next.send(command); |
261 } | |
262 | |
431 | 263 public List<REPCommand> getSentList() { |
264 return sentList; | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
265 } |
465 | 266 |
267 public void setSentList(LinkedList<REPCommand> list) { | |
268 sentList = list; | |
269 } | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
270 |
391 | 271 /** |
272 * 一周して来たcommandの処理。 | |
404 | 273 * |
274 * INSERT/DELETEを受け取った時に、sentListに登録 | |
275 * INSERT_ACK/DELETE_ACKが来たら一周。そこで、Mergeする。 | |
276 * | |
277 * 自分が出したINSERT/DELETEが戻って来たら、ACKに変更して、Merge。 | |
278 * | |
279 * 途中から参加した場合、自分が受けとってないcommandのACKが先に来ることが | |
280 * ある。それは、無視して良い。 | |
391 | 281 * @param command |
282 */ | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
283 void checkReturnedCommand(REPCommand command) { |
451 | 284 ServerMainLoop.logger.writeLog("Editor"+eid+": startMerge "+command); |
397 | 285 preMergeCommand = new REPCommand(command); |
391 | 286 // merge は必須だが、EditorのCommand実装をテストするには邪魔なので、off に出来るようにする。 |
287 if (noMergeMode) { | |
410 | 288 checkQuit(); |
396 | 289 endMerge(); |
391 | 290 return; |
291 } | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
292 // START_MERGE を送る |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
293 // 送らないで良い場合もある? |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
294 REPCommand cmd = new REPCommand(REP.SMCMD_START_MERGE,command.sid,REP.SM_EID.id,seq(),0,""); |
450 | 295 sendToEditor(cmd); |
387 | 296 merging = true; |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
297 // Session Manager 側で、このeditorへの他のeditorからの |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
298 // 入力を止めて、merge にそなえる。merge は、eidtor 側から |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
299 // ACKが来てから始まる。 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
300 } |
457 | 301 |
463 | 302 /** |
303 * sentList と ack を見比べて、正しい順序で来たかどうかを調べる。途中参加したEditorの場合は、Ackは | |
304 * 無視して良い。 | |
305 * @param command | |
306 * @return | |
307 */ | |
457 | 308 private boolean checkAck(REPCommand command) { |
466 | 309 REPCommand prev = null; |
310 try { | |
311 if(isMerging() || ackList.size()==0) throw new Exception(); | |
312 prev=ackList.remove(0); | |
313 if (prev==null || prev.seq != command.seq || prev.eid!=command.eid) throw new Exception(); | |
314 } catch (Exception n) { | |
457 | 315 // should be more robust to allow communication failure |
316 String err = "Editor eid="+eid+" checkReturnedCommand() : command = " + command + " prev="+ | |
466 | 317 (prev==null?"null":prev)+" ackList="; |
464 | 318 err += ackList; |
457 | 319 ServerMainLoop.logger.writeLog(err); |
320 assert(false); | |
321 } | |
322 return true; | |
323 } | |
324 | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
325 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
326 @Override |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
327 public void setQuit2(REPCommand cmd) { |
462 | 328 quit_2 = cmd; |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
329 checkQuit(); |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
330 // do not send quit2 until we received all pending |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
331 // command |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
332 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
333 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
334 @Override |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
335 public void setEID(int eid) { |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
336 this.eid = eid; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
337 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
338 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
339 public String toString(){ |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
340 return ("Editor eid="+eid+" sid="+sid+" " + host + ":" + file); |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
341 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
342 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
343 void checkEndMerge() { |
387 | 344 if (merging) { |
468 | 345 if (isMerging()) return; |
396 | 346 endMerge(); |
387 | 347 merging = false; |
348 } | |
462 | 349 if (quit_2!=null) checkQuit(); |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
350 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
351 |
442 | 352 |
396 | 353 private void endMerge() { |
468 | 354 sortedEditCmds = null; |
355 LinkedList<REPCommand>u = new LinkedList<REPCommand>(); | |
356 boolean flag=true; | |
357 for(REPCommand command:unMergedCmds) { | |
358 if (command.cmd==REP.REPCMD_MERGE_MARK) { | |
359 flag = false; | |
360 } | |
361 if (flag) u.addLast(command); | |
362 } | |
363 unMergedCmds = u; | |
364 | |
396 | 365 REPCommand mergeEnd = new REPCommand(REP.SMCMD_END_MERGE,sid,eid,seq(),0,""); |
450 | 366 sendToEditor(mergeEnd); |
452 | 367 checkAck(preMergeCommand); |
397 | 368 if (preMergeCommand.eid==eid) { |
463 | 369 if (!slowMerge) { |
462 | 370 sendAck(preMergeCommand); |
463 | 371 } |
397 | 372 } else { |
468 | 373 ServerMainLoop.logger.writeLog("Editor"+eid+": send preMergeCommand "+preMergeCommand); |
397 | 374 next.send(preMergeCommand); |
375 } | |
465 | 376 // sentList.clear(); |
397 | 377 preMergeCommand = null; |
396 | 378 } |
379 | |
462 | 380 private void sendAck(REPCommand command) { |
381 REPCommand keep = new REPCommand(command); | |
382 // First Phase End, send ACK | |
383 switch(keep.cmd) { | |
384 case REPCMD_INSERT: keep.cmd = REP.REPCMD_INSERT_ACK;break; | |
385 case REPCMD_DELETE: keep.cmd = REP.REPCMD_DELETE_ACK;break; | |
386 default: assert(false); | |
387 } | |
464 | 388 ackList.addLast(keep); |
463 | 389 ServerMainLoop.logger.writeLog("Editor"+eid+": sendAck sentList = "+sentList); |
464 | 390 assert(ackList.size()<limit); |
463 | 391 keep.string = ""; |
462 | 392 next.send(keep); |
393 } | |
394 | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
395 private boolean checkQuit() { |
468 | 396 if (quit_2!=null && ackList.size()==0 && sentList.size()==0&&!isMerging() && waitingCommandInMerge.size()==0) { |
462 | 397 sendToEditor(quit_2); |
398 quit_2 = null; | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
399 return true; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
400 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
401 return false; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
402 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
403 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
404 @Override |
387 | 405 public boolean manage(REPCommand command) { |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
406 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
407 |
387 | 408 switch(command.cmd){ |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
409 // Editor Command |
396 | 410 |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
411 case REPCMD_DELETE: |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
412 case REPCMD_INSERT: |
400 | 413 case REPCMD_DELETE_USER: |
414 case REPCMD_INSERT_USER: | |
396 | 415 case REPCMD_DELETE_ACK: |
416 case REPCMD_INSERT_ACK: | |
465 | 417 case REPCMD_MERGE_MARK: |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
418 { |
387 | 419 translate(command); |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
420 break; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
421 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
422 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
423 case SMCMD_START_MERGE_ACK: |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
424 { |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
425 // マージの処理と次のエディタへコマンドを送信する処理 |
468 | 426 mergeAck(); |
427 if (!merge(this, preMergeCommand)) { | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
428 // nothing to do, send END_MERGE |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
429 checkEndMerge(); |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
430 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
431 break; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
432 } |
386 | 433 |
434 case SMCMD_SYNC: | |
435 if (isMaster()) | |
450 | 436 sendToEditor(command); |
386 | 437 else |
387 | 438 next.send(command); |
386 | 439 |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
440 case SMCMD_QUIT: |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
441 { |
387 | 442 next.send(command); |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
443 break; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
444 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
445 case SMCMD_QUIT_2: |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
446 { |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
447 // QUIT_2 is returned. |
387 | 448 if (command.eid!=eid) { |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
449 // stop this editor unless this is the start, starter will stopped |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
450 // by QUIT_2_ACK |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
451 manager.remove(this); |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
452 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
453 // don't send quit_2 directly to the editor until all pending |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
454 // merge is processed. |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
455 // this does not work in distributed case. |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
456 if (next.isDirect()) |
387 | 457 next.setQuit2(command); |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
458 else |
387 | 459 next.send(command); |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
460 break; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
461 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
462 case SMCMD_QUIT_2_ACK: |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
463 { |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
464 manager.remove(this); |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
465 break; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
466 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
467 default: |
396 | 468 assert false; |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
469 return false; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
470 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
471 return true; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
472 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
473 |
442 | 474 |
450 | 475 private boolean isMergeCommand(REPCommand command) { |
476 switch(command.cmd) { | |
477 case REPCMD_INSERT: case REPCMD_DELETE: | |
462 | 478 return slowMerge?false:command.eid==eid; |
450 | 479 case REPCMD_INSERT_ACK: case REPCMD_DELETE_ACK: |
462 | 480 return slowMerge?true:command.eid!=eid; |
450 | 481 } |
482 return false; | |
483 } | |
484 | |
485 public void sendToEditor(REPCommand command) { | |
460 | 486 writeQueue.add(command); |
450 | 487 } |
488 | |
442 | 489 @Override |
387 | 490 public void handle(REPCommand command, REPSelectionKey<REPCommand> key) throws IOException { |
491 if (command.cmd==REP.SMCMD_JOIN||command.cmd==REP.SMCMD_PUT) { | |
404 | 492 // assert false; |
427 | 493 // 一つのエディタ上に複数のセッションが作られた場合。 |
387 | 494 // 若干問題があるらしい |
495 next = new Forwarder(manager,next.channel); | |
496 REPNode first = new FirstConnector(manager,channel); | |
497 first.handle(command, key); | |
498 key.attach(new Dispatcher(manager,channel)); | |
499 return; | |
500 } | |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
501 if (manager.sessionManage(this, command)) return; |
471 | 502 // ServerMainLoop.logger.writeLog("Editor"+eid+": handle command="+command); |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
503 manage(command); |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
504 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
505 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
506 @Override |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
507 public void cancel(REPSocketChannel<REPCommand> socketChannel) { |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
508 manager.remove(socketChannel); |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
509 } |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
510 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
511 public boolean isMaster() { |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
512 return mode==REP.SMCMD_PUT; |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
513 } |
386 | 514 |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
515 |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
516 /* Handle special case first, usually these cases |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
517 * are handled in the next Editor in a session manager, but |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
518 * it is forwarded here. |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
519 */ |
385 | 520 public void forwardedCommandManage(REPCommand command) { |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
521 if (command.cmd==REP.SMCMD_QUIT_2) { |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
522 // we have to wait next editor's finishing before sending this. |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
523 // this is odd, but the editor itself does not know it's merging |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
524 // state. Only this session manager knows it. |
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
525 setQuit2(command); |
401 | 526 return; |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
527 } |
385 | 528 send(command); |
529 } | |
530 | |
468 | 531 /** |
532 * New command from an editor | |
533 * The command is sent to the next editor | |
534 * @param cmd | |
535 * @return translated command. | |
536 */ | |
537 public REPCommand transSendCmd(REPCommand cmd){ | |
538 assert(cmd.eid==eid); | |
539 unMergedCmds.addLast(cmd); | |
540 | |
541 //マージ中にユーザから割り込みがあった場合 | |
542 if(isMerging()){ | |
543 mergeAgain = true; | |
544 } | |
545 | |
546 return cmd; | |
547 } | |
548 | |
549 /** | |
550 * My command is returned from the session ring, and START_MERGE_ACK | |
551 * is returned. At this | |
552 * stage my writeQueue is empty, our editor is waiting for me. | |
553 * Start merge process. | |
554 * @param cmd | |
555 */ | |
556 public boolean merge(Editor editor, REPCommand prev){ | |
557 logger.writeLog("beforeMerge"+eid+":"+unMergedCmds); | |
558 LinkedList<REPCommand> output = new LinkedList<REPCommand>(); | |
559 LinkedList<REPCommand> newSentList = new LinkedList<REPCommand>(); | |
560 // merge queue上にあるコマンドを全部undoコマンドするのと同時に | |
561 // sort したコマンド列を生成する | |
562 for( REPCommand cmd0 : unMergedCmds) { | |
563 output.addLast( createUndo(cmd0) ); | |
564 } | |
565 | |
566 sortedEditCmds = new TreeSet<REPCommand>(new REPCommandComparator(1)); | |
567 logger.writeLog("sentList"+eid+":"+editor.getSentList()); | |
568 for( REPCommand cmd0 : editor.getSentList()) { | |
569 if (cmd0.cmd==REP.REPCMD_INSERT || cmd0.cmd==REP.REPCMD_DELETE) { | |
570 sortedEditCmds.add(cmd0); | |
571 } | |
572 } | |
469 | 573 output.addAll(sortedEditCmds); |
574 output.addLast(new REPCommand(REP.REPCMD_MERGE_MARK,0, editor.getSID(), REP.MERGE_EID.id, editor.seq(), "")); | |
468 | 575 logger.writeLog("sortedMerge"+eid+":"+sortedEditCmds); |
576 // unMerged command のdeleteのundo string は、この時点で使えない。 | |
577 // Editor 側から送り返して来たものを使う必要がある。 | |
578 unMergedCmds.clear(); | |
579 logger.writeLog("outputMerge"+eid+":"+output); | |
580 editor.setSentList(newSentList); | |
581 return optimizedSend(editor,output); | |
582 } | |
583 | |
584 /** | |
585 * Sent optimized merged command list | |
586 * @param editor | |
587 * @param output | |
588 * @return if any sent commands output | |
589 */ | |
590 public boolean optimizedSend(REPNode editor, LinkedList<REPCommand> output) { | |
591 /* | |
592 * Optimized send の場合は、unMergedCommand のつじつまを合わせる必要がある。 | |
593 */ | |
594 sentMergedList.clear(); | |
595 List<REPCommand> output1 = optimizer.optimize(output); | |
596 if (output1.size()==0) { | |
597 merge_mode = false; | |
598 return false; | |
599 } | |
600 for(REPCommand c:output1) { | |
601 REPCommand m = new REPCommand(c); | |
602 m.setEID(REP.MERGE_EID.id); | |
603 m.setSEQID(editor.seq()); | |
604 sentMergedList.addLast(m); | |
605 editor.sendToEditor(m); | |
606 } | |
607 logger.writeLog("OptimizedOutputMerge"+eid+":"+sentMergedList); | |
608 merge_mode = true; | |
609 return true; | |
610 } | |
611 | |
612 private REPCommand createUndo(REPCommand cmd){ | |
613 REPCommand retCmd = new REPCommand(cmd); | |
614 if (cmd.cmd==REP.REPCMD_INSERT) { | |
615 retCmd.cmd=REP.REPCMD_DELETE; | |
616 retCmd.string=""; | |
617 } | |
618 else if (cmd.cmd==REP.REPCMD_DELETE) retCmd.cmd=REP.REPCMD_INSERT; | |
619 return retCmd; | |
620 } | |
621 | |
622 class REPCommandComparator implements Comparator<REPCommand>{ | |
623 int base; | |
624 REPCommandComparator(int base) { | |
625 this.base = base; | |
626 } | |
627 public int compare(REPCommand o1, REPCommand o2) { | |
628 int eid1 = o1.eid-base; if (eid1<0) eid1 += Integer.MAX_VALUE; | |
629 int eid2 = o2.eid-base; if (eid2<0) eid2 += Integer.MAX_VALUE; | |
630 if ( eid1<eid2 ) return -1; | |
631 if ( eid1>eid2 ) return 1; | |
632 if ( o1.seq<o2.seq ) return -1; | |
633 if ( o1.seq>o2.seq ) return 1; | |
634 // assert(false); // this can happen in MergedAgain case | |
635 return 0; | |
636 } | |
637 } | |
638 | |
639 /** | |
640 * Translate Command that was received from SeMa. | |
641 * @param cmd the command to be translated. | |
642 * @return translated command. | |
643 */ | |
644 public void transReceiveCmd(REPNode nextEditor,REPCommand cmd){ | |
645 assert (cmd.eid != eid); | |
471 | 646 unMergedCmds.addFirst(new REPCommand(cmd)); |
468 | 647 } |
648 | |
649 public void setEid(int _eid){ | |
650 eid = _eid; | |
651 } | |
652 | |
653 public boolean checkMergeConflict(REPCommand command) { | |
471 | 654 unMergedCmds.addFirst(new REPCommand(command)); |
468 | 655 |
656 REPCommand prev = sentMergedList.getFirst(); | |
657 if (prev.seq==command.seq) { | |
658 // logger.writeLog("Input eid="+eid+"SentMergedList = "+sentMergedList); | |
659 sentMergedList.removeFirst(); | |
660 } | |
661 // previous merge command may be returned | |
662 | |
663 if(sentMergedList.size()==0 && !mergeAgain) { | |
664 merge_mode=false; | |
665 } | |
666 return mergeAgain; | |
667 } | |
668 | |
669 public void getMergeAgain(Editor editor) { | |
670 if (sentMergedList.size()>0) return; // wait for previous merge completion | |
671 | |
672 LinkedList<REPCommand> returnCommand = new LinkedList<REPCommand>(); | |
673 for(REPCommand command : unMergedCmds) { | |
674 if (command.cmd==REP.REPCMD_INSERT||command.cmd==REP.REPCMD_DELETE) | |
675 returnCommand.add(createUndo(command)); | |
676 } | |
677 returnCommand.addAll(sortedEditCmds); | |
678 returnCommand.addLast(new REPCommand(REP.REPCMD_MERGE_MARK,0, editor.getSID(), REP.MERGE_EID.id, editor.seq(), "")); | |
679 returnCommand.addAll(editor.getSentList()); | |
680 unMergedCmds.clear(); | |
681 logger.writeLog("MergeAgain "+eid+" ret="+returnCommand.size()); | |
682 mergeAgain = false; | |
683 optimizedSend(editor, returnCommand); | |
684 } | |
685 // | |
686 // public boolean isFinished() { | |
687 // if(unMergedCmds.size() > 0) return false; | |
688 // if(sentMergedList.size() > 0) return false; | |
689 // return true; | |
690 // } | |
691 | |
692 public boolean isMerging() { | |
693 return merge_mode; | |
694 } | |
695 | |
696 /** | |
697 * receive SMCMD_START_MERGE_ACK | |
698 */ | |
699 public void mergeAck() { | |
700 logger.writeLog("Editor"+eid+": START MERGE "+ | |
701 ((unMergedCmds.size()>0)?" and top of unMergedCmds = "+ unMergedCmds.getLast():"")); | |
702 merge_mode = true; | |
703 } | |
704 | |
399 | 705 |
382
4b87f89b3afd
REP Session Manager (Java version)
one@firefly.cr.ie.u-ryukyu.ac.jp
parents:
diff
changeset
|
706 } |