001/* 002 * $Id$ 003 */ 004 005package edu.jas.util; 006 007 008import java.io.IOException; 009import java.util.ArrayList; 010import java.util.Iterator; 011import java.util.List; 012import java.util.Map.Entry; 013import java.util.SortedMap; 014import java.util.TreeMap; 015 016import org.apache.logging.log4j.LogManager; 017import org.apache.logging.log4j.Logger; 018 019 020/** 021 * Server for the distributed version of a list. TODO: redistribute list for 022 * late coming clients, removal of elements. 023 * @author Heinz Kredel 024 */ 025 026public class DistHashTableServer<K> extends Thread { 027 028 029 private static final Logger logger = LogManager.getLogger(DistHashTableServer.class); 030 031 032 public final static int DEFAULT_PORT = 9009; //ChannelFactory.DEFAULT_PORT + 99; 033 034 035 protected final ChannelFactory cf; 036 037 038 protected List<DHTBroadcaster<K>> servers; 039 040 041 private volatile boolean goon = true; 042 043 044 private volatile Thread mythread = null; 045 046 047 protected final SortedMap<K, DHTTransport> theList; 048 049 050 private long etime; 051 052 053 private long dtime; 054 055 056 private long ertime; 057 058 059 private long drtime; 060 061 062 /** 063 * Constructs a new DistHashTableServer. 064 */ 065 public DistHashTableServer() { 066 this(DEFAULT_PORT); 067 } 068 069 070 /** 071 * DistHashTableServer. 072 * @param port to run server on. 073 */ 074 public DistHashTableServer(int port) { 075 this(new ChannelFactory(port)); 076 } 077 078 079 /** 080 * DistHashTableServer. 081 * @param cf ChannelFactory to use. 082 */ 083 public DistHashTableServer(ChannelFactory cf) { 084 this.cf = cf; 085 cf.init(); 086 servers = new ArrayList<DHTBroadcaster<K>>(); 087 theList = new TreeMap<K, DHTTransport>(); 088 etime = DHTTransport.etime; 089 dtime = DHTTransport.dtime; 090 ertime = DHTTransport.ertime; 091 drtime = DHTTransport.drtime; 092 } 093 094 095 /** 096 * main. Usage: DistHashTableServer <port> 097 */ 098 public static void main(String[] args) throws InterruptedException { 099 int port = DEFAULT_PORT; 100 if (args.length < 1) { 101 System.out.println("Usage: DistHashTableServer <port>"); 102 } else { 103 try { 104 port = Integer.parseInt(args[0]); 105 } catch (NumberFormatException e) { 106 } 107 } 108 DistHashTableServer dhts = new DistHashTableServer/*raw: <K>*/(port); 109 dhts.init(); 110 dhts.join(); 111 // until CRTL-C 112 } 113 114 115 /** 116 * thread initialization and start. 117 */ 118 public void init() { 119 this.start(); 120 } 121 122 123 /** 124 * main server method. 125 */ 126 @Override 127 public void run() { 128 SocketChannel channel = null; 129 DHTBroadcaster<K> s = null; 130 mythread = Thread.currentThread(); 131 Entry<K, DHTTransport> e; 132 DHTTransport tc; 133 while (goon) { 134 //logger.debug("list server " + this + " go on"); 135 try { 136 channel = cf.getChannel(); 137 if (logger.isDebugEnabled()) { 138 logger.debug("dls channel = " + channel); 139 } 140 if (mythread.isInterrupted()) { 141 goon = false; 142 //logger.info("list server " + this + " interrupted"); 143 } else { 144 s = new DHTBroadcaster<K>(channel, servers, /*listElem,*/theList); 145 int ls = 0; 146 synchronized (servers) { 147 if (goon) { 148 servers.add(s); 149 ls = theList.size(); 150 s.start(); 151 } 152 } 153 if (logger.isDebugEnabled()) { 154 logger.info("server " + s + " started " + s.isAlive()); 155 } 156 if (ls > 0) { 157 //logger.debug("sending " + ls + " list elements"); 158 synchronized (theList) { 159 Iterator<Entry<K, DHTTransport>> it = theList.entrySet().iterator(); 160 for (int i = 0; i < ls; i++) { 161 e = it.next(); 162 // n = e.getKey(); // findbugs, already in tc 163 tc = e.getValue(); 164 //DHTTransport tc = (DHTTransport) o; 165 try { 166 s.sendChannel(tc); 167 } catch (IOException ioe) { 168 // stop s 169 } 170 } 171 } 172 } 173 } 174 } catch (InterruptedException end) { 175 goon = false; 176 Thread.currentThread().interrupt(); 177 } 178 } 179 if (logger.isDebugEnabled()) { 180 logger.info("DHTserver " + this + " terminated"); 181 } 182 } 183 184 185 /** 186 * terminate all servers. 187 */ 188 public void terminate() { 189 goon = false; 190 logger.info("terminating"); 191 if (cf != null) { 192 cf.terminate(); 193 } 194 int svs = 0; 195 List<DHTBroadcaster<K>> scopy = null; 196 if (servers != null) { 197 synchronized (servers) { 198 svs = servers.size(); 199 scopy = new ArrayList<DHTBroadcaster<K>>(servers); 200 Iterator<DHTBroadcaster<K>> it = scopy.iterator(); 201 while (it.hasNext()) { 202 DHTBroadcaster<K> br = it.next(); 203 br.goon = false; 204 br.closeChannel(); 205 try { 206 int c = 0; 207 while (br.isAlive()) { 208 c++; 209 if (c > 10) { 210 logger.warn("giving up on " + br); 211 break; 212 } 213 //System.out.print("."); 214 br.interrupt(); 215 br.join(50); 216 } 217 if (logger.isDebugEnabled()) { 218 logger.info("server+ " + br + " terminated"); 219 } 220 // now possible: 221 servers.remove(br); 222 } catch (InterruptedException e) { 223 Thread.currentThread().interrupt(); 224 } 225 } 226 servers.clear(); 227 } 228 logger.info("" + svs + " broadcasters terminated " + scopy); 229 //? servers = null; 230 } 231 logger.debug("DHTBroadcasters terminated"); 232 long enc = DHTTransport.etime - etime; 233 long dec = DHTTransport.dtime - dtime; 234 long encr = DHTTransport.ertime - ertime; 235 long decr = DHTTransport.drtime - drtime; 236 long drest = (encr * dec) / (enc + 1); 237 logger.info("DHT time: encode = " + enc + ", decode = " + dec + ", enc raw = " + encr 238 + ", dec raw wait = " + decr + ", dec raw est = " + drest + ", sum est = " 239 + (enc + dec + encr + drest)); // +decr not meaningful 240 if (mythread == null) { 241 return; 242 } 243 try { 244 while (mythread.isAlive()) { 245 //System.out.print("-"); 246 mythread.interrupt(); 247 mythread.join(100); 248 } 249 if (logger.isWarnEnabled()) { 250 logger.warn("server terminated " + mythread); 251 } 252 } catch (InterruptedException e) { 253 Thread.currentThread().interrupt(); 254 } 255 mythread = null; 256 logger.info("terminated"); 257 } 258 259 260 /** 261 * number of servers. 262 */ 263 public int size() { 264 if (servers == null) { 265 return -1; 266 } 267 //synchronized (servers) removed 268 return servers.size(); 269 } 270 271 272 /** 273 * toString. 274 * @return a string representation of this. 275 */ 276 @Override 277 public String toString() { 278 return "DHTServer(" + servers.size() + ", " + cf + ", " + super.toString() + ")"; 279 } 280 281} 282 283 284/** 285 * Thread for broadcasting all incoming objects to the list clients. 286 */ 287class DHTBroadcaster<K> extends Thread /*implements Runnable*/ { 288 289 290 private static final Logger logger = LogManager.getLogger(DHTBroadcaster.class); 291 292 293 private final SocketChannel channel; 294 295 296 private final List<DHTBroadcaster<K>> bcaster; 297 298 299 private final SortedMap<K, DHTTransport> theList; 300 301 302 volatile boolean goon = true; 303 304 305 /** 306 * DHTBroadcaster. 307 * @param s SocketChannel to use. 308 * @param bc list of broadcasters. 309 * @param sm SortedMap with key value pairs. 310 */ 311 public DHTBroadcaster(SocketChannel s, List<DHTBroadcaster<K>> bc, SortedMap<K, DHTTransport> sm) { 312 channel = s; 313 bcaster = bc; 314 theList = sm; 315 } 316 317 318 /** 319 * closeChannel. 320 */ 321 public void closeChannel() { 322 channel.close(); 323 } 324 325 326 /** 327 * sendChannel. 328 * @param tc DHTTransport. 329 * @throws IOException 330 */ 331 public void sendChannel(DHTTransport tc) throws IOException { 332 if (goon) { 333 channel.send(tc); 334 } 335 } 336 337 338 /** 339 * broadcast. 340 * @param o DHTTransport element to broadcast. 341 */ 342 @SuppressWarnings({ "unchecked", "cast" }) 343 public void broadcast(DHTTransport o) { 344 if (logger.isDebugEnabled()) { 345 logger.debug("broadcast = " + o); 346 } 347 DHTTransport<K, Object> tc = null; 348 if (o == null) { 349 return; 350 } 351 //if ( ! (o instanceof DHTTransport) ) { 352 // return; 353 //} 354 tc = (DHTTransport<K, Object>) o; 355 K key = null; 356 synchronized (theList) { 357 //test 358 //Object x = theList.get( tc.key ); 359 //if ( x != null ) { 360 // logger.info("theList duplicate key " + tc.key ); 361 //} 362 try { 363 if (!(o instanceof DHTTransportClear)) { 364 key = tc.key(); 365 theList.put(key, tc); 366 } 367 } catch (IOException e) { 368 logger.warn("IO exception: tc.key() not ok " + tc); 369 e.printStackTrace(); 370 } catch (ClassNotFoundException e) { 371 logger.warn("CNF exception: tc.key() not ok " + tc); 372 e.printStackTrace(); 373 } catch (Exception e) { 374 logger.warn("exception: tc.key() not ok " + tc); 375 e.printStackTrace(); 376 } 377 } 378 logger.info("sending key=" + key + " to " + bcaster.size() + " nodes"); 379 List<DHTBroadcaster<K>> bccopy = null; 380 synchronized (bcaster) { 381 bccopy = new ArrayList<DHTBroadcaster<K>>(bcaster); 382 } 383 Iterator<DHTBroadcaster<K>> it = bccopy.iterator(); 384 while (it.hasNext()) { 385 DHTBroadcaster<K> br = it.next(); 386 try { 387 if (logger.isDebugEnabled()) { 388 logger.debug("bcasting to " + br); 389 } 390 br.sendChannel(tc); 391 } catch (IOException e) { 392 logger.info("bcaster, IOexception " + e); 393 synchronized (bcaster) { 394 bcaster.remove(br); //no more: ConcurrentModificationException 395 } 396 try { 397 br.goon = false; 398 br.closeChannel(); 399 while (br.isAlive()) { 400 br.interrupt(); 401 br.join(100); 402 } 403 } catch (InterruptedException w) { 404 Thread.currentThread().interrupt(); 405 } 406 // 407 logger.info("bcaster.remove() " + br); 408 } catch (Exception e) { 409 logger.info("bcaster, exception " + e); 410 } 411 } 412 } 413 414 415 /** 416 * run. 417 */ 418 @Override 419 public void run() { 420 goon = true; 421 while (goon) { 422 try { 423 logger.debug("trying to receive"); 424 Object o = channel.receive(); 425 if (this.isInterrupted()) { 426 goon = false; 427 break; 428 } 429 if (logger.isDebugEnabled()) { 430 logger.debug("received = " + o); 431 } 432 if (!(o instanceof DHTTransport)) { 433 logger.warn("wrong object type: " + o); 434 goon = false; 435 break; //continue; 436 } 437 if (o instanceof DHTTransportClear) { 438 logger.info("receive, clear"); 439 synchronized (theList) { 440 theList.clear(); 441 theList.notifyAll(); 442 } 443 } 444 DHTTransport tc = (DHTTransport) o; 445 broadcast(tc); 446 if (this.isInterrupted()) { 447 goon = false; 448 } 449 } catch (IOException e) { 450 goon = false; 451 logger.info("receive, IO exception " + e); 452 //e.printStackTrace(); 453 } catch (ClassNotFoundException e) { 454 goon = false; 455 logger.info("receive, CNF exception " + e); 456 e.printStackTrace(); 457 } catch (Exception e) { 458 goon = false; 459 logger.info("receive, exception " + e); 460 e.printStackTrace(); 461 } 462 } 463 if (logger.isInfoEnabled()) { 464 logger.info("ending " + this); 465 } 466 synchronized (bcaster) { 467 bcaster.remove(this); 468 } 469 channel.close(); 470 } 471 472 473 /** 474 * toString. 475 * @return a string representation of this. 476 */ 477 @Override 478 public String toString() { 479 return "DHTBroadcaster(" + channel + "," + bcaster.size() + ")"; 480 } 481 482}