# HG changeset patch # User one # Date 1228805068 -32400 # Node ID 784a4d67e6a51a64d47719e97c70ef9816764880 # Parent 1cdbdeedc5b7e0f93285d5ed97aa2d5c01936762 (no commit message) diff -r 1cdbdeedc5b7 -r 784a4d67e6a5 .project --- a/.project Wed Nov 26 11:17:29 2008 +0900 +++ b/.project Tue Dec 09 15:44:28 2008 +0900 @@ -1,6 +1,6 @@ - SessionManager + REPSessionManagerNew diff -r 1cdbdeedc5b7 -r 784a4d67e6a5 test/AutoSelectManager.java --- a/test/AutoSelectManager.java Wed Nov 26 11:17:29 2008 +0900 +++ b/test/AutoSelectManager.java Tue Dec 09 15:44:28 2008 +0900 @@ -21,7 +21,7 @@ port = Integer.parseInt(args[0]); port_s = Integer.parseInt(args[1]); } - Editor.noMergeMode = false; + Editor.noMergeMode = true; SessionManager sm = new SessionManager(); sm.setReceivePort(port); sm.setParentPort(port_s); diff -r 1cdbdeedc5b7 -r 784a4d67e6a5 test/editortest/LogTarget.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/editortest/LogTarget.java Tue Dec 09 15:44:28 2008 +0900 @@ -0,0 +1,7 @@ +package test.editortest; + +public interface LogTarget { + + void printLog(Object obj); + +} diff -r 1cdbdeedc5b7 -r 784a4d67e6a5 test/editortest/Logger.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/editortest/Logger.java Tue Dec 09 15:44:28 2008 +0900 @@ -0,0 +1,1 @@ +package test.editortest; public class Logger { public static void print(Object obj){ StackTraceElement e = new Exception().getStackTrace()[1]; System.out.println(e.getClassName() + "." + e.getMethodName() + "() : " + obj.toString()); } public static void print() { StackTraceElement e = new Exception().getStackTrace()[1]; System.out.println(e.getClassName() + "." + e.getMethodName() + "()"); } public static void printT(Object obj){ System.out.println(Thread.currentThread().toString() + " : " + obj); } public static void print(LogTarget target, Object obj){ target.printLog(obj); } } \ No newline at end of file diff -r 1cdbdeedc5b7 -r 784a4d67e6a5 test/editortest/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/editortest/Main.java Tue Dec 09 15:44:28 2008 +0900 @@ -0,0 +1,11 @@ +package test.editortest; + +public class Main { + + public static void main(String[] args){ + REPSimpleEditor frame = new REPSimpleEditor(); + frame.setDefaultCloseOperation(REPSimpleEditor.EXIT_ON_CLOSE); + frame.setVisible(true); + } + +} diff -r 1cdbdeedc5b7 -r 784a4d67e6a5 test/editortest/REPSimpleEditor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/editortest/REPSimpleEditor.java Tue Dec 09 15:44:28 2008 +0900 @@ -0,0 +1,175 @@ +package test.editortest; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTextArea; +import javax.swing.JToolBar; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; + +import rep.REP; +import rep.REPCommand; +import test.Text; + +public class REPSimpleEditor extends JFrame implements DocumentListener, ActionListener, LogTarget{ + + /** + * + */ + private static final long serialVersionUID = 1L; + private String BR = System.getProperty("line.separator"); + private Document document; + private JTextArea textArea; + private int seq; + private int eid; + private int sid; + private JButton putButton; + private JButton joinButton; + private REPText repText; + private TestEditor2 testEditor; + private JSplitPane splitPane; + private JScrollPane scrollPane1; + private JTextArea console; + private JScrollPane scrollPane2; + + public REPSimpleEditor(String title){ + super(title); + setSize(new Dimension(640, 480)); + this.setLayout(new BorderLayout()); + + setToolBar(); + setEditor(); + setConsole(); + setSplitPane(); + } + + public REPSimpleEditor(){ + this("Sample Editor"); + } + + private void setToolBar() { + JToolBar toolbar = new JToolBar(); + putButton = new JButton("put"); + joinButton = new JButton("join"); + + putButton.addActionListener(this); + joinButton.addActionListener(this); + + toolbar.add(putButton); + toolbar.add(joinButton); + add(toolbar, BorderLayout.NORTH); + } + + private void setEditor(){ + textArea = new JTextArea(); + textArea.setFont(new Font("Monaco", Font.PLAIN, textArea.getFont().getSize())); + document = textArea.getDocument(); + document.addDocumentListener(this); + + scrollPane1 = new JScrollPane(textArea); + + repText = new REPTextImpl(textArea); + } + + private void setConsole(){ + console = new JTextArea(); + console.setFont(new Font("Monaco", Font.PLAIN, console.getFont().getSize())); + console.setEditable(false); + scrollPane2 = new JScrollPane(console); + } + + private void setSplitPane(){ + splitPane = new JSplitPane(); + splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT); + splitPane.add(scrollPane1, JSplitPane.TOP); + splitPane.add(scrollPane2, JSplitPane.BOTTOM); + splitPane.setDividerLocation(300); + add(splitPane, BorderLayout.CENTER); + } + + private REPCommand createREPCommand(int offset, int length) { + REPCommand command = null; + try { + int lineno = textArea.getLineOfOffset(offset); + int lineStart = textArea.getLineStartOffset(lineno); + int lineEnd = textArea.getLineEndOffset(lineno); + String text = textArea.getText(lineStart, lineEnd-lineStart); + command = new REPCommand(REP.REPCMD_INSERT_USER, sid, eid, seq++, lineno, text); + } catch (BadLocationException e1) { + e1.printStackTrace(); + } + Logger.printT(command); + return command; + } + + public void changedUpdate(DocumentEvent e) { + Logger.print(e); + } + + public void insertUpdate(DocumentEvent e) { + int offset = e.getOffset(); + int length = e.getLength(); + createREPCommand(offset, length); + } + + public void removeUpdate(DocumentEvent e) { + Logger.print(e); + } + + public void actionPerformed(ActionEvent e) { + if(e.getSource() == putButton){ + repPut(); + }else if(e.getSource() == joinButton){ + repJoin(); + } + } + + private void repJoin() { + testEditor = new TestEditor2("TestEditor", "localhost", 8766, false); + testEditor.setREPText(repText); + testEditor.setLogTarget(this); + testEditor.start(); + } + + private void repPut() { + setMasterText(); + testEditor = new TestEditor2("TestEditor", "localhost", 8766, true); + testEditor.setText(repText.list()); + testEditor.setREPText(repText); + testEditor.setLogTarget(this); + testEditor.start(); + } + + private void setMasterText() { + textArea.append("AAAAA" + BR); + textArea.append("BBBBB" + BR); + textArea.append("CCCCC" + BR); + textArea.append("DDDDD" + BR); + } + + public void printLog(Object obj) { + String log = obj.toString(); + console.append(log + BR); + } + + public REPText getREPText() { + return repText; + } + + public void setText(Text text) { + for(String str : text){ + textArea.append(str + BR); + } + } + +} diff -r 1cdbdeedc5b7 -r 784a4d67e6a5 test/editortest/REPText.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/editortest/REPText.java Tue Dec 09 15:44:28 2008 +0900 @@ -0,0 +1,13 @@ +package test.editortest; + +import java.util.List; + +public interface REPText { + + public void insert(int lineno, String text); + + public String delete(int lineno, String text); + + public List list(); + +} diff -r 1cdbdeedc5b7 -r 784a4d67e6a5 test/editortest/REPTextImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/editortest/REPTextImpl.java Tue Dec 09 15:44:28 2008 +0900 @@ -0,0 +1,89 @@ +package test.editortest; + +import java.util.LinkedList; +import java.util.List; + +import javax.swing.JTextArea; +import javax.swing.text.BadLocationException; + +import rep.REP; +import rep.REPCommand; + +public class REPTextImpl implements REPText { + + private JTextArea textArea; + private String BR = System.getProperty("line.separator"); + + public REPTextImpl(JTextArea area){ + this.textArea = area; + } + + public String delete(int lineno, String text) { + increaseLine(lineno); + + String deleted = ""; + try { + int start = textArea.getLineStartOffset(lineno); + int end = textArea.getLineEndOffset(lineno); + int length = end - start; + deleted = textArea.getText(start, length); + textArea.replaceRange("", start, end); + } catch (BadLocationException e) { + e.printStackTrace(); + } + return deleted; + } + + public void insert(int lineno, String text) { + increaseLine(lineno); + + String text2 = text + BR; + try { + int offset = textArea.getLineStartOffset(lineno); + textArea.insert(text2, offset); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + + private void increaseLine(int lineno) { + while(true){ + int lines = textArea.getLineCount(); + Logger.print(lineno + " : " + lines); + if(lineno >= lines){ + textArea.append(BR); + }else{ + break; + } + } + } + + public void edit(REPCommand command){ + int lineno = command.lineno; + String text = command.string; + if(command.cmd == REP.REPCMD_INSERT){ + insert(lineno, text); + }else if(command.cmd == REP.REPCMD_DELETE){ + delete(lineno, text); + }else{ + Logger.print(command); + } + } + + public List list() { + int count = textArea.getLineCount(); + LinkedList list = new LinkedList(); + for(int i = 0; i < count; i++){ + try { + int start = textArea.getLineStartOffset(i); + int end = textArea.getLineEndOffset(i); + String text = textArea.getText(start, end - start); + list.add(text); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + return list; + } + +} diff -r 1cdbdeedc5b7 -r 784a4d67e6a5 test/editortest/TestEditor2.java --- /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 cmds; + private int eid = 0; + private int sid = 0; + REPSocketChannel 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 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); + LinkedListcmdList = new LinkedList(); + 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 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.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 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 key : selector.selectedKeys1()) { + REPSocketChannel 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 cmds) { + this.cmds = cmds; + timeout=1; + if(selector!=null) selector.wakeup(); + } + + public void setText(List list) { + text = new Text(list); + } + + public void setLogTarget(LogTarget target) { + logTarget = target; + } + + public void setREPText(REPText repText) { + this.repText = repText; + } +} diff -r 1cdbdeedc5b7 -r 784a4d67e6a5 test/editortest/TestInterManagerSession2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/editortest/TestInterManagerSession2.java Tue Dec 09 15:44:28 2008 +0900 @@ -0,0 +1,162 @@ +package test.editortest; + + +import java.util.LinkedList; + +import rep.REP; +import rep.REPCommand; +import rep.SessionManager; +import rep.channel.REPServerSocketChannel; +import rep.gui.SessionManagerEvent; + +public class TestInterManagerSession2 extends TestSessionManager2 { + + /* + * All test is performed in localhost, so all session manager + * should have differenct port number each other. + * Test Pattern List + * Connect port for each editor + * Master/client flag for each editor + * Editor or slave session manager must be started by + * master session managers using syncExec. + */ + + public int slavePort[] = {masterPort,masterPort,masterPort}; + public int editorPort[] = {masterPort,masterPort+1,masterPort+2}; + public boolean editorMaster[] = {true,false,false,false}; + private LinkedList editorStartCmds; + + private SessionManagerEvent ev2[] = { + new SessionManagerEvent() { + public void exec(SessionManager manager) { + int i = sessionManagers.length; + for(SessionManager slave:slaveSessionManagers) { + if (slave.getParentPort()==masterPort) { + logger.writeLog("Start slave "+slave); + i = startSessionManager(slave,i,masterPort + i); + } + } + } + }, + new SessionManagerEvent() { + public void exec(SessionManager manager) { + manager.connectSessionManager(host); + } + }, + new SessionManagerEvent() { + public void exec(SessionManager manager) { + // try to make a loop + if (false) { + sessionManagers[0].connectSessionManager(host, + manager.getPort()); + } + manager.connectSessionManager(host); + manager.execAfterConnect( + new SessionManagerEvent() { + public void exec(SessionManager manager) { + for(SessionManager m:sessionManagers) { + startEditor(m); + } + for(SessionManager m:slaveSessionManagers) { + startEditor(m); + } + editors[0].setCommand(editorStartCmds); + } + } + ); + } + + } + }; + //private int inscnt=2; + + private void startEditor(SessionManager m) { + for(TestEditor2 editor:editors) { + if(editor.getPort()==m.getPort()) { + logger.writeLog("Start client "+editor); + editor.start(); + } + } + } + + /* + * Create all editors, master session managers and slave session + * managers with specified port. All instances are not started yet. + */ + + public TestInterManagerSession2(int sm, int ss, int e) { + super(sm,ss,e); + + sessionManagers = new SessionManager[sm]; + slaveSessionManagers = new SessionManager[ss]; + editors = new TestEditor2[e]; + for(int i=0;icmds = new LinkedList(); + cmds.add(new REPCommand(REP.SMCMD_JOIN,0,0,0,0,"")); + //if (inscnt-->0) cmds.add(new REPCommand(REP.REPCMD_INSERT,0,0,0,0,"m0")); + editors[i] = new TestEditor2("Editor"+i,host,port,master); + //editors[i].setCommand(cmds); + } + setupEditor0(); + } + + private void setupEditor0() { + /* + * do not startup Editor0 until SessionManagers are ready. + * Define pending command and set null command for now. + */ + LinkedListcmds = new LinkedList(); + //cmds.add(new REPCommand(REP.SMCMD_JOIN,0,0,0,0,"Editor0-file")); + cmds.add(new REPCommand(REP.SMCMD_PUT,0,0,0,0,"Editor0-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,"")); + editorStartCmds = cmds; + LinkedListnullcmds = new LinkedList(); + editors[0].setCommand(nullcmds); + } + + @Override + public void setSMEvent(SessionManager s,int i) { + if (i