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 &lt;port&gt;
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 &lt; c), 0 if (this == c), -1 if (this &gt; 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}