Mercurial > hg > RemoteEditor > REPSessionManager
view test/sematest/TestEditor.java @ 398:7de83b6a34e7
not yet fixed....
author | one |
---|---|
date | Sun, 23 Nov 2008 18:38:52 +0900 |
parents | dc616339b00a |
children | 19705f4b8015 |
line wrap: on
line source
package test.sematest; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SelectionKey; import java.util.LinkedList; import rep.REP; import rep.REPCommand; import rep.REPCommandPacker; import rep.channel.REPLogger; import rep.channel.REPSelectionKey; import rep.channel.REPSelector; import rep.channel.REPSocketChannel; import test.Text; /** * @author kono * Basic Temote Editor client implementation * should support multi-session * currently multi-session requires new channel, that is * only one session for this editor. */ public class TestEditor 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=false; public TestEditor(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,0,0,0,0,"m0")); cmds.add(new REPCommand(REP.REPCMD_DELETE,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,0,0,0,0,"c0")); //cmds.add(new REPCommand(REP.REPCMD_DELETE,0,0,0,0,"c0")); } } public TestEditor(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()) { syncCounter=0; } else { int i=syncCounter-1; REPCommand del = new REPCommand(REP.REPCMD_DELETE,sid,eid,0,i, text.get(i)); REPCommand ins = new REPCommand(REP.REPCMD_INSERT,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: text.insert(cmd.lineno, cmd.string); sendCommand(cmd); break; case REPCMD_DELETE: String del = text.delete(cmd.lineno); 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(); 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) { sendCommand(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); channel.write(cmd); } private void handle(REPCommand cmd) { if (cmd==null) return; ns.writeLog(name +": read "+cmd); switch(cmd.cmd) { case REPCMD_INSERT : text.insert(cmd.lineno, cmd.string); forwardCommand(cmd); break; case REPCMD_DELETE : String del=""; if(cmd.lineno>text.size()) { del = text.delete(cmd.lineno); } 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) forwardCommand(cmd); else sendCommand(new REPCommand(REP.SMCMD_QUIT_2, sid, eid, seq, 0, "")); // 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(); } }