changeset 358:034acadc0cdc

*** empty log message ***
author kono
date Sun, 19 Oct 2008 16:54:37 +0900
parents 6ae9dcb30a12
children fa041bae35f1
files Todo rep/Editor.java rep/EditorList.java rep/FirstConnector.java rep/Forwarder.java rep/RoutingTable.java rep/Session.java rep/SessionList.java rep/SessionManager.java rep/handler/REPHandler.java rep/xml/SessionXMLDecoder.java rep/xml/SessionXMLEncoder.java test/XMLTest.java
diffstat 13 files changed, 410 insertions(+), 323 deletions(-) [+]
line wrap: on
line diff
--- a/Todo	Sat Oct 18 21:28:17 2008 +0900
+++ b/Todo	Sun Oct 19 16:54:37 2008 +0900
@@ -1,3 +1,69 @@
+Sat Oct 18 20:03:10 JST 2008
+
+Note: Routing Table (kono)
+
+Routing Table (Session, Editor)を作るには、上下双方向の通信が必要。
+SessionID を master が作ると、一旦、multi cast した後、もう一度、
+上に上げる必要がある。Select の時には、editor から上に上がるので、
+その時に構築すれば良い。SessionManagerIDと組み合わせれば、eid/sid
+ともに、下から構築出来る。
+
+自分が出したjoin/put/sm_joinに対するackかどうかを見るために、
+SessionManagerID は、どうせ必要。この方法だと、routing table
+もSessionManagerIDに対してだけ構築すれば良い。とは、ならない。
+Session は、複数のSessionManagerにまたがるので。
+
+join_ack が来た時には、そのeditorのrouting tableは完成している、
+あるいは、select が完成させるjoin_ackに追い付くことはない。
+put_ack も同様。
+
+select は、editorへのpathを探しながら、session routing table
+を構築する。もっとも高位のsession managerへのrouting table
+は、これで作成される。ここからjoinしたeditorまでのpathは、
+そのeditor単一のpathだが、routing table に登録される。
+select は、session ringに到達した時点で update を流す。
+update は、木をさかのぼりrouting tableを構築する。
+これで上方向のroutingは確定する。update_ackにより、
+下方向のsesionn routing tableが確定する。
+
+Wed Oct 15 13:33:58 JST 2008
+
+Note: (kono)
+
+Session List を渡すタイミング
+
+    SM_JOIN_ACK (必須...)
+    SM_JOIN では、Session List は0なはず。
+    JOIN,PUT は、multi-cast されるので、その時に登録すれば良い。
+       その時に、Session List を送っても良いが...
+    SELECTは、joinするeditorからしか出ない。Session List は必要ない。
+    SELECT_ACK は、UPDATEが出るので必要ない
+    UPDATE,UPDATE_ACK には、Session List が付く
+    GATHER,GATHER_ACK には、Session List が付く
+
+Session List では、editor,session に対するroutingも作成する、必要
+な情報を含む必要がある。
+    eid, EditorName, FileName, sid, SessionManagerName
+SessionManagerName が入っていれば、editor, session が
+Session Listが来た方向にいるということになる。
+
+SessionManagerName は、network 上でuniqueな必要がある。
+sm_join した時に、そのchannelの名前が大域的に確定する。
+sm_join は複数行なわれないから、名前が変わることはない。
+sm_join された側の名前も、接続されて初めて確定する。
+複数 sm_join されることはあるが、その場合は最初のもの
+を使う。ということは、localにsm_join された後、大域的
+に接続される場合があるってことか。ってことは、やっぱり、
+session manager id を配布するべきだってことね。で、
+SMの名前はあくまでも補助的に使う。
+
+UPDATEの情報によって削除も行なう。delete entry が必要。
+
+Routing Table
+    <eid, channel>
+    <sid, channel>
+null は、local。channel==parent なら、自分の下にはいない。
+
 Tue Oct 14 06:02:37 JST 2008
 
 Todo: (kono)
--- a/rep/Editor.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/rep/Editor.java	Sun Oct 19 16:54:37 2008 +0900
@@ -1,5 +1,6 @@
 package rep;
 
+import java.io.IOException;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -192,7 +193,7 @@
 	
 
 	@Override
-	public void handle(REPSelectionKey<REPCommand> key) throws Exception {
+	public void handle(REPSelectionKey<REPCommand> key) throws IOException {
 		REPSocketChannel<REPCommand> channel = key.channel1();
 		REPCommand command = channel.read();
 		SessionManager.logger.writeLog("REPHandlerImpl.handle() read : command = " + command +" from "+channel);
@@ -204,4 +205,8 @@
 	public void cancel(REPSocketChannel<REPCommand> socketChannel) {
 		manager.remove(socketChannel);
 	}
+
+	public boolean isMaster() {
+		return mode==REP.SMCMD_PUT;
+	}
 }
--- a/rep/EditorList.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/rep/EditorList.java	Sun Oct 19 16:54:37 2008 +0900
@@ -1,30 +1,32 @@
 package rep;
 
-import java.util.LinkedList;
+import java.util.HashMap;
 
-public class EditorList extends LinkedList<Editor> {
+public class EditorList extends HashMap<Integer,Editor> {
 
 	/**
 	 * 
 	 */
 	private static final long serialVersionUID = 1L;
 	private int eid_root=0;
-	private LinkedList<Editor> waiting= new LinkedList<Editor>();
+	private HashMap<Integer,Editor> waiting= new HashMap<Integer,Editor>();
 
 
 	public void addWaitingEditor(Editor fw) {
-		waiting.add(fw);
+		waiting.put(fw.getEID(),fw);
+	}
+	
+	public void add(Editor fw) {
+		put(fw.getEID(),fw);
 	}
 
-
 	public void assignSessionManagerIDtoWaitingSM(int eid) {
-		// 待っていたEditorにEditor IDを登録し,Session Manager List
+		// 待っていたEditorを登録する。
 		// に登録する。この後、EditorのPUT/JOINに従って、ACKを送り、EID
 		// を確定する。
 		Editor waiter;
-		if ((waiter=waiting.poll())!=null) {
-			waiter.setEID(eid);
-			add(waiter);
+		if ((waiter=waiting.remove(eid))!=null) {
+			put(eid,waiter);
 			return;
 		}
 		assert false;
@@ -37,7 +39,12 @@
 
 
 	public boolean waiting(Editor editor) {
-		return waiting.contains(editor);
+		return waiting.containsKey(editor.getEID());
+	}
+
+
+	public boolean hasEid(int eid) {
+		return containsKey(eid);
 	}
 
 
--- a/rep/FirstConnector.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/rep/FirstConnector.java	Sun Oct 19 16:54:37 2008 +0900
@@ -15,7 +15,7 @@
 		manager.remove(socketChannel);
 	}
 
-	public void handle(REPSelectionKey<REPCommand> key) throws Exception {
+	public void handle(REPSelectionKey<REPCommand> key) throws IOException {
 		/*
 		 * 接続要求は、EditorかSlave Editorで、
 		 *    join, put, sm_join
@@ -37,6 +37,7 @@
 			Editor editor = new Editor(manager,-1,channel);
 			editor.setHost(manager.myHost);
 			manager.editorList.add(editor);
+			manager.updateGUI();
 			fw = editor;
 			break;
 		}
--- a/rep/Forwarder.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/rep/Forwarder.java	Sun Oct 19 16:54:37 2008 +0900
@@ -1,8 +1,6 @@
 package rep;
 
 import java.io.IOException;
-
-
 import rep.channel.REPLogger;
 import rep.channel.REPSelectionKey;
 import rep.channel.REPSocketChannel;
@@ -69,7 +67,7 @@
 		manager.remove(socketChannel);
 	}
 
-	public void handle(REPSelectionKey<REPCommand> key) throws Exception {
+	public void handle(REPSelectionKey<REPCommand> key) throws IOException {
 		/*
 		 * SessionManagerから来たコマンドは、Editor関係のコマンドは、
 		 * sessionとeidを判定して、そのeditorにforwardしてやれば良い。
@@ -98,5 +96,9 @@
 		return mode==REP.SMCMD_SM_JOIN||mode==REP.SMCMD_SM_JOIN_ACK;
 	}
 
+	public boolean isDirect() {
+		return isEditor();
+	}
+
 
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rep/RoutingTable.java	Sun Oct 19 16:54:37 2008 +0900
@@ -0,0 +1,46 @@
+package rep;
+
+import java.util.HashMap;
+import java.util.Map.Entry;
+
+public class RoutingTable {
+
+	HashMap<Integer,Forwarder> sessionManagers =new HashMap<Integer,Forwarder>();
+	// we don't need this, but we keep it because it is easy.
+	// editor can be reached using this routing table.
+	HashMap<Integer,Forwarder> sessionTable =new HashMap<Integer,Forwarder>();
+	// session may have multiple forward, if so we have a session
+	// here. So we don't have to keep multiple session, just keep
+	// one.
+
+	public void add(Forwarder forwarder, int smid, int sid) {
+		if (smid>0) sessionManagers.put(smid, forwarder)	;
+		if (sid>0) sessionTable.put(sid, forwarder)	;
+	}
+	
+	public void remove(Forwarder f) {
+		for(Entry<Integer, Forwarder> entry:sessionManagers.entrySet()) {
+			if (entry.getValue()==f) sessionManagers.remove(entry.getKey());
+		}
+		for(Entry<Integer, Forwarder> entry:sessionTable.entrySet()) {
+			if (entry.getValue()==f) sessionTable.remove(entry.getKey());
+		}
+	}
+
+	public void removeSession(int sid) {
+		sessionTable.remove(sid);
+	}
+
+	public void removeManager(int smid) {
+		sessionManagers.remove(smid);
+	}
+
+	public Forwarder toSession(int sid) {
+		return sessionTable.get(sid);
+	}
+	
+	public Forwarder toSessionManager(int eid) {
+		return sessionManagers.get(eid);
+	}
+
+}
--- a/rep/Session.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/rep/Session.java	Sun Oct 19 16:54:37 2008 +0900
@@ -79,6 +79,11 @@
 		return flag;
 	}
 	
+	public boolean deleteForwarder(Forwarder editor) {
+		unconnect(editor);
+		return editorList.remove(editor);
+	}
+	
 	private void unconnect(Forwarder e) {
 		for(EditorPlus e1:editorList) {
 			Forwarder f = (Forwarder)e1;
@@ -125,14 +130,19 @@
 	}
 	
 
-	public boolean deleteForwarder(Forwarder editor) {
-		unconnect(editor);
-		return editorList.remove(editor);
-	}
 	
 	public Forwarder getFirstForwarder() {
 		return firstForwarder;
 	}
+
+
+	public void remove(SessionManager manager) {
+		for(EditorPlus editor : editorList){
+			if(editor.getChannel() !=null) 
+				unconnect((Forwarder)editor);
+			manager.editorList.remove(editor);
+		}	
+	}
 	
 
 }
--- a/rep/SessionList.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/rep/SessionList.java	Sun Oct 19 16:54:37 2008 +0900
@@ -1,21 +1,16 @@
 package rep;
 
-import java.util.LinkedList;
+
 
+import java.util.HashMap;
 import rep.channel.REPSocketChannel;
-import rep.xml.SessionXMLEncoder;
 
-public class SessionList extends LinkedList<Session>{
-	
+public class SessionList extends HashMap<Integer,Session>{
 	/**
 	 * 
 	 */
 	private static final long serialVersionUID = 1L;
-	LinkedList<String> stringlist = new LinkedList<String>();
-	private String maxHost;
 	private int sid_root = 0;
-	
-
 
 	public void sendSelect(int sid) {
 		REPSocketChannel<REPCommand> channel = get(sid-1).getOwner().getChannel();
@@ -88,43 +83,26 @@
 //		return str.toString();
 //	}*/
 
-	public LinkedList<Session> getList() {
-		return new LinkedList<Session>(this);
-	}
-
-	public String toXML() {
-		SessionXMLEncoder encoder = new SessionXMLEncoder(this);
-		encoder.sessionListToXML();
-		return null;
-	}
 
 	public int addSession(Session session) {
 		int sid;
 		session.setSID(sid=newSessionID());
-		add(session);
+		put(sid,session);
 		return sid;
 	}
-
+	
 	public Session getSession(int sid) {
-		for(Session session : this){
-			if(session.getSID() == sid){
-				return session;
-			}
-		}
-		return get(sid - 1);
+		return get(sid);
 	}
 
-	public void setMaxHost(String myHost) {
-		maxHost = myHost;
-	}
-
-	public String getMaxHost() {
-		return maxHost;
-	}
-
-
 	public int newSessionID() {
 		return ++sid_root ;
 	}
 
+	public void merge(SessionList receivedSessionList) {
+		// TODO Auto-generated method stub
+		
+	}
+	
+
 }
--- a/rep/SessionManager.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/rep/SessionManager.java	Sun Oct 19 16:54:37 2008 +0900
@@ -46,7 +46,7 @@
 public class SessionManager implements SessionManagerEventListener{
 	static public REPLogger logger = REPLogger.singleton();
 
-	LinkedList<Session> sessionList;
+	SessionList sessionList;
 	private SessionManagerGUI gui;
 	private REPSelector<REPCommand> selector;
 	SessionManagerList smList;
@@ -61,9 +61,14 @@
 	private int parent_port;
 	static final int DEFAULT_PORT = 8766;
 	private static final int packetLimit = 200;
+
+	private static final int MAXID = 10000;
 	SessionXMLDecoder decoder = new SessionXMLDecoder();
+	SessionXMLEncoder encoder = new SessionXMLEncoder();
 	private Forwarder sm_join_channel;
 
+	private RoutingTable routingTable = new RoutingTable();
+
 	public static void main(String[] args) throws InterruptedException, IOException {
 		
 		int port = DEFAULT_PORT;
@@ -109,7 +114,7 @@
 		ssc.register(selector, SelectionKey.OP_ACCEPT, 
 				new Forwarder(this));
 
-		sessionList = new LinkedList<Session>();
+		sessionList = new SessionList();
 		smList = new SessionManagerList();
 		editorList = new EditorList();
 		waitingCommandInMerge = new LinkedList<PacketSet>();
@@ -214,7 +219,7 @@
 				REPHandler handler = (REPHandler)(key.attachment());
 				try {
 					handler.handle(key);
-				} catch (Exception x) {
+				} catch (IOException e) {
 					key.cancel();
 					handler.cancel(key.channel1());
 				}
@@ -248,47 +253,16 @@
 	}
 
 
-	boolean hasSession(int sid) {
-		for(Session s:sessionList) {
-			if (s.getSID()==sid) return true;
-		}
-		return false;
-	}
-
-
 	void updateGUI() {
 		//リストのコピーをGUIに渡す
-		LinkedList<Session> sList = new LinkedList<Session>(sessionList);
-		LinkedList<Editor> eList = new LinkedList<Editor>(editorList);
+		LinkedList<Session> sList = new LinkedList<Session>(sessionList.values());
+		LinkedList<Editor> eList = new LinkedList<Editor>(editorList.values());
 		//GUIに反映
 		Runnable doRun = new DoGUIUpdate(sList, eList, gui);
 		gui.invokeLater(doRun);
 	}
 
-	Forwarder getEditor(String hostport) {
-		for(Editor editor : editorList){
-			if(editor.getHost() == hostport){
-				return editor;
-			}
-		}
-		return null;
-	}
-	
-	public Session getSession(int sid) throws IOException {
-		for(Session session : sessionList){
-			if(session.getSID() == sid) return session;
-		}
-		throw new IOException();
-	}
 
-//	private boolean setMaxHost(REPSocketChannel<REPCommand> channel, String maxHost2) {
-//		if(maxHost.compareTo(maxHost2) > 0){
-//			return false;
-//		}else{
-//			maxHost = maxHost2;
-//			return true;
-//		}
-//	}
 
 	void setMyHostName(String localHostName) {
 		myHost = localHostName + receive_port;
@@ -299,8 +273,9 @@
 	}
 
 	private void setHostToEditor(String myHost2) {
-		for(Editor editor : editorList){
-			editor.setHost(myHost2);
+		for(Editor editor : editorList.values()){
+			if (editor.channel!=null)
+				editor.setHost(myHost2);
 		}
 	}
 
@@ -339,9 +314,6 @@
 		//hostnameをセット。
 		setMyHostName(channel.getLocalHostName());
 		
-		//XMLを生成。送信コマンドにセット。
-		//SessionXMLEncoder encoder = new SessionXMLEncoder(sessionList);
-		//String string = encoder.sessionListToXML();
 		String string = myHost;
 		command.setString(string);
 		
@@ -353,7 +325,7 @@
 	
 	public void selectSession(SelectButtonEvent event) throws IOException {
 		int sid = event.getSID();
-		Session session = getSession(sid);
+		Session session = sessionList.get(sid);
 		
 		Editor editor = (Editor)event.getEditor();
 		if(editor == null){
@@ -361,32 +333,48 @@
 			return;
 		}
 		if (editor.hasSession()) return;
-		if (editorList.waiting(editor)) return;
 
+		selectSession(sid, session, editor.getEID(), editor);
+	}
+
+
+	private void selectSession(int sid, Session session, int eid, Forwarder editor) {
 		if(session.hasOwner()){
-			editor.setEID(editorList.newEid());
-			editor.setSID(sid);
 			session.addForwarder(editor);
 			REPCommand sendCommand = new REPCommand();
-			sendCommand.setCMD(REP.SMCMD_JOIN_ACK);
+			if (editor.isDirect()&&editor.getEID()==eid) {
+				sendUpdate();
+				sendCommand.setCMD(REP.SMCMD_JOIN_ACK);
+			} else {
+				// SELECT_ACK is sent to the session ring to
+				// find out joined editor
+				sendCommand.setCMD(REP.SMCMD_SELECT_ACK);
+			}
 			sendCommand.setEID(editor.getEID());
 			sendCommand.setSID(sid);
 			sendCommand.string = "";
 			editor.send(sendCommand);
 		}else {
+			session.addForwarder(editor);
 			editor.setHost(myHost);
 			editor.setSID(sid);
-			session.addEditor(editor);
-			Forwarder owner = session.getOwner();
+			Forwarder next = routingTable.toSession(sid);
 			
 			REPCommand command = new REPCommand();
 			command.setCMD(REP.SMCMD_SELECT);
 			command.setSID(sid);
+			command.setEID(eid);
 			command.setString(editor.getHost());
-			owner.send(command);
+			next.send(command);
 		}
 	}
 
+	private void sendUpdate() {
+		// TODO Auto-generated method stub
+		
+	}
+
+
 	public void addWaitingCommand(PacketSet set) {
 		waitingCommandInMerge.add(set);
 	}
@@ -413,12 +401,13 @@
 	}
 
 	public void remove(REPSocketChannel<REPCommand> channel) {
-		for(Session s:sessionList) {
+		int i = 0;
+		for(Session s:sessionList.values()) {
 			if (s.deleteEditor(channel)) {
-				return ;
+				i++;
 			}
 		}
-		assert(false);
+		assert(i==1);
 		// can be other session manager? what should I do?
 	}
 
@@ -430,20 +419,20 @@
 
 
 	public void remove(Editor editor) {
-		Session s0 = null;
-		editorList.remove(editor);
-		for(Session s:sessionList) {
-			if (s.deleteForwarder(editor)) {
-				if (editor.getEID()==0) s0=s;
-			}
+		Session s = sessionList.get(editor.getSID());
+		if (editor.isMaster()) {
+			removeSession(s);
+		} else {
+			s.deleteForwarder(editor);
+			editorList.remove(editor);
 		}
-		if (s0!=null) removeSession(s0);
 		updateGUI();
 	}
 
 	private void removeSession(Session s0) {
+		s0.remove(this);
 		sessionList.remove(s0);
-		// send UPDATE to all the session manager
+		sendUpdate();
 	}
 
 	public void setParentPort(int port) {
@@ -459,103 +448,73 @@
 
 
 	boolean sessionManage(Forwarder forwarder, REPCommand command) throws ClosedChannelException,
-			IOException, SAXException {
+			IOException {
 		switch(command.cmd){
 	
 		// Session Manager Command
 	
 		case SMCMD_JOIN:
 		{
-			updateGUI();
+			// first connection or forwarded command
+			if (forwarder.isDirect()) {
+				// direct linked editor なので、ここでIDを作成する
+				command.setEID(makeID(editorList.newEid()));
+			}
+			if(isMaster()) {
+				REPCommand ackCommand = new REPCommand();
+				ackCommand.setCMD(REP.SMCMD_JOIN_ACK);
+				ackCommand.setEID(command.eid);
+				ackCommand.string = command.string;
+				smList.sendToSlaves(ackCommand);
+				registEditor(forwarder,ackCommand);
+			} else {
+				routingTable.add(forwarder,getSMID(command.eid),command.sid);
+				smList.sendToMaster(command);
+			}
+
 		}
 	
 		break;
 	
 		case SMCMD_JOIN_ACK:
-			assert (false);
+		case SMCMD_PUT_ACK:
+			registEditor(forwarder,command);
 			break;
 	
 		case SMCMD_PUT:
 		{
-			//Sessionを生成
-			// sessionIDってglobaly uniqueだから、本来は、
-			// 自分の親に作ってもらう必要がある。自分が親なら自分で作って良い。
-			
-			int sid = sessionList.size();
-			Editor editor = (Editor) forwarder;
-			Session session = new Session(sid, command.string, (Editor)forwarder);
-			session.hasOwner(true);
-			sessionList.add(session);
-	
-			updateGUI();
-	
-			//エディタにAckを送信
-			REPCommand sendCommand = new REPCommand(command);
-			sendCommand.setCMD(REP.SMCMD_PUT_ACK);
-			sendCommand.setEID(editor.getEID());
-			sendCommand.setSID(session.getSID());
-			editor.send(sendCommand);
-	
-			//他のSessionManagerへSessionの追加を報告
-			//親に送って、親から子へ
-			SessionXMLEncoder sessionEncoder = new SessionXMLEncoder(session);
-			REPCommand command1 = new REPCommand();
-			command1.setSID(session.getSID());
-			command1.setString(sessionEncoder.sessionListToXML());
-			command1.setCMD(REP.SMCMD_UPDATE);
-			smList.sendToSlaves( command);
+			// first connection or forwarded command
+			if (forwarder.isDirect()) {
+				// direct link, make new ID
+				int eid = makeID(editorList.newEid());
+				int sid = makeID(sessionList.newSessionID());
+				command.setEID(eid);
+				command.setSID(sid);
+			}
+			if(isMaster()) {
+				command.setCMD(REP.SMCMD_PUT_ACK);
+				command.string = command.string;
+				command.setEID(command.eid);
+				command.setSID(command.sid);
+				smList.sendToSlaves(command);
+				registEditor(forwarder,command);
+			} else {
+				routingTable.add(forwarder,getSMID(command.eid),command.sid);
+				smList.sendToMaster(command);
+			}
 	
 		}
-	
 		break;
-	
-		// SELECT is no longer used in a editor. Select
-		// operation is handled in Session Manager Only
+
 		case SMCMD_SELECT:
-		{
-			//他のSessionManagerをエディタとしてSessionに追加
-			Forwarder next = new Forwarder(this);
-			next.setChannel(forwarder.channel);
-			Session session = getSession(command.sid);
-			session.addForwarder(next);
-	
-			if(session.hasOwner()){
-				//このSessionManagerがオーナーを持っている場合、Sessionにエディタを追加し、エディタへAckを返す
-				REPCommand sendCommand = new REPCommand(command);
-				sendCommand.setCMD(REP.SMCMD_SELECT_ACK);
-				sendCommand.setEID(next.getEID());
-				next.send(sendCommand);
-			}else{
-				//オーナーを持ってない場合は、オーナーを持っているSessionManagerへSELECTコマンドを中継する
-				Forwarder owner = session.getOwner();
-				owner.send(command);
-			}
-		}
-	
-		break;
-	
 		case SMCMD_SELECT_ACK:
 		{
-			String hostport = command.string;
-			Forwarder editor1 = getEditor(hostport);
-	
-			if(editor1 != null) {
-				//host, port を見て、このコマンドが自分が送信したSelectコマンドのAckかどうかを判断する
-				REPCommand command1 = new REPCommand();
-				command1.setCMD(REP.SMCMD_JOIN_ACK);
-				command1.setSID(command.sid);
-				command1.setEID(command.eid);
-				editor1.send(command);
-	
-			}else{
-				//自分が送信したコマンドでなければ、次のSessionManagerへ中継する
-				smList.sendToSlaves(command);
-			}
+			Session session = sessionList.get(command.sid);
+			selectSession(command.sid, session, command.eid, forwarder);
 		}
-	
-		break;
+			break;
+			
 		case SMCMD_SM_JOIN:
-	
 		{
 			// SM_JOIN中にSMCMD_SM_JOINが来たら、これはループなので、
 			///自分のSM_JOINを取り消す。
@@ -600,55 +559,17 @@
 			break;
 	
 		case SMCMD_UPDATE:
-		{
-			SessionList receivedSessionList3 = decoder.decode(command.string);
-	
-			//UPDATEコマンドにより送られてきたSessionの情報を追加する
-			LinkedList<Session> list = receivedSessionList3.getList();
-			for(Session session : list){
-				session.getEditorList().get(0).setChannel(forwarder.channel);
-				sessionList.add(session);
-			}
-	
-			//他のSessionManagerへ中継する
-			smList.sendToSlaves(command);
-	
-			updateGUI();
-		}
+			command.setString(mergeUpdate(command));
+			// 上に知らせる
+			smList.sendToMaster(command);
 			break;
 	
 		case SMCMD_UPDATE_ACK:
-		{
-			if(!hasSession(command.sid)) {
-				// accept new Session
-				// ここで初めてsession id が決まる。
-				// このコマンドは、master session manager が出すはず
-				Forwarder sm = new Forwarder(this);
-				sm.setChannel(forwarder.channel);
-				Session session = new Session(command.sid,command.string,null);
-				session.addForwarder(sm);
-	
-				sessionList.add(session);
-				
-				updateGUI();
-			}
+			command.setString(mergeUpdate(command));
+			// 下に知らせる
 			smList.sendToSlaves(command);
-		}
+			updateGUI();
 			break;
-	
-	//		case SMCMD_CH_MASTER:
-	//		{
-	//			//maxHost を設定。
-	//			if(setMaxHost(channel, receivedCommand.string)){
-	//				REPCommand sendCommand = new REPCommand();
-	//				sendCommand.setCMD(REP.SMCMD_CH_MASTER);
-	//				sendCommand.setString(maxHost);
-	//				smList.sendExcept(channel, sendCommand);
-	//			}
-	//		}
-	//			break;
-	
-	
 		default:
 			return false;
 		}
@@ -656,12 +577,66 @@
 	}
 
 
+	private String mergeUpdate(REPCommand command) throws IOException {
+		SessionList receivedSessionList;
+		try {
+			receivedSessionList = decoder.decode(command.string);
+		} catch (SAXException e) {
+			throw new IOException();
+		}
+		// UPDATE/UPDATE_ACKにより送られてきたSessionの情報を追加する
+		//XMLを生成。送信コマンドにセット。
+		sessionList.merge(receivedSessionList);
+		return encoder.sessionListToXML(sessionList);
+
+	}
+
+	/*
+	 * id has SessionManager ID part
+	 */
+
+	private int makeID(int newid) {
+		return newid+smList.sessionManagerID()*MAXID;
+	}
+
+	private int getSessionID(int id) {
+		return id%MAXID;
+	}
+	
+	private int getSMID(int id) {
+		return id/MAXID;
+	}
+
+
+	private void registEditor(Forwarder forwarder,REPCommand command) {
+		// make ack for PUT/JOIN. Do not send this to the editor,
+		// before select. After select, ack is sent to the editor. 
+		routingTable.add(forwarder,getSMID(command.eid),command.sid);
+		Editor editor;
+		if (getSessionID(command.sid)==smList.sessionManagerID()
+				&& forwarder.isDirect()) {
+			// direct link だった
+			editor = (Editor)forwarder;
+		} else {
+			editor = new Editor(this, command.cmd==REP.SMCMD_PUT_ACK, command.eid);
+		}
+		editor.setName(command.string);
+		editor.setSID(command.sid);
+		if (!editorList.hasEid(command.eid)) {
+			editorList.add(editor);
+			updateGUI();
+		}
+		// we don't join ack to the direct linked editor. We
+		// have to wait select command
+	}
+
+
 	void send_sm_join_ack(int psid, int sid,REPCommand sendCommand) {
 		if (psid==smList.sessionManagerID()) {
 			// 直下のsessionManagerにIDを割り振る必要がある。
 			smList.assignSessionManagerIDtoWaitingSM(sid);
 			// ここで smList に一つだけ追加されるので
-			// 待っている最初のsm一つにだけ、sm_joinが新たに送られる。
+			// 待っている最初のsm一つにだけ、sm_join_ackが新たに送られる。
 		}
 		smList.sendToSlaves(sendCommand);
 	}
@@ -670,10 +645,9 @@
 	private REPCommand makeREPCommandWithSessionList(REP cmd) {
 		//SessionListからXMLを生成。
 		//joinしてきたSessionManagerに対してACKを送信。
-		SessionXMLEncoder sessionlistEncoder = new SessionXMLEncoder(sessionList);
 		REPCommand sendCommand = new REPCommand();
 		sendCommand.setCMD(cmd);
-		sendCommand.setString(sessionlistEncoder.sessionListToXML());
+		sendCommand.setString(encoder.sessionListToXML(sessionList));
 		return sendCommand;
 	}
 
@@ -687,4 +661,9 @@
 		smList.setSessionManagerID(sid);		
 	}
 
+
+	public Session getSession(int sid) {
+		return sessionList.get(sid);
+	}
+
 }
--- a/rep/handler/REPHandler.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/rep/handler/REPHandler.java	Sun Oct 19 16:54:37 2008 +0900
@@ -1,11 +1,13 @@
 package rep.handler;
 
+import java.io.IOException;
+
 import rep.REPCommand;
 import rep.channel.REPSelectionKey;
 import rep.channel.REPSocketChannel;
 
 public interface REPHandler {
-	void handle(REPSelectionKey<REPCommand> key)throws Exception;
+	void handle(REPSelectionKey<REPCommand> key) throws IOException;
 
 	void cancel(REPSocketChannel<REPCommand> socketChannel);
 
--- a/rep/xml/SessionXMLDecoder.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/rep/xml/SessionXMLDecoder.java	Sun Oct 19 16:54:37 2008 +0900
@@ -44,22 +44,22 @@
 
 		//sessionlist = createSessionList(root);
 		sessionlist = generateSessionList(root);
-		sessionlist.setMaxHost(getSessionManagerHost(root));
+		//sessionlist.setMaxHost(getSessionManagerHost(root));
 
 
 		return sessionlist;
 	}
 
-	private String getSessionManagerHost(Element root) {
-		NodeList nodelist = root.getChildNodes();
-		String host = null;
-		for(int i = 0; i < nodelist.getLength(); i++){
-			if(nodelist.item(i).getNodeName().equals("host")){
-				host = nodelist.item(i).getTextContent();
-			}
-		}
-		return host;
-	}
+//	private String getSessionManagerHost(Element root) {
+//		NodeList nodelist = root.getChildNodes();
+//		String host = null;
+//		for(int i = 0; i < nodelist.getLength(); i++){
+//			if(nodelist.item(i).getNodeName().equals("host")){
+//				host = nodelist.item(i).getTextContent();
+//			}
+//		}
+//		return host;
+//	}
 	
 	private SessionList generateSessionList(Element element){
 		SessionList sessionlist = new SessionList();
--- a/rep/xml/SessionXMLEncoder.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/rep/xml/SessionXMLEncoder.java	Sun Oct 19 16:54:37 2008 +0900
@@ -5,7 +5,10 @@
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
@@ -18,26 +21,10 @@
 import rep.SessionList;
 
 public class SessionXMLEncoder {
-	private LinkedList<Session> list = new LinkedList<Session>();
-	private String sessionmanagerHost;
-
-	public SessionXMLEncoder(SessionList sessionlist) {
-		sessionmanagerHost = sessionlist.getMaxHost();
-		this.list = sessionlist.getList();
-	}
+	DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+	TransformerFactory tfactory = TransformerFactory.newInstance(); 
 
-	public SessionXMLEncoder(LinkedList<Session> sessionList) {
-		this.list = sessionList;
-	}
-
-	public SessionXMLEncoder() {
-	}
-
-	public SessionXMLEncoder(Session session) {
-		list.add(session);
-	}
-
-	public String sessionListToXML() {
+	public String sessionListToXML(SessionList list) {
 		/* SessionListをXMLに書き出す。ときの形式
 		 * <?xml version="1.0" encoding="UTF-8"?>
 		 * <SessionList><host>naha.ie.u-ryukyu.ac.jp</host>
@@ -59,57 +46,62 @@
 		 *   </Session>
 		 * </SessionList>
 		 */
-				StringWriter str = null;
-				try {
-					str = new StringWriter();
-					DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-					DocumentBuilder builder = factory.newDocumentBuilder();
-					Document doc = builder.newDocument();
-					Element root = doc.getDocumentElement();
-					root = doc.createElement("SessionList");
-					
-					Element elementSeMaHost = doc.createElement("host");
-					if(sessionmanagerHost != null) elementSeMaHost.setTextContent(sessionmanagerHost);
-					root.appendChild(elementSeMaHost);
+		StringWriter str = new StringWriter();
+		DocumentBuilder builder;
+		try {
+			builder = factory.newDocumentBuilder();
+		} catch (ParserConfigurationException e) {
+			return "";
+		}
+		Document doc = builder.newDocument();
+		Element root = doc.getDocumentElement();
+		root = doc.createElement("SessionList");
+		//					
+		//					Element elementSeMaHost = doc.createElement("host");
+		//					if(sessionmanagerHost != null) elementSeMaHost.setTextContent(sessionmanagerHost);
+		//					root.appendChild(elementSeMaHost);
 
-					for(Session session : list){
-						Element elementSession = doc.createElement("Session");
-						root.appendChild(elementSession);
-						elementSession.setAttribute("sid", Integer.toString(session.getSID()));
+		for(Session session : list.values()){
+			Element elementSession = doc.createElement("Session");
+			root.appendChild(elementSession);
+			elementSession.setAttribute("sid", Integer.toString(session.getSID()));
+
+			LinkedList <EditorPlus> editorlist = session.getEditorList();
+			for(EditorPlus editor : editorlist){
+				Element elementEditor = doc.createElement("Editor");
+				elementEditor.setAttribute("eid", Integer.toString(editor.getEID()));
+				elementSession.appendChild(elementEditor);
+
+				Element elementHost = doc.createElement("host");
+				elementEditor.appendChild(elementHost);
+				elementHost.setTextContent(editor.getHost());
 
-						LinkedList <EditorPlus> editorlist = session.getEditorList();
-						for(EditorPlus editor : editorlist){
-							Element elementEditor = doc.createElement("Editor");
-							elementEditor.setAttribute("eid", Integer.toString(editor.getEID()));
-							elementSession.appendChild(elementEditor);
-							
-							Element elementHost = doc.createElement("host");
-							elementEditor.appendChild(elementHost);
-							elementHost.setTextContent(editor.getHost());
-							
-							Element elementPort = doc.createElement("port");
-							elementEditor.appendChild(elementPort);
-							//elementPort.setTextContent(editor.getPort());
-							
-							if(editor.getName() != null){
-								Element elementName = doc.createElement("file");
-								elementEditor.appendChild(elementName);
-								elementName.setTextContent(editor.getName());
-								//System.out.println(editor.getName());
-							}
-						}
-					}
-					doc.appendChild(root);
-					
-					TransformerFactory tfactory = TransformerFactory.newInstance(); 
-					Transformer transformer = tfactory.newTransformer(); 
-					StreamResult result = new StreamResult(str);
-					transformer.transform(new DOMSource(doc), result); 
+				Element elementPort = doc.createElement("port");
+				elementEditor.appendChild(elementPort);
+				//elementPort.setTextContent(editor.getPort());
+
+				if(editor.getName() != null){
+					Element elementName = doc.createElement("file");
+					elementEditor.appendChild(elementName);
+					elementName.setTextContent(editor.getName());
+					//System.out.println(editor.getName());
+				}
+			}
+		}
+		doc.appendChild(root);
 
-				}catch (Exception e){
-					
-				}
-				//System.out.println(str.toString());
-				return str.toString();
-			}
+		Transformer transformer;
+		StreamResult result = new StreamResult(str);
+		try {
+			transformer = tfactory.newTransformer();
+			transformer.transform(new DOMSource(doc), result);
+		} catch (TransformerConfigurationException e1) {
+			return "";
+		} catch (TransformerException e) {
+			return "";
+		} 
+
+		//System.out.println(str.toString());
+		return str.toString();
+	}
 }
--- a/test/XMLTest.java	Sat Oct 18 21:28:17 2008 +0900
+++ b/test/XMLTest.java	Sun Oct 19 16:54:37 2008 +0900
@@ -26,19 +26,19 @@
 		session.addEditor(editor2);
 		
 		SessionList sessionlist = new SessionList();
-		sessionlist.setMaxHost("naha.ie.u-ryukyu.ac.jp");
+		//sessionlist.setMaxHost("naha.ie.u-ryukyu.ac.jp");
 		sessionlist.addSession(session);
 		
 		/*** SessionList から XML へ ***/
 		//SessionXMLEncoder encoder = new SessionXMLEncoder(sessionlist.getList());
-		SessionXMLEncoder encoder = new SessionXMLEncoder(sessionlist);
-		System.out.println(encoder.sessionListToXML());
+		SessionXMLEncoder encoder = new SessionXMLEncoder();
+		System.out.println(encoder.sessionListToXML(sessionlist));
 		
 		/*** XML から SessionList へ ***/
-		SessionXMLDecoder decoder = new SessionXMLDecoder();
-		String t1 = encoder.sessionListToXML();
+		String t1 = encoder.sessionListToXML(sessionlist);
 		System.out.println(t1);
 		SessionList sessionlist2;
+		SessionXMLDecoder decoder = new SessionXMLDecoder();
 		try {
 			sessionlist2 = decoder.decode(t1);
 		} catch (Exception e) {
@@ -47,11 +47,10 @@
 		
 		/*** SessionList から XML へ ***/
 		//SessionXMLEncoder encoder2 = new SessionXMLEncoder(sessionlist2.getList());
-		SessionXMLEncoder encoder2 = new SessionXMLEncoder(sessionlist2);
-		System.out.println(encoder2.sessionListToXML());
+		System.out.println(encoder.sessionListToXML(sessionlist2));
 		
 		/*** TestSAX ***/
-		String[] string = {encoder2.sessionListToXML(),};
+		String[] string = {encoder.sessionListToXML(sessionlist2),};
 		TestSax.main(string);
 	}
 }