Mercurial > hg > RemoteEditor > REPSessionManager
diff test/editortest/TestEditor2.java @ 414:784a4d67e6a5
(no commit message)
author | one |
---|---|
date | Tue, 09 Dec 2008 15:44:28 +0900 |
parents | |
children | 648c676bf9df |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/editortest/TestEditor2.java Tue Dec 09 15:44:28 2008 +0900 @@ -0,0 +1,359 @@ +package test.editortest; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.SelectionKey; +import java.util.LinkedList; +import java.util.List; + +import rep.REP; +import rep.REPCommand; +import rep.REPCommandPacker; +import rep.SessionManager; +import rep.channel.REPLogger; +import rep.channel.REPSelectionKey; +import rep.channel.REPSelector; +import rep.channel.REPSocketChannel; +import test.Text; + + +/** + * @author kono + * Basic Remote Editor client implementation + * should support multi-session + * currently multi-session requires new channel, that is + * only one session for this editor. + */ +public class TestEditor2 extends Thread{ + private InetSocketAddress semaIP; + private REPLogger ns; + private int seq = 0; + public Text text; + public LinkedList<REPCommand> cmds; + private int eid = 0; + private int sid = 0; + REPSocketChannel<REPCommand> channel; + REPCommand nop = new REPCommand(REP.REPCMD_NOP, 0, 0, 0, 0, ""); + boolean running = true; + long timeout = 1; + private String name; + private boolean inputLock=false; + public boolean detached=false; + public boolean master=false; + REPCommand quit=null; + private int syncCounter=0; + private boolean hasInputLock=false; + private int port; + private REPSelector<REPCommand> selector; + private boolean syncEnable=true; + private LogTarget logTarget; + private REPText repText; + private REPSimpleEditor simpleEditor; + + + public TestEditor2(String name, String _host,int _port, boolean master){ + super(name); + LinkedList<REPCommand>cmdList = new LinkedList<REPCommand>(); + String[] txts = { + "aaa", "bbb", // "ccc", "ddd", "eee", + }; + port = _port; + semaIP = new InetSocketAddress(_host, _port); + ns = REPLogger.singleton(); + this.name = name; + cmds = cmdList; + if (master) { + this.master=true; + text = new Text(txts); + cmds.add(new REPCommand(REP.SMCMD_PUT,0,0,0,0,name+"-file")); + cmds.add(new REPCommand(REP.REPCMD_INSERT_USER,0,0,0,0,"m0")); + cmds.add(new REPCommand(REP.REPCMD_DELETE_USER,0,0,0,0,"m0")); + cmds.add(new REPCommand(REP.SMCMD_QUIT,0,0,0,0,"")); + } else { + text = new Text(new String[0]); + cmds.add(new REPCommand(REP.SMCMD_JOIN,0,0,0,0,name)); + cmds.add(new REPCommand(REP.REPCMD_INSERT_USER,0,0,0,0,"c0")); + cmds.add(new REPCommand(REP.REPCMD_DELETE_USER,0,0,0,0,"c0")); + } + + simpleEditor = new REPSimpleEditor(); + simpleEditor.setDefaultCloseOperation(REPSimpleEditor.EXIT_ON_CLOSE); + simpleEditor.setVisible(true); + simpleEditor.setText(text); + repText = simpleEditor.getREPText(); + logTarget = simpleEditor; + } + + public TestEditor2(String name, String _host,int _port, boolean master, + String[] txts,LinkedList<REPCommand> cmdList){ + super(name); + port = _port; + semaIP = new InetSocketAddress(_host, _port); + ns = REPLogger.singleton(); + this.name = name; + cmds = cmdList; + if (master) { + this.master=true; + text = new Text(txts); + } else { + text = new Text(new String[0]); + } + } + + public void run(){ + /* + * Create Socket and connect to the session manager + */ + try { + channel = REPSocketChannel.<REPCommand>create(new REPCommandPacker()); + } catch (IOException e) { return; } + + ns.writeLog("try to connect to SessionManager whose ip is "+semaIP+" "+name, 1); + try { + while (!channel.connect(semaIP)){ + ns.writeLog("SeMa not listen to socket yet, wait", 1); + } + } catch (IOException e) { return; } + ns.writeLog("successes to connect "+name); + /* + * Start editor main loop + * public REPCommand(REP cmd,int sid,int eid, int seq, int lineno, String string) + */ + try { + mainloop(); + } catch (IOException e) { + } + } + + /* + * Editor main loop with input lock + */ + private void mainloop() throws IOException { + + channel.configureBlocking(false); + REPSelector<REPCommand> selector = REPSelector.create(); + channel.register(selector, SelectionKey.OP_READ); + this.selector = selector; + while(running) { + if (inputLock) { + // No user input during merge mode (optional) + if (selector.select(0)>0) { + handle(channel.read()); + } + continue; + } else if (selector.select(timeout)<=0) { + if (syncCounter>0) { + syncText(); // send the master editor buffer to clients. + } + userInput(); + } + // selector(timeout) returns 0, but it may contain readable channel.. + for(REPSelectionKey<REPCommand> key : selector.selectedKeys1()) { + REPSocketChannel<REPCommand> ch = key.channel1(); + handle(ch.read()); + } + } + } + + private void syncText() { + /* + * Send delete/insert one at a time to synchronize + * all clients. SYNC is requested by the session manager. + */ + if (syncCounter>text.size()) { + SessionManager.logger.writeLog("Sync Completed."); + syncCounter=0; + } else { + if (inputLock) return; + int i=syncCounter-1; + REPCommand del = new REPCommand(REP.REPCMD_DELETE_USER,sid,eid,0,i, text.get(i)); + REPCommand ins = new REPCommand(REP.REPCMD_INSERT_USER,sid,eid,0,i, text.get(i)); + sendCommand(del); + sendCommand(ins); + syncCounter++; + } + } + + /* + * Simulate User Input + */ + private void userInput() { + REPCommand cmd = cmds.poll(); + if (cmd!=null) { + switch(cmd.cmd) { + case REPCMD_INSERT_USER: + text.insert(cmd.lineno, cmd.string); + repText.insert(cmd.lineno, cmd.string); + sendCommand(cmd); + break; + case REPCMD_DELETE_USER: + String del = text.delete(cmd.lineno); + del = repText.delete(cmd.lineno, cmd.string); + cmd.setString(del); + sendCommand(cmd); + break; + case SMCMD_QUIT: + /* + * start termination phase 1 by the master editor. + * after this command do not send any user input. + * clients simply disconnect from the session manager. + */ + cmds.clear(); + cmd.eid = -1; + quit = cmd; + break; + case SMCMD_JOIN: + case SMCMD_PUT: + sendCommand(cmd); + /* + * To prevent confusion, stop user input until the ack + */ + inputLock = true; // wait until ACK + break; + default: + assert(false); + } + } else { + if(syncCounter==0) { + // no more command to send, and we don't have syncCounter + timeout = 0; + if (quit!=null) { + if (quit.eid==-1) + sendCommand(quit); + else + forwardCommand(quit); + quit=null; + } + } + } + } + + + private void sendCommand(REPCommand cmd1) { + REPCommand cmd = new REPCommand(cmd1); + cmd.setSEQID(seq++); + cmd.setEID(eid); + cmd.setSID(sid); + ns.writeLog(name +" send "+cmd); + channel.write(cmd); + } + + private void forwardCommand(REPCommand cmd1) { + REPCommand cmd = new REPCommand(cmd1); + ns.writeLog(name +" forward "+cmd); + Logger.print(logTarget, "write : " + cmd); + channel.write(cmd); + } + + private void handle(REPCommand cmd) { + if (cmd==null) return; + ns.writeLog(name +": read "+cmd + " textsize="+text.size()); + Logger.print(logTarget, "read : " + cmd); + switch(cmd.cmd) { + case REPCMD_INSERT : + if (cmd.eid!=eid) { + text.insert(cmd.lineno, cmd.string); + repText.insert(cmd.lineno, cmd.string); + } + forwardCommand(cmd); + break; + case REPCMD_DELETE : + if (cmd.eid!=eid) { + String del=""; + if(cmd.lineno<text.size()) { + del = text.delete(cmd.lineno); + del = repText.delete(cmd.lineno, cmd.string); + } + cmd.setString(del); + } + forwardCommand(cmd); + break; + case REPCMD_NOP : + case REPCMD_INSERT_ACK : + case REPCMD_DELETE_ACK : + forwardCommand(cmd); + break; + case REPCMD_CLOSE : + case REPCMD_CLOSE_2 : + assert(false); + break; + + case SMCMD_JOIN_ACK : + sid = cmd.sid; + eid = cmd.eid; + name += "(eid="+eid+",sid="+sid+")"; + inputLock = false; + break; + case SMCMD_PUT_ACK : + sid = cmd.sid; + eid = cmd.eid; + name += "(eid="+eid+",sid="+sid+")"; + inputLock = false; + break; + case SMCMD_QUIT : + if (cmd.eid!=eid) + quit = cmd; + else // eid=-1 means do not forward but send it. + quit = new REPCommand(REP.SMCMD_QUIT_2, + sid, -1, seq, 0, ""); + timeout=1; + // stop input processing after this command + cmds.clear(); + break; + case SMCMD_START_MERGE : + // lock user input during merge (optional) + inputLock = hasInputLock; + cmd.cmd = REP.SMCMD_START_MERGE_ACK; + sendCommand(cmd); + break; + case SMCMD_END_MERGE : + inputLock = false; + break; + case SMCMD_QUIT_2 : + if (cmd.eid!=eid) { + forwardCommand(cmd); + } else { + cmd.cmd = REP.SMCMD_QUIT_2_ACK; + sendCommand(cmd); + } + running = false; + break; + case SMCMD_SYNC: + // start contents sync with newly joined editor + cmd.cmd = REP.SMCMD_SYNC_ACK; + forwardCommand(cmd); + //if (cmd.eid==eid) { + if (master && syncEnable ) { + syncCounter = 1; + timeout = 1; + } + break; + default: + assert(false); + break; + } + } + + + public int getPort() { + return port; + } + + public synchronized void setCommand(LinkedList<REPCommand> cmds) { + this.cmds = cmds; + timeout=1; + if(selector!=null) selector.wakeup(); + } + + public void setText(List<String> list) { + text = new Text(list); + } + + public void setLogTarget(LogTarget target) { + logTarget = target; + } + + public void setREPText(REPText repText) { + this.repText = repText; + } +}