package thaw.fcp; import java.util.Observable; import java.util.Observer; import thaw.core.Logger; /** * http://wiki.freenetproject.org/FreenetFCPSpec2Point0 * See "ClientHello" and "NodeHello". * Note: This query disconnect you if node answer CloseConnectionDuplicateClientName * and start() returns false. */ public class FCPClientHello implements FCPQuery, Observer { public final static int NODEHELLO_TIMEOUT = 60; /* in seconds */ private final static String FCP_EXPECTED_VERSION = "2.0"; private String id; private String connectionId; private String nodeFCPVersion; private String nodeVersion; private String nodeName = null; private boolean testnet = false; /* Hmm, in fact, we shouldn't have to bother about this one */ private int nmbCompressionCodecs = -1; private String[] codecs; private boolean receiveAnswer = false; private final FCPQueryManager queryManager; /** * Need to know the id of the application (see FCP specs). */ public FCPClientHello(final FCPQueryManager queryManager, final String id) { this.id = id; this.queryManager = queryManager; } public void setID(final String id) { this.id = id; } public String getNodeFCPVersion() { return nodeFCPVersion; } public String getNodeVersion() { return nodeVersion; } public String getNodeName() { return nodeName; } public boolean isOnTestnet() { return testnet; } public int getNmbCompressionCodecs() { return nmbCompressionCodecs; } /** * Warning: This query is blocking (only this one) ! * @param queueManager always null */ public boolean start(final FCPQueueManager queueManager) { queryManager.getConnection().registerClientHello(this); final FCPMessage message = new FCPMessage(); message.setMessageName("ClientHello"); message.setValue("Name", id); message.setValue("ExpectedVersion", FCPClientHello.FCP_EXPECTED_VERSION); queryManager.addObserver(this); if(!queryManager.writeMessage(message)) { Logger.warning(this, "Unable to say hello ... ;("); return false; } int count = 0; while(!receiveAnswer && count < (NODEHELLO_TIMEOUT*2)) { try { Thread.sleep(500); } catch(final java.lang.InterruptedException e) { /* \_o< */ } count++; } if(nodeName != null) { Logger.info(this, "Hello "+nodeName+", I'm Thaw :)"); } else if (count >= (NODEHELLO_TIMEOUT*2)) { Logger.warning(this, "Unable to connect, timeout ..."); return false; } else { Logger.warning(this, "ID already used !"); return false; } return true; } public void update(final Observable o, final Object arg) { if(arg == null) return; final FCPMessage answer = (FCPMessage)arg; if(o == queryManager) { if("NodeHello".equals( answer.getMessageName() )) { Logger.info(this, "Received a nodeHello"); connectionId = answer.getValue("ConnectionIdentifier"); if (connectionId == null) Logger.error(this, "** no connection identifier **"); nodeFCPVersion = answer.getValue("FCPVersion"); nodeVersion = answer.getValue("Version"); nodeName = answer.getValue("Node"); testnet = Boolean.valueOf(answer.getValue("Testnet")).booleanValue(); codecs = answer.getValue("CompressionCodecs").split(" "); nmbCompressionCodecs = Integer.parseInt(codecs[0]); queryManager.deleteObserver(this); receiveAnswer = true; synchronized(this) { notify(); } } if("CloseConnectionDuplicateClientName".equals( answer.getMessageName() )) { /* Damn ... ! */ Logger.warning(this, "According to the node, Thaw ID is already used. Please change it in the configuration (in advanced mode)"); queryManager.deleteObserver(this); receiveAnswer = true; synchronized(this) { notify(); } } } if(!receiveAnswer) { Logger.warning(this, "This message wasn't for us ?! : "+answer.getMessageName()); } } /** * Not used. */ public boolean stop(final FCPQueueManager queueManager) { return true; } public int getQueryType() { return 0; } public String getConnectionId() { return connectionId; } public String getCodec(int i) { if(i < nmbCompressionCodecs) { return codecs[i+2].split("\\(")[0]; } else { return ""; } } }