package org.jgroups.demos; import org.jgroups.client.StompConnection; import org.jgroups.util.Util; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import java.util.List; /** * Simple STOMP demo client. Use -h and -p to connect to *any* JGroups server (has to have STOMP in the config) * <p> * @author Bela Ban, Oct 17 2001 */ public class StompDraw implements StompConnection.Listener, ActionListener { private int num_servers=1; private int num_clients=0; private JFrame mainFrame=null; private JPanel sub_panel=null; private DrawPanel panel=null; private JButton clear_button, leave_button; private final Random random=new Random(System.currentTimeMillis()); private final Font default_font=new Font("Helvetica",Font.PLAIN,12); private final Color draw_color=selectColor(); private static final Color background_color=Color.white; private final List<String> servers=new ArrayList<String>(); private final Set<String> clients=new HashSet<String>(); protected StompConnection stomp_client; protected static final String draw_dest="/topics/draw-demo"; protected static final String clients_dest="/topics/clients"; public StompDraw(String host, String port) throws Exception { stomp_client=new StompConnection(host + ":" + port); stomp_client.addListener(this); } public static void main(String[] args) { StompDraw draw=null; String host="localhost", port="8787"; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { help(); return; } if("-h".equals(args[i])) { host=args[++i]; continue; } if("-p".equals(args[i])) { port=args[++i]; continue; } help(); return; } try { draw=new StompDraw(host, port); draw.go(); } catch(Throwable e) { System.err.println("fatal error: " + e.getLocalizedMessage() + ", cause: "); Throwable t=e.getCause(); if(t != null) t.printStackTrace(System.err); System.exit(0); } } static void help() { System.out.println("\nDraw [-help] [-no_channel] [-h host] [-port port]"); } private Color selectColor() { int red=Math.abs(random.nextInt()) % 255; int green=Math.abs(random.nextInt()) % 255; int blue=Math.abs(random.nextInt()) % 255; return new Color(red, green, blue); } private void sendToAll(byte[] buf) throws Exception { if(buf != null) stomp_client.send(draw_dest, buf, 0, buf.length); } public void go() throws Exception { stomp_client.connect(); stomp_client.subscribe(draw_dest); stomp_client.subscribe(clients_dest); mainFrame=new JFrame(); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); panel=new DrawPanel(false); panel.setBackground(background_color); sub_panel=new JPanel(); mainFrame.getContentPane().add("Center", panel); clear_button=new JButton("Clear"); clear_button.setFont(default_font); clear_button.addActionListener(this); leave_button=new JButton("Leave"); leave_button.setFont(default_font); leave_button.addActionListener(this); sub_panel.add("South", clear_button); sub_panel.add("South", leave_button); mainFrame.getContentPane().add("South", sub_panel); mainFrame.setBackground(background_color); clear_button.setForeground(Color.blue); leave_button.setForeground(Color.blue); mainFrame.pack(); mainFrame.setLocation(15, 25); mainFrame.setBounds(new Rectangle(250, 250)); mainFrame.setVisible(true); setTitle(); String session_id=stomp_client.getSessionId(); if(session_id != null) stomp_client.send(clients_dest, null, 0, 0, "client-joined", session_id); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { StompDraw.this.stop(); } }); } void setTitle() { if(mainFrame != null) mainFrame.setTitle(num_servers + " server(s), " + num_clients + " client(s)"); } int getNumberOfClients() { synchronized(clients) { return clients.size(); } } String getAllClients() { StringBuilder sb=new StringBuilder(); boolean first=true; for(String client: clients) { if(first) first=false; else sb.append(","); sb.append(client); } return sb.toString(); } public void onInfo(Map<String, String> information) { String view=information.get("view"); Collection<String> list; if(view != null) { list=Util.parseCommaDelimitedStrings(view); if(list != null) { num_servers=list.size(); if(mainFrame != null) setTitle(); servers.clear(); servers.addAll(list); } else { String targets=information.get("endpoints"); if(targets != null) { list=Util.parseCommaDelimitedStrings(targets); if(list != null) { num_servers=list.size(); if(mainFrame != null) setTitle(); servers.clear(); servers.addAll(list); } } } } } public void onMessage(Map<String, String> headers, byte[] buf, int offset, int length) { if(buf == null) return; String destination=headers.get("destination"); if(destination != null && destination.equals(clients_dest)) { String new_client=headers.get("client-joined"); if(new_client != null) { synchronized(clients) { if(clients.add(new_client)) { num_clients=clients.size(); setTitle(); } } stomp_client.send(clients_dest, null, 0, 0, "clients", getAllClients()); } String left_client=headers.get("client-left"); if(left_client != null) { synchronized(clients) { if(clients.remove(left_client)) { num_clients=clients.size(); setTitle(); } } } String all_clients=headers.get("clients"); if(all_clients != null) { List<String> list=Util.parseCommaDelimitedStrings(all_clients); if(list != null) { synchronized(clients) { if(clients.addAll(list)) { num_clients=clients.size(); setTitle(); } } } } return; } try { DrawCommand comm=(DrawCommand)Util.streamableFromByteBuffer(DrawCommand.class, buf, offset, length); switch(comm.mode) { case DrawCommand.DRAW: if(panel != null) panel.drawPoint(comm); break; case DrawCommand.CLEAR: clearPanel(); break; default: System.err.println("***** received invalid draw command " + comm.mode); break; } } catch(Exception e) { e.printStackTrace(); } } /* --------------- Callbacks --------------- */ public void clearPanel() { if(panel != null) panel.clear(); } public void sendClearPanelMsg() { DrawCommand comm=new DrawCommand(DrawCommand.CLEAR); try { byte[] buf=Util.streamableToByteBuffer(comm); sendToAll(buf); } catch(Exception ex) { System.err.println(ex); } } public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); if("Clear".equals(command)) { sendClearPanelMsg(); } else if("Leave".equals(command)) { stop(); mainFrame.setVisible(false); mainFrame.dispose(); } else System.out.println("Unknown action"); } public void stop() { if(!stomp_client.isConnected()) return; String session_id=stomp_client.getSessionId(); if(session_id != null) { stomp_client.send(clients_dest, null, 0, 0, "client-left", session_id); } stomp_client.disconnect(); } private class DrawPanel extends JPanel implements MouseMotionListener { final Dimension preferred_size=new Dimension(235, 170); Image img=null; // for drawing pixels Dimension d, imgsize=null; Graphics gr=null; final Map<Point,Color> state; public DrawPanel(boolean use_state) { if(use_state) state=new LinkedHashMap<Point,Color>(); else state=null; createOffscreenImage(false); addMouseMotionListener(this); addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { if(getWidth() <= 0 || getHeight() <= 0) return; createOffscreenImage(false); } }); } public void writeState(OutputStream outstream) throws IOException { synchronized(state) { if(state != null) { DataOutputStream dos=new DataOutputStream(outstream); dos.writeInt(state.size()); Point point; Color col; for(Map.Entry<Point,Color> entry: state.entrySet()) { point=entry.getKey(); col=entry.getValue(); dos.writeInt(point.x); dos.writeInt(point.y); dos.writeInt(col.getRGB()); } dos.flush(); } } } public void readState(InputStream instream) throws IOException { DataInputStream in=new DataInputStream(instream); Map<Point,Color> new_state=new HashMap<Point,Color>(); int num=in.readInt(); Point point; Color col; for(int i=0; i < num; i++) { point=new Point(in.readInt(), in.readInt()); col=new Color(in.readInt()); new_state.put(point, col); } synchronized(state) { state.clear(); state.putAll(new_state); System.out.println("read state: " + state.size() + " entries"); createOffscreenImage(true); } } final void createOffscreenImage(boolean discard_image) { d=getSize(); if(discard_image) { img=null; imgsize=null; } if(img == null || imgsize == null || imgsize.width != d.width || imgsize.height != d.height) { img=createImage(d.width, d.height); if(img != null) { gr=img.getGraphics(); if(gr != null && state != null) { drawState(); } } imgsize=d; } repaint(); } /* ---------------------- MouseMotionListener interface------------------------- */ public void mouseMoved(MouseEvent e) {} public void mouseDragged(MouseEvent e) { int x=e.getX(), y=e.getY(); DrawCommand comm=new DrawCommand(DrawCommand.DRAW, x, y, draw_color.getRed(), draw_color.getGreen(), draw_color.getBlue()); try { byte[] buf=Util.streamableToByteBuffer(comm); sendToAll(buf); } catch(Exception ex) { System.err.println(ex); } } /* ------------------- End of MouseMotionListener interface --------------------- */ /** * Adds pixel to queue and calls repaint() whenever we have MAX_ITEMS pixels in the queue * or when MAX_TIME msecs have elapsed (whichever comes first). The advantage compared to just calling * repaint() after adding a pixel to the queue is that repaint() can most often draw multiple points * at the same time. */ public void drawPoint(DrawCommand c) { if(c == null || gr == null) return; Color col=new Color(c.r, c.g, c.b); gr.setColor(col); gr.fillOval(c.x, c.y, 10, 10); repaint(); if(state != null) { synchronized(state) { state.put(new Point(c.x, c.y), col); } } } public void clear() { if(gr == null) return; gr.clearRect(0, 0, getSize().width, getSize().height); repaint(); if(state != null) { synchronized(state) { state.clear(); } } } /** Draw the entire panel from the state */ public void drawState() { // clear(); Map.Entry entry; Point pt; Color col; synchronized(state) { for(Iterator it=state.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); pt=(Point)entry.getKey(); col=(Color)entry.getValue(); gr.setColor(col); gr.fillOval(pt.x, pt.y, 10, 10); } } repaint(); } public Dimension getPreferredSize() { return preferred_size; } public void paintComponent(Graphics g) { super.paintComponent(g); if(img != null) { g.drawImage(img, 0, 0, null); } } } }