# HG changeset patch # User one # Date 1230703582 -32400 # Node ID 3133040ee4f4a0583ba516a2ba5b4ae67404f5e4 # Parent c921022bf494b348770867059e8712250a538975 (no commit message) diff -r c921022bf494 -r 3133040ee4f4 src/remoteeditor/editors/LogTarget.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/remoteeditor/editors/LogTarget.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,7 @@ +package remoteeditor.editors; + +public interface LogTarget { + + void printLog(Object obj); + +} diff -r c921022bf494 -r 3133040ee4f4 src/remoteeditor/editors/Logger.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/remoteeditor/editors/Logger.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,1 @@ +package remoteeditor.editors; 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 c921022bf494 -r 3133040ee4f4 src/remoteeditor/editors/REPEditor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/remoteeditor/editors/REPEditor.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,243 @@ +package remoteeditor.editors; + +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.REPSelectionKey; +import rep.channel.REPSelector; +import rep.channel.REPSocketChannel; + + +public class REPEditor extends Thread implements REPTextListener{ + + private REPSocketChannel channel; + REPSelector selector; + private boolean running = true; + private boolean inputLock = false; + private long timeout = 1; + private int syncCounter = 0; + private LinkedList userCommand = new LinkedList(); + private LinkedList runners = new LinkedList(); + private String name = "test"; + private int seq; + private int eid; + private int sid; + private REPText repText; + private boolean hasInputLock; + private boolean master; + private boolean syncEnable = true; + + public REPEditor(REPText repText, boolean master){ + this.repText = repText; + this.master = master; + repText.addTextListener(this); + if(master){ + userCommand.add(new REPCommand(REP.SMCMD_PUT,0,0,0,0,name +"-file")); + }else{ + userCommand.add(new REPCommand(REP.SMCMD_JOIN, 0, 0, 0, 0, name)); + } + } + + public void textDeleted(REPTextEvent event) { + Logger.print(event.getText()); + addUserInput(new REPCommand(REP.REPCMD_DELETE_USER, 0, 0, 0, event.getLineno(), event.getText())); + } + + public void textInserted(REPTextEvent event) { + Logger.print(event.getText()); + addUserInput(new REPCommand(REP.REPCMD_INSERT_USER, 0, 0, 0, event.getLineno(), event.getText())); + } + + private void addUserInput(final REPCommand command) { + Runnable runner = new Runnable(){ + public void run(){ + userCommand.add(command); + timeout = 1; + } + }; + synchronized(runners){ + runners.add(runner); + } + if(selector != null){ + selector.wakeup(); + } + } + + public void run(){ + /* + * Create Socket and connect to the session manager + */ + try { + channel = REPSocketChannel.create(new REPCommandPacker()); + } catch (IOException e) { + e.printStackTrace(); + return; + } + try { + InetSocketAddress semaIP = new InetSocketAddress("localhost", 8766); + while (!channel.connect(semaIP)){ + Logger.print("SeMa not listen to socket yet, wait"); + } + } catch (IOException e) { + e.printStackTrace(); + return; + } + /* + * 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); + selector = REPSelector.create(); + channel.register(selector, SelectionKey.OP_READ); + while(running) { + + synchronized(runners){ + for(Runnable runner : runners){ + runner.run(); + } + runners.clear(); + } + + 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 handle(REPCommand command) { + Logger.print(command); + if(command == null) return; + switch(command.cmd){ + case REPCMD_DELETE: + if(command.eid != eid){ + String del = repText.delete(command.lineno); + command.setString(del); + } + forward(command); + break; + case REPCMD_INSERT: + if(command.eid != eid){ + repText.insert(command.lineno, command.string); + } + forward(command); + break; + case REPCMD_NOP: + case REPCMD_INSERT_ACK: + case REPCMD_DELETE_ACK: + forward(command); + break; + case SMCMD_PUT_ACK: + sid = command.sid; + eid = command.eid; + name += "(eid="+eid+",sid="+sid+")"; + inputLock = false; + break; + case SMCMD_JOIN_ACK : + sid = command.sid; + eid = command.eid; + name += "(eid="+eid+",sid="+sid+")"; + inputLock = false; + break; + case SMCMD_START_MERGE : + // lock user input during merge (optional) + inputLock = hasInputLock; + command.cmd = REP.SMCMD_START_MERGE_ACK; + send(command); + break; + case SMCMD_END_MERGE : + inputLock = false; + break; + case SMCMD_SYNC: + // start contents sync with newly joined editor + command.cmd = REP.SMCMD_SYNC_ACK; + forward(command); + //if (cmd.eid==eid) { + if (master && syncEnable ) { + syncCounter = 1; + timeout = 1; + } + break; + } + } + + private void userInput() { + Logger.print(); + REPCommand command = userCommand.poll(); + if(command != null){ + switch(command.cmd){ + case REPCMD_DELETE_USER: + send(command); + break; + case REPCMD_INSERT_USER: + send(command); + break; + case SMCMD_PUT: + case SMCMD_JOIN: + send(command); + break; + } + }else{ + if(syncCounter == 0){ + timeout = 0; + } + } + } + + private void forward(REPCommand command) { + REPCommand cmd = new REPCommand(command); + channel.write(cmd); + } + + private void send(REPCommand command) { + REPCommand cmd = new REPCommand(command); + cmd.setSEQID(seq++); + cmd.setEID(eid); + cmd.setSID(sid); + channel.write(cmd); + } + + private void syncText() { + Logger.print(); + if(syncCounter>repText.size()){ + syncCounter = 0; + }else { + if(inputLock) return; + int i = syncCounter - 1; + REPCommand del = new REPCommand(REP.REPCMD_DELETE_USER, sid, eid, 0, i, repText.get(i)); + REPCommand ins = new REPCommand(REP.REPCMD_INSERT_USER, sid, eid, 0, i, repText.get(i)); + send(del); + send(ins); + syncCounter++; + } + } + +} diff -r c921022bf494 -r 3133040ee4f4 src/remoteeditor/editors/REPText.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/remoteeditor/editors/REPText.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,19 @@ +package remoteeditor.editors; + +import java.util.List; + +public interface REPText { + + public void insert(int lineno, String text); + + public String delete(int lineno); + + public List list(); + + public void addTextListener(REPTextListener listener); + + public int size(); + + public String get(int i); + +} diff -r c921022bf494 -r 3133040ee4f4 src/remoteeditor/editors/REPTextEvent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/remoteeditor/editors/REPTextEvent.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,20 @@ +package remoteeditor.editors; + +public class REPTextEvent { + + private int lineno; + private String text; + + public REPTextEvent(int lineno, String text) { + this.lineno = lineno; + this.text = text; + } + + public int getLineno(){ + return lineno; + } + + public String getText(){ + return text; + } +} diff -r c921022bf494 -r 3133040ee4f4 src/remoteeditor/editors/REPTextImpl2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/remoteeditor/editors/REPTextImpl2.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,155 @@ +package remoteeditor.editors; + +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.swt.widgets.Display; + +public class REPTextImpl2 implements REPText, IDocumentListener { + + private IDocument document; + private Display display; + private String BR = System.getProperty("line.separator"); + private List textListenerList; + private boolean myInput = false; + + public REPTextImpl2(IDocument document, Display display){ + this.document = document; + this.display = display; + textListenerList = new LinkedList(); + document.addDocumentListener(this); + } + + public String delete(int lineno) { + int d = lineno - document.getNumberOfLines(); + for(int i = 0; i <= d; i++){ + increaseLine(); + } + + String deleted = getLineText(lineno); + try { + final int length = document.getLineLength(lineno); + final int offset = document.getLineOffset(lineno); + edit(offset, length, ""); + } catch (BadLocationException e) { + e.printStackTrace(); + } + return deleted; + } + + public void insert(int lineno, String text) { + int d = lineno - document.getNumberOfLines(); + for(int i = 0; i <= d; i++){ + increaseLine(); + } + + try { + final int offset = document.getLineOffset(lineno); + final String changedText = text + BR; + edit(offset, 0, changedText); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + + private void increaseLine() { + edit(document.getLength(), 0, BR); + } + + private void edit(final int offset, final int length, final String text) { + display.syncExec(new Runnable(){ + public void run(){ + try { + myInput = true; + document.replace(offset, length, text); + myInput = false; + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + }); + } + + public List list() { + LinkedList list = new LinkedList(); + int lines = document.getNumberOfLines(); + for(int i = 0; i < lines; i++){ + String text = getLineText(i); + list.add(text); + } + return list; + } + + private String getLineText(int lineno){ + int offset = 0; + int length = 0; + String text = null; + try { + offset = document.getLineOffset(lineno); + length = document.getLineLength(lineno); + String tmp = document.get(offset, length); + text = tmp.replace(BR, ""); + } catch (BadLocationException e) { + e.printStackTrace(); + } + return text; + } + + public void documentAboutToBeChanged(DocumentEvent event) { + if(myInput) return; + int offset = event.getOffset(); + int length = event.getLength(); + + try { + int startLine = document.getLineOfOffset(offset); + int endLine = document.getLineOfOffset(offset + length); + for(int i = startLine; i <= endLine; i++){ + String text = getLineText(i); + for(REPTextListener listener : textListenerList){ + listener.textDeleted(new REPTextEvent(i, text)); + } + } + } catch (BadLocationException e) { + e.printStackTrace(); + } + + } + + public void documentChanged(DocumentEvent event) { + if(myInput) return; + int offset = event.getOffset(); + int length = event.getText().length(); + + try { + int startLine = document.getLineOfOffset(offset); + int endLine = document.getLineOfOffset(offset + length); + for(int i = startLine; i <= endLine; i++){ + String text = getLineText(i); + for(REPTextListener listener : textListenerList){ + listener.textInserted(new REPTextEvent(i, text)); + } + } + } catch (BadLocationException e) { + e.printStackTrace(); + } + + } + + public synchronized void addTextListener(REPTextListener listener) { + Logger.print(listener); + textListenerList.add(listener); + } + + public String get(int lineno) { + return getLineText(lineno); + } + + public int size() { + return document.getNumberOfLines(); + } + +} diff -r c921022bf494 -r 3133040ee4f4 src/remoteeditor/editors/REPTextListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/remoteeditor/editors/REPTextListener.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,9 @@ +package remoteeditor.editors; + +public interface REPTextListener { + + public void textInserted(REPTextEvent event); + + public void textDeleted(REPTextEvent event); + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/REP.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/REP.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,66 @@ +package rep; + +public enum REP { + REPCMD_INSERT_USER ( 5), + REPCMD_INSERT ( 6), + REPCMD_INSERT_ACK ( 7), + REPCMD_DELETE_USER ( 8), + REPCMD_DELETE ( 9), + REPCMD_DELETE_ACK ( 10), + REPCMD_CLOSE ( 11), + REPCMD_CLOSE_2 ( 12), + REPCMD_NOP ( 15), + SMCMD_JOIN ( 41), + SMCMD_JOIN_ACK ( 42), + SMCMD_PUT ( 45), + SMCMD_PUT_ACK ( 46), + SMCMD_SELECT ( 47), + SMCMD_SELECT_ACK ( 48), + SMCMD_SELECT0(49), + SMCMD_QUIT ( 53), + SMCMD_QUIT_ACK ( 54), + SMCMD_SM_JOIN ( 62), + SMCMD_SM_JOIN_ACK ( 63), + SMCMD_UPDATE ( 65), + SMCMD_UPDATE_ACK ( 66), + SMCMD_START_MERGE ( 75), + SMCMD_START_MERGE_ACK ( 76), + SMCMD_END_MERGE ( 77), + SMCMD_QUIT_2 ( 67), + SMCMD_QUIT_2_ACK ( 68), + + + SM_EID ( -1), + MERGE_EID ( -2), + + SMCMD_SYNC ( 83), + SMCMD_SYNC_ACK ( 84); + + public final int id; + + REP(int id) { + this.id = id; + } + + static int max = 0; + static int min = 100; + + static REP rep[] ; + static { + // Certainly this is ridiculous... + for (REP r : REP.values()) { + if (maxr.id) min = r.id; + } + rep = new REP[max-min+1]; + for (REP r : REP.values()) { + rep[r.id-min] = r; + } + } + + public static REP newREP(int id) { + // return new REP(id); this does not work... + return rep[id-min]; + } + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/REPCommand.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/REPCommand.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,68 @@ +package rep; + +import rep.REP; + +public class REPCommand { + public REP cmd; + public int sid; + public int eid; + public int seq; + public int len; + public int lineno; + + public String string; + + public REPCommand(REP cmd,int sid,int eid, int seq, int lineno, String string) { + this.cmd = cmd; + this.sid = sid; + this.eid = eid; + this.seq = seq; + this.lineno = lineno; + this.string = string; + } + + public REPCommand(REPCommand cmd){ + this(cmd.cmd, cmd.sid, cmd.eid, cmd.seq, cmd.lineno, cmd.string); + } + + public REPCommand() { + } + + public REPCommand(int cmd, int sid, int eid, int seq, int lineno, + int textsiz, String string) { + this.cmd = REP.newREP(cmd); + this.sid = sid; + this.eid = eid; + this.seq = seq; + this.lineno = lineno; + this.string = string; + } + + public String toString(){ + String repCmdString = cmd + ",sid=" + sid + ",eid=" + eid + ",seq=" + seq + + ",lineno=" + lineno ; + if (string!=null) repCmdString += ",sz=" + string.length() +"," + string; + return repCmdString; + } + + public void setEID(int eid2) { + this.eid = eid2; + } + + public void setCMD(REP cmd2) { + this.cmd = cmd2; + } + + public void setSID(int sessionID) { + this.sid = sessionID; + } + + public void setString(String string2) { + string = string2; + } + + public void setSEQID(int i) { + seq = i; + } + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/REPCommandPacker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/REPCommandPacker.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,123 @@ +package rep; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.SocketChannel; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import rep.channel.REPPack; + +/* +//+-------+--------+--------+-------+--------+---------+------+ +//| cmd | session| editor | seqid | lineno | textsiz | text | +//| | id | id | | | | | +//+-------+--------+--------+-------+--------+---------+------+ +//o-------header section (network order)-------------o +int cmd; // command +int sid; // session ID : uniqu to editing file +int eid; // editor ID : owner editor ID = 1。Session に対して unique +int seqno; // Sequence number : sequence number はエディタごとに管理 +int lineno; // line number +int textsize; // textsize : bytesize +byte[] text; +*/ + + +public class REPCommandPacker implements REPPack { + private static final int TEXTSIZELIMIT = 0x7000000; + private static final int HEADER_SIZE = 24; + // JIS/S-JIS = 2, UTF-8 = 3, UTF-?? = 5 + private static final int CHARSIZE = 5; + + Charset charset = Charset.forName("UTF-8"); + CharsetEncoder encoder = charset.newEncoder(); + CharsetDecoder decoder = charset.newDecoder(); + + /* (non-Javadoc) + * @see rep.REPPack#packUConv(rep.REPCommand) + */ + public ByteBuffer packUConv(REPCommand command){ + int size = 0; + if (command.string!=null) size = command.string.length()*CHARSIZE; + ByteBuffer buffer = ByteBuffer.allocateDirect(HEADER_SIZE+size); + buffer.clear(); // position = 0 + buffer.putInt(command.cmd.id); + buffer.putInt(command.sid); + buffer.putInt(command.eid); + buffer.putInt(command.seq); + buffer.putInt(command.lineno); + + int pos = buffer.position(); + buffer.putInt(0); + int pos1 = buffer.position(); + + if (command.string!=null) { + //Encode to UTF8 + CharBuffer cb = CharBuffer.wrap(command.string); + try { + encoder.encode(cb, buffer, true); + } catch (IllegalStateException e) { + buffer.position(pos1); + } + } + + //Encoded string length set + int length = buffer.position() -pos1 ; + buffer.putInt(pos, length); + buffer.limit(HEADER_SIZE+length); + buffer.rewind(); + + return buffer; + } + + + public REPCommand unpackUConv(SocketChannel sc) throws IOException { + ByteBuffer header = ByteBuffer.allocateDirect(HEADER_SIZE); + header.clear(); + + while(header.remaining()>0){ + if (sc.read(header)<0) throw new IOException(); + } + + header.rewind(); // position = 0 + + int cmd = header.getInt(); + int sid = header.getInt(); + int eid = header.getInt(); + int seqid = header.getInt(); + int lineno = header.getInt(); + int textsiz = header.getInt(); + + if (textsiz>TEXTSIZELIMIT||textsiz<0) { + // corrupted packet + throw new IOException(); + } + ByteBuffer textBuffer = ByteBuffer.allocateDirect(textsiz); + + while(textBuffer.remaining()>0){ + if (sc.read(textBuffer)<0) throw new IOException(); + } + textBuffer.rewind(); + + //Decode UTF-8 to System Encoding(UTF-16) + String string; + try { + CharBuffer cb; + cb = decoder.decode(textBuffer); + cb.rewind(); + string = cb.toString(); + } catch (CharacterCodingException e) { + string = ""; + } + textsiz = string.length(); + REPCommand repcommand = new REPCommand(cmd, sid, eid, seqid, lineno, textsiz, string); + //if (isLogging) + //System.out.println("Reading: "+repcommand); + return repcommand; + } + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/ChannelSimulator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/ChannelSimulator.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,188 @@ +package rep.channel; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.LinkedList; +import java.util.Queue; + +public class ChannelSimulator

extends REPSocketChannel

{ + protected NetworkSimulator ns; + protected Queue

readQ; + protected Selector selector = new NullSelector(); + protected boolean isBlocking; + protected ChannelSimulator

otherEnd; + protected SelectionKeySimulator

key; + private String ownerName; + + /** Constructors. */ + public ChannelSimulator(){ + super(null, null); + ns = NetworkSimulator.singleton(); + readQ = new LinkedList

(); + ownerName = Thread.currentThread().getName(); + } + + /** read from Queue. + * @throws IOException */ + public synchronized P read() throws IOException { + // We may lock selector instead of this, but it reduces + // concurrency, but be careful of dead lock. + P tmp; + while ( (tmp=readQ.poll())==null && isBlocking ) { + try { + wait(); // queue is empty + } catch (InterruptedException e) { + throw new IOException(); + } + } + // for write wait (we don't need this) + //otherEnd.wakeup(); selector.wakeup(); + return tmp; + } + + //private synchronized void wakeup() { + // notify(); + //} + + /** write packet to other end. */ + public boolean write(P p){ + if (otherEnd==null) throw new NotYetConnectedException(); + return otherEnd.enQ(p); + } + + /** otherEnd Channel enqueue p to own queue using this method. */ + + boolean enQ(P p) { + if (enQ1(p)) { + // don't lock this channel while calling selector.wakeup(). + // selector may lock this channel, which may cause dead lock. + selector.wakeup(); + return true; + } + return false; + } + + protected synchronized boolean enQ1(P p){ + while(true) { + if (readQ.offer(p)) { + notify(); // other end my wait() + break; + } else { // this can't happen + assert(false); + try { + wait(); // queue is full, we have to wait here + } catch (InterruptedException e) { + } + } + } + return true; + } + + /** Connecting methods */ + // for clients. + public boolean connect(InetSocketAddress ip){ + return ns.connect(ip, this); // ns.connectはotherEndを返した方がよいか? + } + + public boolean connect(SocketAddress ip){ + return ns.connect((InetSocketAddress)ip, this); + } + + void setOtherEnd(ChannelSimulator

other){ + otherEnd = other; + } + + public ChannelSimulator

accept(){ + return null; + } + + public boolean isAcceptable() { + return false; + } + public synchronized boolean isReadable() { + return !readQ.isEmpty(); + } + + public boolean isWritable() { + return true; + } + + @Override + public SelectableChannel configureBlocking(boolean block) throws IOException { + isBlocking = block; + return this; + } + + + @SuppressWarnings("unchecked") + @Override + public SelectionKey keyFor(Selector selector2) { + return ((SelectorSimulator) selector2).getKey(this); + } + + @SuppressWarnings("unchecked") + @Override + public SelectionKey keyFor(REPSelector sel) { + return (SelectionKey)((SelectorSimulator) sel).getKey(this); + } + + @SuppressWarnings("unchecked") + @Override + public REPSelectionKey

keyFor1(REPSelector

sel) { + return (REPSelectionKey

)((SelectorSimulator) sel).getKey(this); + } + + @SuppressWarnings("unchecked") + @Override + public SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException { + SelectorSimulator

sel1 = (SelectorSimulator

) sel; + selector = sel1; + key = sel1.register(this, ops, att); + return key; + } + + @SuppressWarnings("unchecked") + public SelectionKey register(REPSelector

sel, int ops, Object att) throws ClosedChannelException { + selector = sel; + key = (SelectionKeySimulator

) sel.register(this, ops, att); + return key; + } + + + public boolean finishConnect() throws IOException { + return otherEnd!=null; + } + + public Socket socket() { + assert(false); + return null; + } + + @Override + public String getLocalHostName() { + return "localhost"; // always... + } + + public String toString(){ + return "ChSim("+ownerName+")"; + } + + public ChannelSimulator

newChannel() { + return new ChannelSimulator

(); + } + + @SuppressWarnings("unchecked") + public void setOtherEnd1(ChannelSimulator other) { + otherEnd = (ChannelSimulator

) other; + } + + public void close1() throws IOException { + } +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/NetworkSimulator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/NetworkSimulator.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,113 @@ +package rep.channel; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.HashMap; +import java.util.LinkedList; + +public class NetworkSimulator { + // we don't use

because we need singleton. + public static NetworkSimulator ns = new NetworkSimulator(); + + public HashMapnamedb = new HashMap(); + public int ipcount = 1; + public REPLogger logger; + + public static NetworkSimulator singleton() { + // singleton pattern may used here, but it has a little cost. + return ns; + } + + int logLevel=5; + /** Listening Servers. */ + private LinkedList serverList; + + /** Constructor. */ + public NetworkSimulator(){ + serverList = new LinkedList(); + logger = REPLogger.singleton(); + logger.writeLog("construct Networksimulator", 1); + // printAllState(); + } + + /* */ + synchronized public void listen(InetSocketAddress ip, ServerChannelSimulator scs) { + serverList.add(new ServerData(ip, scs)); + logger.writeLog("listen", 1); + printAllState(); + } + + synchronized public boolean connect(InetSocketAddress ip, ChannelSimulator CHclient) { + logger.writeLog("connecting..", 1); + for (ServerData sd0: serverList){ + // ANY address (0.0.0.0/0.0.0.0) should be considered. + if (sd0.IP.getAddress().isAnyLocalAddress()) { + if (sd0.IP.getPort() != ip.getPort()) continue; + // we have to check, ip is really reachable to sd0 server, + // but this simulator has no notion of host. To distinguish, + // use different port address. + } else if (!sd0.IP.equals(ip)) continue; + + //ChannelSimulator CHserver = new ChannelSimulator(); + ChannelSimulator CHserver = CHclient.newChannel(); + CHserver.setOtherEnd1(CHclient); + CHclient.setOtherEnd1(CHserver); + + sd0.connectedListS.add(CHserver); + sd0.connectedListC.add(CHclient); + sd0.scs.enQ(CHserver); + + logger.writeLog("connected", 1); + //printAllState(); + return true; + } + return false; + } + + /** for DEBUG methods. */ + void printAllState(){ + String log = "NetworkSimulator State:"; + for (ServerData sd: serverList){ + log += "\tSessionManager(ip="+sd.IP.toString()+"): "; + log += channelList(sd.connectedListC); + } + logger.writeLog(log); + } + + private String channelList(LinkedList> list){ + String tmp = ""; + for (ChannelSimulator ch: list){ + tmp += ch.toString()+" "; + } + return "\t"+tmp; + } + + + + public synchronized int nslookup(SocketAddress semaIP) { + Integer ip; + if ((ip=namedb.get(semaIP))==null) { + namedb.put(semaIP, (ip=ipcount++)); + } + return ip; + } + + +} + +class ServerData { + //int virtualIP; + InetSocketAddress IP; + //SelectorSimulator

selector; + ServerChannelSimulator scs; + LinkedList> connectedListS; + LinkedList> connectedListC; + + ServerData(InetSocketAddress ip, ServerChannelSimulator _scs){ + IP = ip; + //selector = _selector; + scs = _scs; + connectedListS = new LinkedList>(); + connectedListC = new LinkedList>(); + } +} \ No newline at end of file diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/NullSelector.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/NullSelector.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,56 @@ +package rep.channel; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.SelectorProvider; +import java.util.Set; + +public class NullSelector extends Selector { + + @Override + public void close() throws IOException { + + } + + @Override + public boolean isOpen() { + return false; + } + + @Override + public Set keys() { + return null; + } + + @Override + public SelectorProvider provider() { + return null; + } + + @Override + public int select() throws IOException { + return 0; + } + + @Override + public int select(long timeout) throws IOException { + return 0; + } + + @Override + public int selectNow() throws IOException { + return 0; + } + + @Override + public Set selectedKeys() { + return null; + } + + @Override + public Selector wakeup() { + return this; + } + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/REPLogger.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/REPLogger.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,33 @@ +package rep.channel; + +public class REPLogger { + static REPLogger single = new REPLogger(); + + public static REPLogger singleton(){ +// if(single==null){ +// synchronized(REPLogger.class){ +// if(single==null) +// single = new REPLogger(); +// } +// } + return single; + } + protected REPLogger(){ + } + + private int logLevel; + /** simulation log command */ + public void writeLog(String log, int level){ + if ( level<=logLevel ) + System.out.println(Thread.currentThread().getName()+": "+log); + //System.out.flush(); + } + public void writeLog(String log){ + writeLog(log, 0); + } + + public void setLogLevel(int logLevel) { + this.logLevel = logLevel; + } + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/REPPack.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/REPPack.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,14 @@ +package rep.channel; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; + +public interface REPPack

{ + + public ByteBuffer packUConv(P command); + + public P unpackUConv(SocketChannel sc) throws IOException ; + + +} \ No newline at end of file diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/REPSelectionKey.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/REPSelectionKey.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,92 @@ +package rep.channel; + +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + + +public class REPSelectionKey

extends SelectionKey { + SelectionKey key; + private REPSelector

selector; + + public REPSelectionKey() { + + } + + public REPSelectionKey(SelectionKey key,REPSelector

s) { + this.key = key; + this.selector = s; + attach(key.attachment()); + } + + @Override + public void cancel() { + key.cancel(); + } + + @Override + public SelectableChannel channel() { + if (REPServerSocketChannel.isSimulation) return key.channel(); + SelectableChannel sc = key.channel(); + SelectableChannel rsc = REPSocketChannel.channels.get(sc); + return rsc; + } + + @SuppressWarnings("unchecked") + public REPSocketChannel

channel1() { + assert (!REPServerSocketChannel.isSimulation); + SelectableChannel sc = key.channel(); + REPSocketChannel

rsc = (REPSocketChannel

) REPSocketChannel.channels.get(sc); + return rsc; + } + + @SuppressWarnings("unchecked") + public REPServerSocketChannel

serverSocketChannel() { + assert (!REPServerSocketChannel.isSimulation); + SelectableChannel sc = key.channel(); + REPServerSocketChannel

rsc = (REPServerSocketChannel

) REPSocketChannel.channels.get(sc); + return rsc; + } + + @Override + public int interestOps() { + return key.interestOps(); + } + + @Override + public SelectionKey interestOps(int ops) { + return key.interestOps(ops); + } + + @Override + public boolean isValid() { + return key.isValid(); + } + + @Override + public int readyOps() { + return key.readyOps(); + } + + @Override + public Selector selector() { + return selector; + } + + @SuppressWarnings("unchecked") + public REPSocketChannel

accept(REPPack

pack) throws IOException { + assert(!REPServerSocketChannel.isSimulation); + if (!key.isAcceptable()) throw new IOException(); + ServerSocketChannel ssc = (ServerSocketChannel)key.channel(); + if (ssc==null) return null; + SocketChannel ss = (SocketChannel)ssc.accept(); + //System.err.println("Accept in SelectionKey "+ss); + if (ss==null) return null; + return new REPSocketChannel(ss, pack); + } + + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/REPSelector.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/REPSelector.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,94 @@ +package rep.channel; + +import java.io.IOException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.spi.SelectorProvider; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + + +public class REPSelector

extends Selector{ + + Selector selector; + + public REPSelector(AbstractSelector selector) { + this.selector = selector; + } + + public static REPSelector create() throws IOException{ + if(REPServerSocketChannel.isSimulation){ + return new SelectorSimulator(); + } + return new REPSelector(SelectorProvider.provider().openSelector()); + } + + @Override + public void close() throws IOException { + selector.close(); + } + + @Override + public boolean isOpen() { + return selector.isOpen(); + } + + @Override + public Set keys() { + return selector.keys(); + } + + @Override + public SelectorProvider provider() { + return selector.provider(); + } + + @Override + public int select() throws IOException { + return selector.select(); + } + + @Override + public int select(long timeout) throws IOException { + return selector.select(timeout); + } + + @Override + public int selectNow() throws IOException { + return selector.selectNow(); + } + + @Override + public Set selectedKeys() { + return selector.selectedKeys(); + } + + @Override + public Selector wakeup() { + return selector.wakeup(); + } + + public SelectionKey register(SelectableChannel ch, int ops, Object att) throws ClosedChannelException{ + return ch.register(selector, ops, att); + } + + public Set> selectedKeys1() { + Set keys = selector.selectedKeys(); + //System.err.println("Selected keys = "+keys); + HashSet> newKeys = new HashSet>(); + + for (Iterator it = keys.iterator();it.hasNext(); ) { + SelectionKey k = it.next(); + newKeys.add(new REPSelectionKey

(k,this)); + it.remove(); + } + return newKeys; + } + + + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/REPServerSocket.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/REPServerSocket.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,62 @@ +package rep.channel; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.SocketAddress; +import java.nio.channels.ServerSocketChannel; + + +/* こいつはシミュレーションの時しか生成され無いゾ */ +public class REPServerSocket extends ServerSocket{ + ServerChannelSimulator scs; + + public REPServerSocket(ServerChannelSimulator channel) throws IOException { + scs = channel; + } + + public void bind(SocketAddress ip){ + scs.bind((InetSocketAddress)ip); + } + + @Override + public void close(){ + } + @Override + public ServerSocketChannel getChannel(){ + return null; + } + @Override + public InetAddress getInetAddress(){ + return null; + } + @Override + public int getLocalPort(){ + return 0; + } + @Override + public SocketAddress getLocalSocketAddress(){ + return null; + } + @Override + public int getReceiveBufferSize(){ + return 0; + } + @Override + public boolean getReuseAddress(){ + return false; + } + @Override + public int getSoTimeout(){ + return 0; + } + /* + @Override + public (){ + } + @Override + public (){ + }*/ + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/REPServerSocketChannel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/REPServerSocketChannel.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,134 @@ +package rep.channel; + +import java.io.IOException; +import java.net.ServerSocket; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; + +/* + * シミュレーションでは inheritance のServerChannelSimulator を生成、 + * リアルコミュニケーションでは 自身を生成、内部にもつ ServerSocketChannelを扱う + */ +public class REPServerSocketChannel

extends SelectableChannel { + + public static boolean isSimulation=false; + private ServerSocketChannel ssc; + private REPPack

packer; + + public REPServerSocketChannel() { + + } + + public static REPServerSocketChannel open(REPPack packer) throws IOException{ + if(isSimulation){ + return new ServerChannelSimulator(); + }else{ + return new REPServerSocketChannel(ServerSocketChannel.open(), packer); + } + } + + public static REPServerSocketChannel open(SelectableChannel c,REPPack packer) throws IOException{ + assert(!isSimulation); + return new REPServerSocketChannel((ServerSocketChannel)c, packer); + } + + public REPServerSocketChannel(ServerSocketChannel open, REPPack

packer) { + ssc = open; + this.packer = packer; + REPSocketChannel.addChannel(ssc,this); + } + + public void close1() throws IOException { + REPSocketChannel.removeChannel(ssc); + ssc.close(); + } + + public REPServerSocketChannel(SelectableChannel channel, REPPack

packer) { + this.ssc = (ServerSocketChannel)channel; + this.packer = packer; + REPSocketChannel.addChannel(ssc,this); + } + + public REPSocketChannel

accept1() throws IOException { + return new REPSocketChannel

(ssc.accept(), packer); + } + + public SelectionKey register(REPSelector

sel, int ops, Object att) throws ClosedChannelException { + assert(!isSimulation); + if(sel!=null) + return sel.register(ssc, ops, att); + else + return null; + } + + public SocketChannel accept() throws IOException { + return accept1().sc; + } + + + public ServerSocket socket() { + return ssc.socket(); + } + + public SelectableChannel configureBlocking(boolean block) throws IOException + { + ssc.configureBlocking(block); + return this; + } + + @Override + public Object blockingLock() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isBlocking() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isRegistered() { + // TODO Auto-generated method stub + return false; + } + + @Override + public SelectionKey keyFor(Selector sel) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SelectorProvider provider() { + // TODO Auto-generated method stub + return null; + } + + @Override + public SelectionKey register(Selector sel, int ops, Object att) + throws ClosedChannelException { + // TODO Auto-generated method stub + return null; + } + + @Override + public int validOps() { + // TODO Auto-generated method stub + return 0; + } + + @Override + protected void implCloseChannel() throws IOException { + // TODO Auto-generated method stub + + } + + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/REPSocketChannel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/REPSocketChannel.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,181 @@ +package rep.channel; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class REPSocketChannel

extends SelectableChannel{ + + public SocketChannel sc; + private REPPack

pack; + static public Map channels = + Collections.synchronizedMap(new HashMap()); + + public REPSocketChannel(SocketChannel channel, REPPack

packer) { + sc = channel; + pack = packer; + addChannel(sc,this); + } + + public REPSocketChannel(SelectableChannel channel, REPPack

packer) { + sc = (SocketChannel)channel; + pack = packer; + addChannel(sc,this); + } + + public static void addChannel(SelectableChannel sc,SelectableChannel rc) { + channels.put(sc, rc); + } + + public void close1() throws IOException { + removeChannel(sc); + sc.close(); + } + + public static void removeChannel(SelectableChannel sc) throws IOException { + if(channels.containsKey(sc)) channels.remove(sc); + } + + @Override + public Object blockingLock() { + return sc.blockingLock(); + } + + @Override + public SelectableChannel configureBlocking(boolean block) throws IOException { + return sc.configureBlocking(block); + } + + @Override + public boolean isBlocking() { + return sc.isBlocking(); + } + + @Override + public boolean isRegistered() { + return sc.isRegistered(); + } + + @Override + public SelectionKey keyFor(Selector sel) { + return sc.keyFor(sel); + } + + public SelectionKey keyFor(REPSelector sel) { + return sc.keyFor(sel.selector); + } + + public REPSelectionKey

keyFor1(REPSelector

sel) { + return new REPSelectionKey

(sc.keyFor(sel.selector), + new REPSelector

((AbstractSelector) sel.selector)); + } + + @Override + public SelectorProvider provider() { + return sc.provider(); + } + + + @Override + public int validOps() { + return sc.validOps(); + } + + @Override + protected void implCloseChannel() throws IOException { + close1(); + } + + + public int read(ByteBuffer header) throws IOException { + return sc.read(header); + } + + public void write(ByteBuffer buffer) throws IOException { + sc.write(buffer); + + } + + public boolean finishConnect() throws IOException { + return sc.finishConnect(); + } + + public Socket socket() { + return sc.socket(); + } + + public P read() throws IOException{ + return pack.unpackUConv(sc); + } + + public boolean write(P p){ + ByteBuffer bb = pack.packUConv(p); + if (bb==null) return true; + try { + while (bb.remaining() > 0 ){ + sc.write(bb); + } + return true; + } catch (IOException e) { + return false; + } + } + + public static REPSocketChannel create(REPPack packer) throws IOException { + if (REPServerSocketChannel.isSimulation) { + return new ChannelSimulator(); + } else { + REPSocketChannel rsc = new REPSocketChannel(SocketChannel.open(), packer); + return rsc; + } + } + + + public boolean connect(SocketAddress semaIP) throws IOException { + return sc.connect(semaIP); + } + + public SelectionKey register(REPSelector

sel, int ops, Object att) throws ClosedChannelException { + return sc.register(sel.selector, ops, att); + } + + + + public SelectionKey register1(REPSelector

sel, int ops, Object att) + throws ClosedChannelException { + if(sel instanceof REPSelector) { + REPSelector

s = (REPSelector

)sel; + return sc.register(s.selector, ops,att); + } + return sc.register(sel, ops,att); + } + + @SuppressWarnings("unchecked") + @Override + public SelectionKey register(Selector sel, int ops, Object att) + throws ClosedChannelException { + if(sel instanceof REPSelector) { + REPSelector

s = (REPSelector

)sel; + return sc.register(s.selector, ops,att); + } + return sc.register(sel, ops,att); + } + + public String getLocalHostName() { + return sc.socket().getLocalAddress().getHostName(); + + } + + +} \ No newline at end of file diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/SelectionKeySimulator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/SelectionKeySimulator.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,120 @@ +package rep.channel; + +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; + +public class SelectionKeySimulator

extends REPSelectionKey

{ + + private int interestOpt; + private SelectableChannel channel; + private int ready; + public Selector selector; + public boolean canceled = false; + + public SelectionKeySimulator(SelectableChannel cs, int opt, Selector _selector) { + channel = cs; + interestOpt = opt; + selector = _selector; + } + public SelectionKeySimulator(SelectionKey k) { + channel = k.channel(); + interestOpt = k.interestOps(); + selector = k.selector(); + attach(k.attachment()); + } + + + public boolean isAble() { + if ( (interestOpt&OP_READ)!=0 && isReadable() ) + return true; + else if( (interestOpt&OP_ACCEPT)!=0 && isAcceptable() ) + return true; + else if( (interestOpt&OP_WRITE)!=0 && isWritable() ) + return true; + else + return false; + } + + public void setFlag() { + ChannelSimulator scs = (ChannelSimulator) channel; + ready = 0; + if(scs.isAcceptable()) ready |= OP_ACCEPT; + if(scs.isReadable()) ready |= OP_READ; + if(scs.isWritable()) ready |= OP_WRITE; + } + + public SelectableChannel channel() { + return channel; + } + + @SuppressWarnings("unchecked") + @Override + public REPSocketChannel

channel1() { + return (REPSocketChannel

)channel; + } + + @SuppressWarnings("unchecked") + @Override + public REPServerSocketChannel

serverSocketChannel() { + return (REPServerSocketChannel

)channel; + } + + public SelectableChannel channel(REPPack

packer) { + return channel; + } + + @SuppressWarnings("unchecked") + public REPSocketChannel

accept(REPPack

pack) throws IOException { + assert(channel instanceof ServerChannelSimulator); + ServerChannelSimulator

scs = (ServerChannelSimulator

) channel; + return scs.accept1(); + } + + @SuppressWarnings("unchecked") + @Override + public void cancel() { + canceled = true; + SelectorSimulator s = (SelectorSimulator)selector; + s.deregister(channel); + } + + @Override + public int interestOps() { + return interestOpt; + } + + @Override + public SelectionKey interestOps(int ops) { + interestOpt = ops; + return this; + } + + @Override + public boolean isValid() { + return (!canceled) && channel.isOpen() && selector.isOpen(); + } + + + @Override + public Selector selector() { + return selector; + } + + @Override + public int readyOps() { + int ops=0; + if ( channel instanceof ServerChannelSimulator ){ + ServerChannelSimulator scs = (ServerChannelSimulator) channel; + ops = ( OP_ACCEPT * (scs.isAcceptable()? 1:0) ); + } else if ( channel instanceof ChannelSimulator ){ + ChannelSimulator scs = (ChannelSimulator) channel; + ops = ( OP_READ * (scs.isReadable()? 1:0) ) + | ( OP_WRITE * (scs.isWritable()? 1:0) ); + // (OP_READ & true) がつかえないらしい. + } + return ops; + } + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/SelectorSimulator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/SelectorSimulator.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,162 @@ +package rep.channel; + +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.SelectorProvider; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class SelectorSimulator

extends REPSelector

{ + + // This selector cannot be shared among threads. + + private Map> keyList; + private Set> selectedKeys; + private boolean isOpen=true; + + public SelectorSimulator() { + super(null); + keyList = new HashMap>(); + } + + public int select() throws IOException { + while(true) { + getSelectedKeys(); + if(selectedKeys.isEmpty()) { + try { + synchronized(this) { + wait(); + } + } catch (InterruptedException e) { + throw new IOException(); + } + } else + break; + } + return selectedKeys.size(); + } + + @Override + public int select(long timeout) throws IOException { + getSelectedKeys(); + if(selectedKeys.isEmpty()) { + try { + synchronized(this) { + wait(timeout); + } + // we cannot know if we time outed or not + getSelectedKeys(); + } catch (InterruptedException e) { + throw new IOException(); + } + } + return selectedKeys.size(); + } + + private void getSelectedKeys() { + selectedKeys = new HashSet>(); + for(SelectionKeySimulator

key : keyList.values()){ + if(key.isAble()) + selectedKeys.add(new SelectionKeySimulator

(key)); + } + } + + @Override + public int selectNow() throws IOException { + getSelectedKeys(); + return selectedKeys.size(); + } + + public SelectionKeySimulator

register(SelectableChannel cs, int opt){ + return register(cs, opt, null); + } + public SelectionKeySimulator

register(SelectableChannel cs, int opt, Object handler){ + SelectionKeySimulator

key = keyList.get(cs); + if (key!=null) { + key.attach(handler); + key.interestOps(opt); + return key; + } + key = new SelectionKeySimulator

(cs, opt, this); + key.attach(handler); + keyList.put(cs,key); + return key; + } + + public SelectionKeySimulator

deregister(SelectableChannel channel) { + SelectionKeySimulator

key = keyList.remove(channel); + return key; + } + + + public SelectionKey getKey(SelectableChannel channel){ + return keyList.get(channel); + } + + @Override + public void close() throws IOException { + isOpen = false; + } + + @Override + public boolean isOpen() { + return isOpen; + } + + @Override + public Set keys() { + Set newKeys = new HashSet(); + for(SelectionKey k: keyList.values()) { + newKeys.add(k); + } + return newKeys; + } + + public Set> keys1() { + // we cannot solve cast, we need the same method again with different types + Set> newKeys = new HashSet>(); + for(SelectionKeySimulator

k: keyList.values()) { + newKeys.add(k); + } + return newKeys; + } + + @Override + public SelectorProvider provider() { + // should return NetworkSimulator? + return null; + } + + + @Override + public synchronized Selector wakeup() { + notifyAll(); + return this; + } + + + public Set> selectedKeys1() { + Set> newKeys = new HashSet>(); + for(SelectionKeySimulator

k: selectedKeys) { + newKeys.add(k); + } + return newKeys; + } + + /* + * type safe copy of selectedKeys1() + */ + @Override + public Set selectedKeys() { + Set newKeys = new HashSet(); + for(SelectionKeySimulator

k: selectedKeys) { + newKeys.add(k); + } + return newKeys; + } + +} diff -r c921022bf494 -r 3133040ee4f4 src/rep/channel/ServerChannelSimulator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rep/channel/ServerChannelSimulator.java Wed Dec 31 15:06:22 2008 +0900 @@ -0,0 +1,146 @@ +package rep.channel; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.SelectorProvider; +import java.util.LinkedList; +import java.util.Queue; + +/* シミュレーションの際にコンストラクトされる REPServerSocketChannel の実装 */ +public class ServerChannelSimulator

extends REPServerSocketChannel

{ + protected NetworkSimulator ns; + //public REPServerSocket> socket; + protected InetSocketAddress IP; + protected Queue> acceptQ; + protected Selector selector; + protected boolean isBlocking; + private SelectionKeySimulator

key; + + /** Constructors. + * @throws IOException */ + public ServerChannelSimulator() throws IOException { + ns = NetworkSimulator.singleton(); + selector = new NullSelector(); // new Object(); + acceptQ = new LinkedList>(); + } + + public void bind(InetSocketAddress ip){ + IP = ip; + } + + public synchronized REPSocketChannel

accept1() throws IOException { + ChannelSimulator

channel; + while ( (channel=acceptQ.poll())==null && isBlocking ) { + try { + wait(); + } catch (InterruptedException e) { + throw new IOException(); + } + } + return channel; + } + + protected boolean enQ(ChannelSimulator

ch){ + // Don't lock a selector from a locked channel, the selector may + // use channel.isAble() which locks the channel. + synchronized(this) { + acceptQ.offer(ch); + notify(); + } + selector.wakeup(); + return true; + } + + @SuppressWarnings("unchecked") + public void enQ(ChannelSimulator hserver) { + // NetworkSimulator doesn't know P + ChannelSimulator

ch = (ChannelSimulator

) hserver; + enQ(ch); + } + + public ServerSocket socket() { + try { + return new REPServerSocket(this); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + + + @SuppressWarnings("unchecked") + public SelectionKey register(REPSelector

sel, int ops, Object att) throws ClosedChannelException { + selector = sel; + REPSelector

selector1 = sel; + ns.listen(IP, this); + key = (SelectionKeySimulator

) selector1.register(this, ops, att); + return key; + } + @SuppressWarnings("unchecked") + @Override + public SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException { + selector = sel; + REPSelector

selector1 = (REPSelector

)sel; + ns.listen(IP, this); // bindに移動してもいいよ + key = (SelectionKeySimulator

) selector1.register(this, ops, att); + return key; + } + + public synchronized boolean isAcceptable() { + return !acceptQ.isEmpty(); + } + + @Override + public Object blockingLock() { + return selector; + } + + public SelectableChannel configureBlocking(boolean block) throws IOException { + isBlocking = block; + return this; + } + + @Override + public boolean isBlocking() { + return isBlocking; + } + + @Override + public boolean isRegistered() { + // TODO Auto-generated method stub + return false; + } + + @Override + public SelectionKey keyFor(Selector sel) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SelectorProvider provider() { + // TODO Auto-generated method stub + return null; + } + + + @Override + public int validOps() { + // TODO Auto-generated method stub + return 0; + } + + @Override + protected void implCloseChannel() throws IOException { + // TODO Auto-generated method stub + + } + + +}