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 &lt;port&gt;
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}