001: // $Id: TotalOrder.java,v 1.13 2006/03/27 08:34:24 belaban Exp $
002:
003: package org.jgroups.demos;
004:
005: import org.jgroups.*;
006: import org.jgroups.util.Util;
007:
008: import java.awt.*;
009: import java.awt.event.ActionEvent;
010: import java.awt.event.ActionListener;
011: import java.awt.event.WindowAdapter;
012: import java.awt.event.WindowEvent;
013: import java.net.InetAddress;
014: import java.nio.ByteBuffer;
015:
016: /**
017: * Originally written to be a demo for TOTAL order (code to be written by a student). In the meantime,
018: * it evolved into a state transfer demo. All members maintain a shared matrix and continually
019: * broadcast changes to be applied to a randomly chosen field (e.g. multiplication of field with new
020: * value, division, addition, subtraction). Each member can be started independently (starts to
021: * broadcast update messages to all members). When "Stop" is pressed, a stop message is broadcast to
022: * all members, causing them to stop sending messages. The "Clear" button clears the shared state;
023: * "GetState" refreshes it from the shared group state (using the state transfer protocol).<p>If the
024: * demo is to be used to show TOTAL order, then the TOTAL protocol would have to be added to the
025: * stack.
026: *
027: * @author Bela Ban
028: */
029: public class TotalOrder extends Frame {
030: final Font def_font = new Font("Helvetica", Font.BOLD, 12);
031: final Font def_font2 = new Font("Helvetica", Font.PLAIN, 12);
032: MyCanvas canvas;
033: final MenuBar menubar = createMenuBar();
034: final Button start = new Button("Start");
035: final Button stop = new Button("Stop");
036: final Button clear = new Button("Clear");
037: final Button get_state = new Button("Get State");
038: final Button quit = new Button("Quit");
039: final Panel button_panel = new Panel();
040: SenderThread sender = null;
041: ReceiverThread receiver = null;
042: Channel channel;
043: Dialog error_dlg;
044: long timeout = 0;
045: int field_size = 0;
046: int num_fields = 0;
047: static final int x_offset = 30;
048: static final int y_offset = 40;
049: private int num = 0;
050:
051: private int num_additions = 0, num_subtractions = 0,
052: num_divisions = 0, num_multiplications = 0;
053:
054: void error(String s) {
055: System.err.println(s);
056: }
057:
058: class EventHandler extends WindowAdapter {
059: final Frame gui;
060:
061: public EventHandler(Frame g) {
062: gui = g;
063: }
064:
065: public void windowClosing(WindowEvent e) {
066: gui.dispose();
067: System.exit(0);
068: }
069: }
070:
071: class SenderThread extends Thread {
072: TotOrderRequest req;
073: boolean running = true;
074:
075: public void stopSender() {
076: running = false;
077: interrupt();
078: System.out.println("-- num_additions: " + num_additions
079: + "\n-- num_subtractions: " + num_subtractions
080: + "\n-- num_divisions: " + num_divisions
081: + "\n-- num_multiplications: "
082: + num_multiplications);
083: num_additions = num_subtractions = num_multiplications = num_divisions = 0;
084: }
085:
086: public void run() {
087: this .setName("SenderThread");
088:
089: byte[] buf;
090: int cnt = 0;
091: while (running) {
092: try {
093: req = createRandomRequest();
094: buf = req.toBuffer();
095: channel.send(new Message(null, null, buf));
096: System.out.print("-- num requests sent: " + cnt
097: + "\r");
098: if (timeout > 0)
099: Util.sleep(timeout);
100: cnt++;
101: if (num > 0 && cnt > num) {
102: running = false;
103: cnt = 0;
104: }
105: } catch (Exception e) {
106: error(e.toString());
107: return;
108: }
109: }
110: }
111: }
112:
113: class ReceiverThread extends Thread {
114: SetStateEvent set_state_evt;
115: boolean running = true;
116:
117: public void stopReceiver() {
118: running = false;
119: interrupt();
120: }
121:
122: public void run() {
123: this .setName("ReceiverThread");
124: Message msg;
125: Object o;
126: ByteBuffer buf;
127: TotOrderRequest req;
128: while (running) {
129: try {
130: o = channel.receive(0);
131: if (o instanceof Message) {
132: try {
133: msg = (Message) o;
134: req = new TotOrderRequest();
135: buf = ByteBuffer.wrap(msg.getBuffer());
136: req.init(buf);
137: processRequest(req);
138: } catch (Exception e) {
139: System.err.println(e);
140: }
141: } else if (o instanceof GetStateEvent) {
142: int[][] copy_of_state = canvas.getCopyOfState();
143: channel.returnState(Util
144: .objectToByteBuffer(copy_of_state));
145: } else if (o instanceof SetStateEvent) { // state was received, set it !
146: set_state_evt = (SetStateEvent) o;
147: canvas.setState(Util
148: .objectFromByteBuffer(set_state_evt
149: .getArg()));
150: } else if (o instanceof View)
151: System.out.println(o.toString());
152: } catch (ChannelClosedException closed) {
153: error("Channel has been closed; receiver thread quits");
154: return;
155: } catch (Exception e) {
156: error(e.toString());
157: return;
158: }
159: }
160: }
161: }
162:
163: void processRequest(TotOrderRequest req) throws Exception {
164: int x = req.x, y = req.y, val = req.val;
165:
166: if (req.type == TotOrderRequest.STOP) {
167: stopSender();
168: return;
169: }
170:
171: switch (req.type) {
172: case TotOrderRequest.ADDITION:
173: canvas.addValueTo(x, y, val);
174: num_additions++;
175: break;
176: case TotOrderRequest.SUBTRACTION:
177: canvas.subtractValueFrom(x, y, val);
178: num_subtractions++;
179: break;
180: case TotOrderRequest.MULTIPLICATION:
181: canvas.multiplyValueWith(x, y, val);
182: num_multiplications++;
183: break;
184: case TotOrderRequest.DIVISION:
185: canvas.divideValueBy(x, y, val);
186: num_divisions++;
187: break;
188: }
189: canvas.update();
190: }
191:
192: public TotalOrder(String title, long timeout, int num_fields,
193: int field_size, String props, int num) {
194: Dimension s;
195:
196: this .timeout = timeout;
197: this .num_fields = num_fields;
198: this .field_size = field_size;
199: this .num = num;
200: setFont(def_font);
201:
202: try {
203: channel = new JChannel(props);
204: channel.connect("TotalOrderGroup");
205: channel.getState(null, 8000);
206: } catch (Exception e) {
207: e.printStackTrace();
208: System.exit(-1);
209: }
210:
211: start.addActionListener(new ActionListener() {
212: public void actionPerformed(ActionEvent e) {
213: startSender();
214: }
215: });
216:
217: stop.addActionListener(new ActionListener() {
218: public void actionPerformed(ActionEvent e) {
219: try {
220: TotOrderRequest req = new TotOrderRequest(
221: TotOrderRequest.STOP, 0, 0, 0);
222: byte[] buf = req.toBuffer();
223: channel.send(new Message(null, null, buf));
224: } catch (Exception ex) {
225: }
226: }
227: });
228:
229: clear.addActionListener(new ActionListener() {
230: public void actionPerformed(ActionEvent e) {
231: canvas.clear();
232: }
233: });
234:
235: get_state.addActionListener(new ActionListener() {
236: public void actionPerformed(ActionEvent e) {
237: try {
238: boolean rc = channel.getState(null, 3000);
239: if (rc == false)
240: error("State could not be retrieved !");
241: } catch (Throwable t) {
242: error("exception fetching state: " + t);
243: }
244: }
245: });
246:
247: quit.addActionListener(new ActionListener() {
248: public void actionPerformed(ActionEvent e) {
249: channel.disconnect();
250: channel.close();
251: System.exit(0);
252: }
253: });
254:
255: setTitle(title);
256: addWindowListener(new EventHandler(this ));
257: setBackground(Color.white);
258: setMenuBar(menubar);
259:
260: setLayout(new BorderLayout());
261: canvas = new MyCanvas(num_fields, field_size, x_offset,
262: y_offset);
263:
264: add("Center", canvas);
265: button_panel.setLayout(new FlowLayout());
266: button_panel.setFont(def_font2);
267: button_panel.add(start);
268: button_panel.add(stop);
269: button_panel.add(clear);
270: button_panel.add(get_state);
271: button_panel.add(quit);
272: add("South", button_panel);
273:
274: s = canvas.getSize();
275: s.height += 100;
276: setSize(s);
277: startReceiver();
278: }
279:
280: void startSender() {
281: if (sender == null || !sender.isAlive()) {
282: sender = new SenderThread();
283: sender.start();
284: }
285: }
286:
287: void stopSender() {
288: if (sender != null) {
289: sender.stopSender();
290: sender = null;
291: }
292: }
293:
294: void startReceiver() {
295: if (receiver == null) {
296: receiver = new ReceiverThread();
297: receiver.setPriority(Thread.MAX_PRIORITY);
298: receiver.start();
299: }
300: }
301:
302: private MenuBar createMenuBar() {
303: MenuBar ret = new MenuBar();
304: Menu file = new Menu("File");
305: MenuItem quitm = new MenuItem("Quit");
306:
307: ret.setFont(def_font2);
308: ret.add(file);
309:
310: file.addSeparator();
311: file.add(quitm);
312:
313: quitm.addActionListener(new ActionListener() {
314: public void actionPerformed(ActionEvent e) {
315: System.exit(1);
316: }
317: });
318: return ret;
319: }
320:
321: private TotOrderRequest createRandomRequest() {
322: TotOrderRequest ret = null;
323: byte op_type = (byte) (((Math.random() * 10) % 4) + 1); // 1 - 4
324: int x = (int) ((Math.random() * num_fields * 2) % num_fields);
325: int y = (int) ((Math.random() * num_fields * 2) % num_fields);
326: int val = (int) ((Math.random() * num_fields * 200) % 10);
327:
328: ret = new TotOrderRequest(op_type, x, y, val);
329: return ret;
330: }
331:
332: public static void main(String[] args) {
333: TotalOrder g;
334: String arg;
335: long timeout = 200;
336: int num_fields = 3;
337: int field_size = 80;
338: String props = null;
339: int num = 0;
340:
341: props = "UDP(mcast_addr=239.10.10.10;mcast_port=7500;ip_ttl=8;"
342: + "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):"
343: + "PING(timeout=2000;num_initial_members=3):"
344: + "MERGE2(min_interval=5000;max_interval=10000):"
345: + "FD_SOCK:"
346: + "VERIFY_SUSPECT(timeout=1500):"
347: + "pbcast.NAKACK(gc_lag=50;retransmit_timeout=300,600,1200,2400,4800):"
348: + "UNICAST(timeout=5000):"
349: + "pbcast.STABLE(desired_avg_gossip=20000):"
350: + "FRAG(frag_size=4096;down_thread=false;up_thread=false):"
351: + "pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;"
352: + "shun=false;print_local_addr=true):"
353: + "pbcast.STATE_TRANSFER";
354:
355: for (int i = 0; i < args.length; i++) {
356: arg = args[i];
357: if ("-timeout".equals(arg)) {
358: timeout = Long.parseLong(args[++i]);
359: continue;
360: }
361: if ("-num_fields".equals(arg)) {
362: num_fields = Integer.parseInt(args[++i]);
363: continue;
364: }
365: if ("-field_size".equals(arg)) {
366: field_size = Integer.parseInt(args[++i]);
367: continue;
368: }
369: if ("-help".equals(arg)) {
370: System.out
371: .println("\nTotalOrder [-timeout <value>] [-num_fields <value>] "
372: + "[-field_size <value>] [-props <properties (can be URL)>] [-num <num requests>]\n");
373: return;
374: }
375: if ("-props".equals(arg)) {
376: props = args[++i];
377: continue;
378: }
379: if ("-num".equals(arg)) {
380: num = Integer.parseInt(args[++i]);
381: }
382: }
383:
384: try {
385: g = new TotalOrder("Total Order Demo on "
386: + InetAddress.getLocalHost().getHostName(),
387: timeout, num_fields, field_size, props, num);
388: g.setVisible(true);
389: } catch (Exception e) {
390: System.err.println(e);
391: }
392: }
393:
394: }
395:
396: class TotOrderRequest {
397: public static final byte STOP = 0;
398: public static final byte ADDITION = 1;
399: public static final byte SUBTRACTION = 2;
400: public static final byte MULTIPLICATION = 3;
401: public static final byte DIVISION = 4;
402: final static int SIZE = Global.BYTE_SIZE + Global.INT_SIZE * 3;
403:
404: public byte type = ADDITION;
405: public int x = 0;
406: public int y = 0;
407: public int val = 0;
408:
409: public TotOrderRequest() {
410: }
411:
412: TotOrderRequest(byte type, int x, int y, int val) {
413: this .type = type;
414: this .x = x;
415: this .y = y;
416: this .val = val;
417: }
418:
419: public String printType() {
420: switch (type) {
421: case STOP:
422: return "STOP";
423: case ADDITION:
424: return "ADDITION";
425: case SUBTRACTION:
426: return "SUBTRACTION";
427: case MULTIPLICATION:
428: return "MULTIPLICATION";
429: case DIVISION:
430: return "DIVISION";
431: default:
432: return "<unknown>";
433: }
434: }
435:
436: // public void writeExternal(ObjectOutput out) throws IOException {
437: // out.writeByte(type);
438: // out.writeInt(x);
439: // out.writeInt(y);
440: // out.writeInt(val);
441: // }
442: //
443: // public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
444: // type=in.readByte();
445: // x=in.readInt();
446: // y=in.readInt();
447: // val=in.readInt();
448: // }
449:
450: public byte[] toBuffer() {
451: ByteBuffer buf = ByteBuffer.allocate(SIZE);
452: buf.put(type);
453: buf.putInt(x);
454: buf.putInt(y);
455: buf.putInt(val);
456: return buf.array();
457: }
458:
459: public void init(ByteBuffer buf) {
460: type = buf.get();
461: x = buf.getInt();
462: y = buf.getInt();
463: val = buf.getInt();
464: }
465:
466: public String toString() {
467: return "[" + x + ',' + y + ": " + printType() + '(' + val
468: + ")]";
469: }
470: }
471:
472: class MyCanvas extends Canvas {
473: int field_size = 100;
474: int num_fields = 4;
475: int x_offset = 30;
476: int y_offset = 30;
477:
478: final Font def_font = new Font("Helvetica", Font.BOLD, 14);
479: int[][] array = null; // state
480:
481: Dimension off_dimension = null;
482: Image off_image = null;
483: Graphics off_graphics = null;
484: final Font def_font2 = new Font("Helvetica", Font.PLAIN, 12);
485: static final Color checksum_col = Color.blue;
486: int checksum = 0;
487:
488: public MyCanvas(int num_fields, int field_size, int x_offset,
489: int y_offset) {
490: this .num_fields = num_fields;
491: this .field_size = field_size;
492: this .x_offset = x_offset;
493: this .y_offset = y_offset;
494:
495: array = new int[num_fields][num_fields];
496: setBackground(Color.white);
497: setSize(2 * x_offset + num_fields * field_size + 30, y_offset
498: + num_fields * field_size + 50);
499:
500: for (int i = 0; i < num_fields; i++)
501: for (int j = 0; j < num_fields; j++)
502: array[i][j] = 0;
503: }
504:
505: public void setFieldSize(int fs) {
506: field_size = fs;
507: }
508:
509: public void setNumFields(int nf) {
510: num_fields = nf;
511: }
512:
513: public void setXOffset(int o) {
514: x_offset = o;
515: }
516:
517: public void setYOffset(int o) {
518: y_offset = o;
519: }
520:
521: public void addValueTo(int x, int y, int value) {
522: synchronized (array) {
523: array[x][y] += value;
524: repaint();
525: }
526: }
527:
528: public void subtractValueFrom(int x, int y, int value) {
529: synchronized (array) {
530: array[x][y] -= value;
531: repaint();
532: }
533: }
534:
535: public void multiplyValueWith(int x, int y, int value) {
536: synchronized (array) {
537: array[x][y] *= value;
538: repaint();
539: }
540: }
541:
542: public void divideValueBy(int x, int y, int value) {
543: if (value == 0)
544: return;
545: synchronized (array) {
546: array[x][y] /= value;
547: repaint();
548: }
549: }
550:
551: public void setValueAt(int x, int y, int value) {
552: synchronized (array) {
553: array[x][y] = value;
554: }
555: repaint();
556: }
557:
558: public int getValueAt(int x, int y) {
559: synchronized (array) {
560: return array[x][y];
561: }
562: }
563:
564: public void clear() {
565: synchronized (array) {
566: for (int i = 0; i < num_fields; i++)
567: for (int j = 0; j < num_fields; j++)
568: array[i][j] = 0;
569: checksum = checksum();
570: repaint();
571: }
572: }
573:
574: public int[][] getState() {
575: synchronized (array) {
576: return array;
577: }
578: }
579:
580: public int[][] getCopyOfState() {
581: int[][] retval = new int[num_fields][num_fields];
582:
583: synchronized (array) {
584: for (int i = 0; i < num_fields; i++)
585: System.arraycopy(array[i], 0, retval[i], 0, num_fields);
586: return retval;
587: }
588: }
589:
590: public void update() {
591: checksum = checksum();
592: repaint();
593: }
594:
595: public void setState(Object new_state) {
596:
597: if (new_state == null)
598: return;
599:
600: try {
601: int[][] new_array = (int[][]) new_state;
602: synchronized (array) {
603: clear();
604:
605: for (int i = 0; i < num_fields; i++)
606: System.arraycopy(new_array[i], 0, array[i], 0,
607: num_fields);
608: checksum = checksum();
609: repaint();
610: }
611: } catch (Exception e) {
612: System.err.println(e);
613: return;
614: }
615: }
616:
617: public int checksum() {
618: int retval = 0;
619:
620: synchronized (array) {
621: for (int i = 0; i < num_fields; i++)
622: for (int j = 0; j < num_fields; j++)
623: retval += array[i][j];
624: }
625: return retval;
626: }
627:
628: public void update(Graphics g) {
629: Dimension d = getSize();
630:
631: if (off_graphics == null || d.width != off_dimension.width
632: || d.height != off_dimension.height) {
633: off_dimension = d;
634: off_image = createImage(d.width, d.height);
635: off_graphics = off_image.getGraphics();
636: }
637:
638: //Erase the previous image.
639: off_graphics.setColor(getBackground());
640: off_graphics.fillRect(0, 0, d.width, d.height);
641: off_graphics.setColor(Color.black);
642: off_graphics.setFont(def_font);
643: drawEmptyBoard(off_graphics);
644: drawNumbers(off_graphics);
645: g.drawImage(off_image, 0, 0, this );
646: }
647:
648: public void paint(Graphics g) {
649: update(g);
650: }
651:
652: /**
653: * Draws the empty board, no pieces on it yet, just grid lines
654: */
655: void drawEmptyBoard(Graphics g) {
656: int x = x_offset, y = y_offset;
657: Color old_col = g.getColor();
658:
659: g.setFont(def_font2);
660: old_col = g.getColor();
661: g.setColor(checksum_col);
662: g.drawString(("Checksum: " + checksum), x_offset + field_size,
663: y_offset - 20);
664: g.setFont(def_font);
665: g.setColor(old_col);
666:
667: for (int i = 0; i < num_fields; i++) {
668: for (int j = 0; j < num_fields; j++) { // draws 1 row
669: g.drawRect(x, y, field_size, field_size);
670: x += field_size;
671: }
672: g.drawString(("" + (num_fields - i - 1)), x + 20, y
673: + field_size / 2);
674: y += field_size;
675: x = x_offset;
676: }
677:
678: for (int i = 0; i < num_fields; i++) {
679: g.drawString(("" + i), x_offset + i * field_size
680: + field_size / 2, y + 30);
681: }
682: }
683:
684: void drawNumbers(Graphics g) {
685: Point p;
686: String num;
687: FontMetrics fm = g.getFontMetrics();
688: int len = 0;
689:
690: synchronized (array) {
691: for (int i = 0; i < num_fields; i++)
692: for (int j = 0; j < num_fields; j++) {
693: num = "" + array[i][j];
694: len = fm.stringWidth(num);
695: p = index2Coord(i, j);
696: g.drawString(num, p.x - (len / 2), p.y);
697: }
698: }
699: }
700:
701: Point coord2Index(int x, int y) {
702: Point ret = new Point();
703:
704: ret.x = x_offset + (x * field_size);
705: ret.y = y_offset + ((num_fields - 1 - y) * field_size);
706: return ret;
707: }
708:
709: Point index2Coord(int i, int j) {
710: int x = x_offset + i * field_size + field_size / 2;
711:
712: // int y=y_offset + j*field_size + field_size/2;
713:
714: int y = y_offset + num_fields * field_size - j * field_size
715: - field_size / 2;
716:
717: return new Point(x, y);
718: }
719:
720: }
|