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 &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 = 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}