001/* 002 * $Id: DistHashTableServer.java 5789 2018-01-04 21:38:39Z kredel $ 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.log4j.Logger; 017 018 019/** 020 * Server for the distributed version of a list. 021 * TODO: redistribute list for late coming clients, removal 022 * of elements. 023 * @author Heinz Kredel 024 */ 025 026public class DistHashTableServer<K> extends Thread { 027 028 029 private static final Logger logger = Logger.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 = Logger.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 le DHTCounter. 310 * @param sm SortedMap with key value pairs. 311 */ 312 public DHTBroadcaster(SocketChannel s, List<DHTBroadcaster<K>> bc, SortedMap<K, DHTTransport> sm) { 313 channel = s; 314 bcaster = bc; 315 theList = sm; 316 } 317 318 319 /** 320 * closeChannel. 321 */ 322 public void closeChannel() { 323 channel.close(); 324 } 325 326 327 /** 328 * sendChannel. 329 * @param tc DHTTransport. 330 * @throws IOException 331 */ 332 public void sendChannel(DHTTransport tc) throws IOException { 333 if (goon) { 334 channel.send(tc); 335 } 336 } 337 338 339 /** 340 * broadcast. 341 * @param o DHTTransport element to broadcast. 342 */ 343 @SuppressWarnings({ "unchecked", "cast" }) 344 public void broadcast(DHTTransport o) { 345 if (logger.isDebugEnabled()) { 346 logger.debug("broadcast = " + o); 347 } 348 DHTTransport<K, Object> tc = null; 349 if (o == null) { 350 return; 351 } 352 //if ( ! (o instanceof DHTTransport) ) { 353 // return; 354 //} 355 tc = (DHTTransport<K, Object>) o; 356 K key = null; 357 synchronized (theList) { 358 //test 359 //Object x = theList.get( tc.key ); 360 //if ( x != null ) { 361 // logger.info("theList duplicate key " + tc.key ); 362 //} 363 try { 364 if (!(o instanceof DHTTransportClear)) { 365 key = tc.key(); 366 theList.put(key, tc); 367 } 368 } catch (IOException e) { 369 logger.warn("IO exception: tc.key() not ok " + tc); 370 e.printStackTrace(); 371 } catch (ClassNotFoundException e) { 372 logger.warn("CNF exception: tc.key() not ok " + tc); 373 e.printStackTrace(); 374 } catch (Exception e) { 375 logger.warn("exception: tc.key() not ok " + tc); 376 e.printStackTrace(); 377 } 378 } 379 logger.info("sending key=" + key + " to " + bcaster.size() + " nodes"); 380 List<DHTBroadcaster<K>> bccopy = null; 381 synchronized (bcaster) { 382 bccopy = new ArrayList<DHTBroadcaster<K>>(bcaster); 383 } 384 Iterator<DHTBroadcaster<K>> it = bccopy.iterator(); 385 while (it.hasNext()) { 386 DHTBroadcaster<K> br = it.next(); 387 try { 388 if (logger.isDebugEnabled()) { 389 logger.debug("bcasting to " + br); 390 } 391 br.sendChannel(tc); 392 } catch (IOException e) { 393 logger.info("bcaster, IOexception " + e); 394 synchronized (bcaster) { 395 bcaster.remove(br); //no more: ConcurrentModificationException 396 } 397 try { 398 br.goon = false; 399 br.closeChannel(); 400 while (br.isAlive()) { 401 br.interrupt(); 402 br.join(100); 403 } 404 } catch (InterruptedException w) { 405 Thread.currentThread().interrupt(); 406 } 407 // 408 logger.info("bcaster.remove() " + br); 409 } catch (Exception e) { 410 logger.info("bcaster, exception " + e); 411 } 412 } 413 } 414 415 416 /** 417 * run. 418 */ 419 @Override 420 public void run() { 421 goon = true; 422 while (goon) { 423 try { 424 logger.debug("trying to receive"); 425 Object o = channel.receive(); 426 if (this.isInterrupted()) { 427 goon = false; 428 break; 429 } 430 if (logger.isDebugEnabled()) { 431 logger.debug("received = " + o); 432 } 433 if (!(o instanceof DHTTransport)) { 434 logger.warn("wrong object type: " + o); 435 goon = false; 436 break; //continue; 437 } 438 if (o instanceof DHTTransportClear) { 439 logger.info("receive, clear"); 440 synchronized (theList) { 441 theList.clear(); 442 theList.notifyAll(); 443 } 444 } 445 DHTTransport tc = (DHTTransport) o; 446 broadcast(tc); 447 if (this.isInterrupted()) { 448 goon = false; 449 } 450 } catch (IOException e) { 451 goon = false; 452 logger.info("receive, IO exception " + e); 453 //e.printStackTrace(); 454 } catch (ClassNotFoundException e) { 455 goon = false; 456 logger.info("receive, CNF exception " + e); 457 e.printStackTrace(); 458 } catch (Exception e) { 459 goon = false; 460 logger.info("receive, exception " + e); 461 e.printStackTrace(); 462 } 463 } 464 if (logger.isInfoEnabled()) { 465 logger.info("ending " + this); 466 } 467 synchronized (bcaster) { 468 bcaster.remove(this); 469 } 470 channel.close(); 471 } 472 473 474 /** 475 * toString. 476 * @return a string representation of this. 477 */ 478 @Override 479 public String toString() { 480 return "DHTBroadcaster(" + channel + "," + bcaster.size() + ")"; 481 } 482 483}