001/* 002 * $Id: DistributedListServer.java 5789 2018-01-04 21:38:39Z kredel $ 003 */ 004 005package edu.jas.util; 006 007import java.io.IOException; 008import java.io.Serializable; 009 010import java.util.Iterator; 011import java.util.List; 012import java.util.ArrayList; 013import java.util.SortedMap; 014import java.util.TreeMap; 015import java.util.Map.Entry; 016 017import org.apache.log4j.Logger; 018 019//import edu.unima.ky.parallel.ChannelFactory; 020//import edu.unima.ky.parallel.SocketChannel; 021 022 023/** 024 * Server for the distributed version of a list. 025 * @author Heinz Kredel 026 * TODO: redistribute list for late comming clients, removal of elements. 027 */ 028public class DistributedListServer extends Thread { 029 030 private static final Logger logger = Logger.getLogger(DistributedListServer.class); 031 032 public final static int DEFAULT_PORT = ChannelFactory.DEFAULT_PORT + 99; 033 034 035 protected final ChannelFactory cf; 036 037 protected List<Broadcaster> servers; 038 039 040 private volatile boolean goon = true; 041 042 private volatile Thread mythread = null; 043 044 045 private Counter listElem = null; 046 047 protected final SortedMap<Counter,Object> theList; 048 049 050 /** 051 * Constructs a new DistributedListServer. 052 */ 053 054 public DistributedListServer() { 055 this(DEFAULT_PORT); 056 } 057 058 /** 059 * DistributedListServer. 060 * @param port to run server on. 061 */ 062 public DistributedListServer(int port) { 063 this( new ChannelFactory(port) ); 064 } 065 066 /** 067 * DistributedListServer. 068 * @param cf ChannelFactory to use. 069 */ 070 public DistributedListServer(ChannelFactory cf) { 071 listElem = new Counter(0); 072 this.cf = cf; 073 cf.init(); 074 servers = new ArrayList<Broadcaster>(); 075 theList = new TreeMap<Counter,Object>(); 076 } 077 078 079 /** 080 * main. 081 * Usage: DistributedListServer <port> 082 */ 083 public static void main(String[] args) throws InterruptedException { 084 int port = DEFAULT_PORT; 085 if ( args.length < 1 ) { 086 System.out.println("Usage: DistributedListServer <port>"); 087 } else { 088 try { 089 port = Integer.parseInt( args[0] ); 090 } catch (NumberFormatException e) { 091 } 092 } 093 DistributedListServer dls = new DistributedListServer(port); 094 dls.init(); 095 dls.join(); 096 // until CRTL-C 097 } 098 099 100 /** 101 * thread initialization and start. 102 */ 103 public void init() { 104 this.start(); 105 } 106 107 108 /** 109 * main server method. 110 */ 111 @Override 112 public void run() { 113 SocketChannel channel = null; 114 Broadcaster s = null; 115 mythread = Thread.currentThread(); 116 Entry e; 117 Object n; 118 Object o; 119 while (goon) { 120 // logger.debug("list server " + this + " go on"); 121 try { 122 channel = cf.getChannel(); 123 logger.debug("dls channel = "+channel); 124 if ( mythread.isInterrupted() ) { 125 goon = false; 126 //logger.info("list server " + this + " interrupted"); 127 } else { 128 s = new Broadcaster(channel,servers,listElem,theList); 129 int ls = 0; 130 synchronized (servers) { 131 servers.add( s ); 132 ls = theList.size(); 133 s.start(); 134 } 135 //logger.debug("server " + s + " started"); 136 if ( ls > 0 ) { 137 logger.info("sending " + ls + " list elements"); 138 synchronized (theList) { 139 Iterator it = theList.entrySet().iterator(); 140 for ( int i = 0; i < ls; i++ ) { 141 e = (Entry)it.next(); 142 n = e.getKey(); 143 o = e.getValue(); 144 try { 145 s.sendChannel( n,o ); 146 } catch (IOException ioe) { 147 // stop s 148 } 149 } 150 } 151 } 152 } 153 } catch (InterruptedException end) { 154 goon = false; 155 Thread.currentThread().interrupt(); 156 } 157 } 158 //logger.debug("listserver " + this + " terminated"); 159 } 160 161 162 /** 163 * terminate all servers. 164 */ 165 public void terminate() { 166 goon = false; 167 logger.debug("terminating ListServer"); 168 if ( cf != null ) cf.terminate(); 169 if ( servers != null ) { 170 Iterator it = servers.iterator(); 171 while ( it.hasNext() ) { 172 Broadcaster br = (Broadcaster) it.next(); 173 br.closeChannel(); 174 try { 175 while ( br.isAlive() ) { 176 //System.out.print("."); 177 br.interrupt(); 178 br.join(100); 179 } 180 //logger.debug("server " + br + " terminated"); 181 } catch (InterruptedException e) { 182 Thread.currentThread().interrupt(); 183 } 184 } 185 servers = null; 186 } 187 logger.debug("Broadcasters terminated"); 188 if ( mythread == null ) return; 189 try { 190 while ( mythread.isAlive() ) { 191 // System.out.print("-"); 192 mythread.interrupt(); 193 mythread.join(100); 194 } 195 //logger.debug("server " + mythread + " terminated"); 196 } catch (InterruptedException e) { 197 Thread.currentThread().interrupt(); 198 } 199 mythread = null; 200 logger.debug("ListServer terminated"); 201 } 202 203 204 /** 205 * number of servers. 206 */ 207 public int size() { 208 if ( servers == null ) { 209 return -1; 210 } 211 return servers.size(); 212 } 213 214} 215 216 217/** 218 * Class for holding the list index used as key in TreeMap. 219 * Implemented since Integer has no add() method. 220 * Must implement Comparable so that TreeMap works with correct ordering. 221 */ 222 223class Counter implements Serializable, Comparable<Counter> { 224 225 private int value; 226 227 228 /** 229 * Counter. 230 */ 231 public Counter() { 232 this(0); 233 } 234 235 236 /** 237 * Counter. 238 * @param v 239 */ 240 public Counter(int v) { 241 value = v; 242 } 243 244 245 /** 246 * intValue. 247 * @return the value. 248 */ 249 public int intValue() { 250 return value; 251 } 252 253 254 /** 255 * add. 256 * @param v 257 */ 258 public void add(int v) { // synchronized elsewhere 259 value += v; 260 } 261 262 263 /** 264 * equals. 265 * @param ob an Object. 266 * @return true if this is equal to o, else false. 267 */ 268 @Override 269 public boolean equals(Object ob) { 270 if ( ! (ob instanceof Counter) ) { 271 return false; 272 } 273 return 0 == compareTo( (Counter)ob ); 274 } 275 276 277 /** 278 * compareTo. 279 * @param c a Counter. 280 * @return 1 if (this < c), 0 if (this == c), -1 if (this > c). 281 */ 282 public int compareTo(Counter c) { 283 int x = c.intValue(); 284 if ( value > x ) { 285 return 1; 286 } 287 if ( value < x ) { 288 return -1; 289 } 290 return 0; 291 } 292 293 294 /** 295 * Hash code for this Counter. 296 * @see java.lang.Object#hashCode() 297 */ 298 @Override 299 public int hashCode() { 300 return value; 301 } 302 303 304 /** 305 * toString. 306 */ 307 @Override 308 public String toString() { 309 return "Counter("+value+")"; 310 } 311 312} 313 314 315/** 316 * Thread for broadcasting all incoming objects to the list clients. 317 */ 318 319class Broadcaster extends Thread /*implements Runnable*/ { 320 321 private static final Logger logger = Logger.getLogger(Broadcaster.class); 322 private final SocketChannel channel; 323 private final List bcaster; 324 private Counter listElem; 325 private final SortedMap<Counter,Object> theList; 326 327 328 /** 329 * Broadcaster. 330 * @param s SocketChannel to use. 331 * @param p list of broadcasters. 332 * @param le counter 333 * @param sm SortedMap with counter value pairs. 334 */ 335 public Broadcaster(SocketChannel s, List p, Counter le, SortedMap<Counter,Object> sm) { 336 channel = s; 337 bcaster = p; 338 listElem = le; 339 theList = sm; 340 } 341 342 343 /** 344 * closeChannel. 345 */ 346 public void closeChannel() { 347 channel.close(); 348 } 349 350 351 /** 352 * sendChannel. 353 * @param n counter. 354 * @param o value. 355 * @throws IOException 356 */ 357 public void sendChannel(Object n, Object o) throws IOException { 358 synchronized (channel) { 359 channel.send(n); 360 channel.send(o); 361 } 362 } 363 364 365 /** 366 * broadcast. 367 * @param o object to store and send. 368 */ 369 public void broadcast(Object o) { 370 Counter li = null; 371 synchronized (listElem) { 372 listElem.add(1); 373 li = new Counter( listElem.intValue() ); 374 } 375 synchronized (theList) { 376 theList.put( li, o ); 377 } 378 synchronized (bcaster) { 379 Iterator it = bcaster.iterator(); 380 while ( it.hasNext() ) { 381 Broadcaster br = (Broadcaster) it.next(); 382 try { 383 br.sendChannel(li,o); 384 //System.out.println("bcast: "+o+" to "+x.channel); 385 } catch (IOException e) { 386 try { 387 br.closeChannel(); 388 while ( br.isAlive() ) { 389 br.interrupt(); 390 br.join(100); 391 } 392 } catch (InterruptedException u) { 393 Thread.currentThread().interrupt(); 394 } 395 bcaster.remove( br ); 396 } 397 } 398 } 399 } 400 401 402 /** 403 * run. 404 */ 405 @Override 406 public void run() { 407 Object o; 408 boolean goon = true; 409 while (goon) { 410 try { 411 o = channel.receive(); 412 //System.out.println("receive: "+o+" from "+channel); 413 broadcast(o); 414 if ( this.isInterrupted() ) { 415 goon = false; 416 } 417 } catch (IOException e) { 418 goon = false; 419 } catch (ClassNotFoundException e) { 420 goon = false; 421 e.printStackTrace(); 422 423 } 424 } 425 logger.debug("broadcaster terminated "+this); 426 channel.close(); 427 } 428 429 430 /** 431 * toString. 432 * @return a string representation of this. 433 */ 434 @Override 435 public String toString() { 436 return "Broadcaster("+channel+","+bcaster.size()+","+listElem+")"; 437 } 438 439}