package org.jgroups.demos; import org.jgroups.*; import org.jgroups.util.Util; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.nio.ByteBuffer; /** * Originally written to be a demo for TOTAL order (code to be written by a student). In the meantime, * it evolved into a state transfer demo. All members maintain a shared matrix and continually * broadcast changes to be applied to a randomly chosen field (e.g. multiplication of field with new * value, division, addition, subtraction). Each member can be started independently (starts to * broadcast update messages to all members). When "Stop" is pressed, a stop message is broadcast to * all members, causing them to stop sending messages. The "Clear" button clears the shared state; * "GetState" refreshes it from the shared group state (using the state transfer protocol).<p>If the * demo is to be used to show total order, then the SEQUENCER protocol would have to be added to the * stack. * * @author Bela Ban */ public class TotalOrder extends Frame { final Font def_font=new Font("Helvetica", Font.BOLD, 12); final Font def_font2=new Font("Helvetica", Font.PLAIN, 12); MyCanvas canvas; final MenuBar menubar=createMenuBar(); final Button start=new Button("Start"); final Button stop=new Button("Stop"); final Button clear=new Button("Clear"); final Button get_state=new Button("Get State"); final Button quit=new Button("Quit"); final Panel button_panel=new Panel(); SenderThread sender=null; Channel channel; long timeout=0; int field_size=0; int num_fields=0; static final int x_offset=30; static final int y_offset=40; private int num=0; private int num_additions=0, num_subtractions=0, num_divisions=0, num_multiplications=0; void error(String s) { System.err.println(s); } class EventHandler extends WindowAdapter { final Frame gui; public EventHandler(Frame g) { gui=g; } public void windowClosing(WindowEvent e) { gui.dispose(); System.exit(0); } } class SenderThread extends Thread { TotOrderRequest req; boolean running=true; public void stopSender() { running=false; interrupt(); System.out.println("-- num_additions: " + num_additions + "\n-- num_subtractions: " + num_subtractions + "\n-- num_divisions: " + num_divisions + "\n-- num_multiplications: " + num_multiplications); num_additions=num_subtractions=num_multiplications=num_divisions=0; } public void run() { this.setName("SenderThread"); byte[] buf; int cnt=0; while(running) { try { req=createRandomRequest(); buf=req.toBuffer(); channel.send(new Message(null, null, buf)); System.out.print("-- num requests sent: " + cnt + "\r"); if(timeout > 0) Util.sleep(timeout); cnt++; if(num > 0 && cnt > num) { running=false; cnt=0; } } catch(Exception e) { error(e.toString()); return; } } } } void processRequest(TotOrderRequest req) throws Exception { int x=req.x, y=req.y, val=req.val; if(req.type == TotOrderRequest.STOP) { stopSender(); return; } switch(req.type) { case TotOrderRequest.ADDITION: canvas.addValueTo(x, y, val); num_additions++; break; case TotOrderRequest.SUBTRACTION: canvas.subtractValueFrom(x, y, val); num_subtractions++; break; case TotOrderRequest.MULTIPLICATION: canvas.multiplyValueWith(x, y, val); num_multiplications++; break; case TotOrderRequest.DIVISION: canvas.divideValueBy(x, y, val); num_divisions++; break; } canvas.update(); } public TotalOrder(String title, long timeout, int num_fields, int field_size, String props, int num) { Dimension s; this.timeout=timeout; this.num_fields=num_fields; this.field_size=field_size; this.num=num; setFont(def_font); start.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { startSender(); } }); stop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { TotOrderRequest req=new TotOrderRequest(TotOrderRequest.STOP, 0, 0, 0); byte[] buf=req.toBuffer(); channel.send( new Message( null, null, buf)); } catch(Exception ex) { } } }); clear.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { canvas.clear(); } }); get_state.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { channel.getState(null, 3000); } catch(Throwable t) { error("exception fetching state: " + t); } } }); quit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { channel.disconnect(); channel.close(); System.exit(0); } }); setTitle(title); addWindowListener(new EventHandler(this)); setBackground(Color.white); setMenuBar(menubar); setLayout(new BorderLayout()); canvas=new MyCanvas(num_fields, field_size, x_offset, y_offset); add("Center", canvas); button_panel.setLayout(new FlowLayout()); button_panel.setFont(def_font2); button_panel.add(start); button_panel.add(stop); button_panel.add(clear); button_panel.add(get_state); button_panel.add(quit); add("South", button_panel); s=canvas.getSize(); s.height+=100; setSize(s); try { channel=new JChannel(props); channel.setReceiver(new ReceiverAdapter() { public void receive(Message msg) { try { TotOrderRequest req=new TotOrderRequest(); ByteBuffer buf=ByteBuffer.wrap(msg.getBuffer()); req.init(buf); processRequest(req); } catch(Exception e) { System.err.println(e); } } public void getState(OutputStream output) throws Exception { int[][] copy_of_state=canvas.getCopyOfState(); Util.objectToStream(copy_of_state, new DataOutputStream(output)); } public void setState(InputStream input) throws Exception { canvas.setState(Util.objectFromStream(new DataInputStream(input))); } public void viewAccepted(View view) { System.out.println("view = " + view); } }); channel.connect("TotalOrderGroup"); channel.getState(null, 8000); } catch(Exception e) { e.printStackTrace(); System.exit(-1); } } void startSender() { if(sender == null || !sender.isAlive()) { sender=new SenderThread(); sender.start(); } } void stopSender() { if(sender != null) { sender.stopSender(); sender=null; } } private MenuBar createMenuBar() { MenuBar ret=new MenuBar(); Menu file=new Menu("File"); MenuItem quitm=new MenuItem("Quit"); ret.setFont(def_font2); ret.add(file); file.addSeparator(); file.add(quitm); quitm.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(1); } }); return ret; } private TotOrderRequest createRandomRequest() { TotOrderRequest ret=null; byte op_type=(byte)(((Math.random() * 10) % 4) + 1); // 1 - 4 int x=(int)((Math.random() * num_fields * 2) % num_fields); int y=(int)((Math.random() * num_fields * 2) % num_fields); int val=(int)((Math.random() * num_fields * 200) % 10); ret=new TotOrderRequest(op_type, x, y, val); return ret; } public static void main(String[] args) { TotalOrder g; String arg; long timeout=200; int num_fields=3; int field_size=80; String props=null; int num=0; props="udp.xml"; for(int i=0; i < args.length; i++) { arg=args[i]; if("-timeout".equals(arg)) { timeout=Long.parseLong(args[++i]); continue; } if("-num_fields".equals(arg)) { num_fields=Integer.parseInt(args[++i]); continue; } if("-field_size".equals(arg)) { field_size=Integer.parseInt(args[++i]); continue; } if("-help".equals(arg)) { System.out.println("\nTotalOrder [-timeout <value>] [-num_fields <value>] " + "[-field_size <value>] [-props <properties (can be URL)>] [-num <num requests>]\n"); return; } if("-props".equals(arg)) { props=args[++i]; continue; } if("-num".equals(arg)) { num=Integer.parseInt(args[++i]); } } try { g=new TotalOrder("Total Order Demo on " + InetAddress.getLocalHost().getHostName(), timeout, num_fields, field_size, props, num); g.setVisible(true); } catch(Exception e) { System.err.println(e); } } } class TotOrderRequest { public static final byte STOP=0; public static final byte ADDITION=1; public static final byte SUBTRACTION=2; public static final byte MULTIPLICATION=3; public static final byte DIVISION=4; final static int SIZE=Global.BYTE_SIZE + Global.INT_SIZE * 3; public byte type=ADDITION; public int x=0; public int y=0; public int val=0; public TotOrderRequest() { } TotOrderRequest(byte type, int x, int y, int val) { this.type=type; this.x=x; this.y=y; this.val=val; } public String printType() { switch(type) { case STOP: return "STOP"; case ADDITION: return "ADDITION"; case SUBTRACTION: return "SUBTRACTION"; case MULTIPLICATION: return "MULTIPLICATION"; case DIVISION: return "DIVISION"; default: return "<unknown>"; } } // public void writeExternal(ObjectOutput out) throws IOException { // out.writeByte(type); // out.writeInt(x); // out.writeInt(y); // out.writeInt(val); // } // // public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // type=in.readByte(); // x=in.readInt(); // y=in.readInt(); // val=in.readInt(); // } public byte[] toBuffer() { ByteBuffer buf=ByteBuffer.allocate(SIZE); buf.put(type); buf.putInt(x); buf.putInt(y); buf.putInt(val); return buf.array(); } public void init(ByteBuffer buf) { type=buf.get(); x=buf.getInt(); y=buf.getInt(); val=buf.getInt(); } public String toString() { return "[" + x + ',' + y + ": " + printType() + '(' + val + ")]"; } } class MyCanvas extends Canvas { int field_size=100; int num_fields=4; int x_offset=30; int y_offset=30; final Font def_font=new Font("Helvetica", Font.BOLD, 14); int[][] array=null; // state Dimension off_dimension=null; Image off_image=null; Graphics off_graphics=null; final Font def_font2=new Font("Helvetica", Font.PLAIN, 12); static final Color checksum_col=Color.blue; int checksum=0; public MyCanvas(int num_fields, int field_size, int x_offset, int y_offset) { this.num_fields=num_fields; this.field_size=field_size; this.x_offset=x_offset; this.y_offset=y_offset; array=new int[num_fields][num_fields]; setBackground(Color.white); setSize(2 * x_offset + num_fields * field_size + 30, y_offset + num_fields * field_size + 50); for(int i=0; i < num_fields; i++) for(int j=0; j < num_fields; j++) array[i][j]=0; } public void setFieldSize(int fs) { field_size=fs; } public void setNumFields(int nf) { num_fields=nf; } public void setXOffset(int o) { x_offset=o; } public void setYOffset(int o) { y_offset=o; } public void addValueTo(int x, int y, int value) { synchronized(array) { array[x][y]+=value; repaint(); } } public void subtractValueFrom(int x, int y, int value) { synchronized(array) { array[x][y]-=value; repaint(); } } public void multiplyValueWith(int x, int y, int value) { synchronized(array) { array[x][y]*=value; repaint(); } } public void divideValueBy(int x, int y, int value) { if(value == 0) return; synchronized(array) { array[x][y]/=value; repaint(); } } public void setValueAt(int x, int y, int value) { synchronized(array) { array[x][y]=value; } repaint(); } public int getValueAt(int x, int y) { synchronized(array) { return array[x][y]; } } public void clear() { synchronized(array) { for(int i=0; i < num_fields; i++) for(int j=0; j < num_fields; j++) array[i][j]=0; checksum=checksum(); repaint(); } } public int[][] getState() { synchronized(array) { return array; } } public int[][] getCopyOfState() { int[][] retval=new int[num_fields][num_fields]; synchronized(array) { for(int i=0; i < num_fields; i++) System.arraycopy(array[i], 0, retval[i], 0, num_fields); return retval; } } public void update() { checksum=checksum(); repaint(); } public void setState(Object new_state) { if(new_state == null) return; try { int[][] new_array=(int[][])new_state; synchronized(array) { clear(); for(int i=0; i < num_fields; i++) System.arraycopy(new_array[i], 0, array[i], 0, num_fields); checksum=checksum(); repaint(); } } catch(Exception e) { System.err.println(e); } } public int checksum() { int retval=0; synchronized(array) { for(int i=0; i < num_fields; i++) for(int j=0; j < num_fields; j++) retval+=array[i][j]; } return retval; } public void update(Graphics g) { Dimension d=getSize(); if(off_graphics == null || d.width != off_dimension.width || d.height != off_dimension.height) { off_dimension=d; off_image=createImage(d.width, d.height); off_graphics=off_image.getGraphics(); } //Erase the previous image. off_graphics.setColor(getBackground()); off_graphics.fillRect(0, 0, d.width, d.height); off_graphics.setColor(Color.black); off_graphics.setFont(def_font); drawEmptyBoard(off_graphics); drawNumbers(off_graphics); g.drawImage(off_image, 0, 0, this); } public void paint(Graphics g) { update(g); } /** * Draws the empty board, no pieces on it yet, just grid lines */ void drawEmptyBoard(Graphics g) { int x=x_offset, y=y_offset; Color old_col=g.getColor(); g.setFont(def_font2); old_col=g.getColor(); g.setColor(checksum_col); g.drawString(("Checksum: " + checksum), x_offset + field_size, y_offset - 20); g.setFont(def_font); g.setColor(old_col); for(int i=0; i < num_fields; i++) { for(int j=0; j < num_fields; j++) { // draws 1 row g.drawRect(x, y, field_size, field_size); x+=field_size; } g.drawString((String.valueOf((num_fields - i - 1))), x + 20, y + field_size / 2); y+=field_size; x=x_offset; } for(int i=0; i < num_fields; i++) { g.drawString((String.valueOf(i)), x_offset + i * field_size + field_size / 2, y + 30); } } void drawNumbers(Graphics g) { Point p; String num; FontMetrics fm=g.getFontMetrics(); int len=0; synchronized(array) { for(int i=0; i < num_fields; i++) for(int j=0; j < num_fields; j++) { num=String.valueOf(array[i][j]); len=fm.stringWidth(num); p=index2Coord(i, j); g.drawString(num, p.x - (len / 2), p.y); } } } Point coord2Index(int x, int y) { Point ret=new Point(); ret.x=x_offset + (x * field_size); ret.y=y_offset + ((num_fields - 1 - y) * field_size); return ret; } Point index2Coord(int i, int j) { int x=x_offset + i * field_size + field_size / 2; // int y=y_offset + j*field_size + field_size/2; int y=y_offset + num_fields * field_size - j * field_size - field_size / 2; return new Point(x, y); } }