changeset 414:784a4d67e6a5

(no commit message)
author one
date Tue, 09 Dec 2008 15:44:28 +0900
parents 1cdbdeedc5b7
children 648c676bf9df
files .project test/AutoSelectManager.java test/editortest/LogTarget.java test/editortest/Logger.java test/editortest/Main.java test/editortest/REPSimpleEditor.java test/editortest/REPText.java test/editortest/REPTextImpl.java test/editortest/TestEditor2.java test/editortest/TestInterManagerSession2.java test/editortest/TestSessionManager2.java
diffstat 11 files changed, 946 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/.project	Wed Nov 26 11:17:29 2008 +0900
+++ b/.project	Tue Dec 09 15:44:28 2008 +0900
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>SessionManager</name>
+	<name>REPSessionManagerNew</name>
 	<comment></comment>
 	<projects>
 	</projects>
--- 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);
--- /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);
+
+}
--- /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
--- /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);
+	}
+
+}
--- /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);
+		}
+	}
+
+}
--- /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<String> list();
+
+}
--- /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<String> list() {
+		int count = textArea.getLineCount();
+		LinkedList<String> list = new LinkedList<String>();
+		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;
+	}
+
+}
--- /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;
+	}
+}
--- /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<REPCommand> 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;i<sm;i++) {
+			sessionManagers[i] = new SessionManager(); 	
+		}
+		for(int i=0;i<ss;i++) {
+			int port = slavePort[i%slavePort.length];
+			slaveSessionManagers[i] = new SessionManager(); 
+			slaveSessionManagers[i].setParentPort(port);
+		}
+		for(int i=0;i<e;i++) {
+			int port = editorPort[i%editorPort.length];
+			boolean master = editorMaster[i%editorMaster.length];
+			// TestEditor extends Thread
+			LinkedList<REPCommand>cmds = new LinkedList<REPCommand>();
+			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.
+		 */
+		LinkedList<REPCommand>cmds = new LinkedList<REPCommand>();
+		//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;
+		LinkedList<REPCommand>nullcmds = new LinkedList<REPCommand>();
+		editors[0].setCommand(nullcmds);
+	}
+
+	@Override
+	public void setSMEvent(SessionManager s,int i) {
+		if (i<ev2.length) {
+			s.syncExec(ev2[i]);
+		}
+		return ;
+	}
+
+	@Override
+	protected void startTest() {
+		int i = 0;
+		for(SessionManager master:sessionManagers) {
+			logger.writeLog("Start master "+i+" "+master);
+			i = startSessionManager(master,i, masterPort + i);
+		}
+	}
+	
+	public static void main(String[] args){
+		/*
+		 * set simulation mode
+		 *    isSimulation=true     thread base simulation for PathFinder
+		 *    isSimulation=false    socket based communication mode
+		 */
+		REPServerSocketChannel.isSimulation = false;
+		//Editor.noMergeMode = true;
+		// At least 3 TestEditors are required.
+		TestInterManagerSession2 test = new TestInterManagerSession2(1, 2, 3);
+		logger.setLogLevel(5);
+		test.startTest();
+	}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/editortest/TestSessionManager2.java	Tue Dec 09 15:44:28 2008 +0900
@@ -0,0 +1,127 @@
+package test.editortest;
+
+import java.io.IOException;
+
+import rep.SessionManager;
+import rep.channel.REPLogger;
+import rep.channel.REPServerSocketChannel;
+import rep.gui.SessionManagerEvent;
+import rep.gui.SessionManagerGUI;
+import test.sematest.TestGUI;
+
+
+public class TestSessionManager2 {
+	
+	static public REPLogger logger = REPLogger.singleton();
+	public int masterPort = 8766;
+	public String host = "localhost";
+	public SessionManager sessionManagers[];
+	public SessionManager slaveSessionManagers[];
+	public TestEditor2 editors[];
+
+	/*
+	 * 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 editorPort[] = {masterPort,masterPort,masterPort};
+	public boolean editorMaster[] = {true,false,false,false};
+	private SessionManagerEvent ev1[] = {
+			new SessionManagerEvent() {
+				// executed before first select();
+				public void exec(SessionManager manager) {	
+					for(TestEditor2 editor:editors) {
+						editor.start();
+					}
+					int i = sessionManagers.length;
+					for(SessionManager slave:slaveSessionManagers) {
+						i = startSessionManager(slave,i,masterPort + i);
+					}
+				}
+			}};
+	
+	/*
+	 * Create all editors, master session managers and slave session 
+	 * managers with specified port. All instances are not started yet.
+	 */
+	
+	public TestSessionManager2(int sm, int ss, int e) {
+		sessionManagers = new SessionManager[sm];
+		slaveSessionManagers = new SessionManager[ss];
+		editors = new TestEditor2[e];
+		for(int i=0;i<sm;i++) {
+			sessionManagers[i] = new SessionManager(); 	
+		}
+		for(int i=0;i<ss;i++) {
+			slaveSessionManagers[i] = new SessionManager(); 
+		}
+		for(int i=0;i<e;i++) {
+			int port = editorPort[i%editorPort.length];
+			boolean master = editorMaster[i%editorMaster.length];
+			// TestEditor extends Thread
+			editors[i] = new TestEditor2("Editor"+i,host,port,master);
+		}
+	}
+
+	/*
+	 * start session manager. sm.init(port,guit) is a mainloop, so
+	 * we need Thread here. 
+	 */
+	public int startSessionManager(final SessionManager sm,int i,int port) {
+		final SessionManagerGUI gui = new TestGUI(sm);
+		final int port1 = port;
+		logger.writeLog("TestSessionManager.startSessionManager() : start SessionManager");
+		// syncExec does not wake selector, do this before run().
+		sm.setReceivePort(port1);
+		setSMEvent(sm,i);
+		Runnable start = new Runnable() {
+			public void run() {		
+				try {
+					sm.init(port1,gui);
+				} catch (IOException e) {
+				} catch (InterruptedException e) {
+				}
+			}
+		};
+		new Thread(start).start();
+		return i+1;
+	}
+
+
+	public void setSMEvent(SessionManager s,int i) {
+		if (i<ev1.length) {
+			s.syncExec(ev1[i]);
+		}
+		return ;
+	}
+
+	protected void startTest() {
+		int i = 0;
+		for(SessionManager master:sessionManagers) {
+			i = startSessionManager(master,i, masterPort + i);
+		}
+	}
+
+	public static void main(String[] args){
+		/*
+		 * set simulation mode
+		 *    isSimulation=true     thread base simulation for PathFinder
+		 *    isSimulation=false    socket based communication mode
+		 */
+		REPServerSocketChannel.isSimulation = false;
+		//Editor.noMergeMode = true;
+		// At least 3 TestEditors are required.
+		TestSessionManager2 test = new TestSessionManager2(1, 0, 3);
+		logger.setLogLevel(5);
+		test.startTest();
+	}
+
+
+}