package org.limewire.mojito.visual; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.geom.Rectangle2D; import java.lang.reflect.InvocationTargetException; import java.net.SocketAddress; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.Timer; import org.limewire.mojito.Context; import org.limewire.mojito.KUID; import org.limewire.mojito.io.MessageDispatcher.MessageDispatcherEvent; import org.limewire.mojito.io.MessageDispatcher.MessageDispatcherListener; import org.limewire.mojito.io.MessageDispatcher.MessageDispatcherEvent.EventType; import org.limewire.mojito.messages.RequestMessage; import org.limewire.mojito.messages.DHTMessage.OpCode; /** * Paints different representations of the Distributed Hash Table (DHT). * <code>ArcsVisualizer</code> is a small framework for visually representing * the DHT messages distinguished by color and dash patterns. */ public class ArcsVisualizer extends JPanel implements MessageDispatcherListener { private static final int SLEEP = 100; private static final float FONT_SIZE = 24f; private final Context context; private Timer timer; public static ArcsVisualizer show(final Context context) { final ArcsVisualizer arcs = new ArcsVisualizer(context, context.getLocalNodeID()); SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame frame = new JFrame(context.getName()); frame.getContentPane().add(arcs); frame.setBounds(20, 30, 640, 640); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { arcs.stopArcs(); } }); frame.setVisible(true); } }); arcs.startArcs(); return arcs; } public void startArcs() { timer.start(); context.getMessageDispatcher().addMessageDispatcherListener(this); } public void stopArcs() { timer.stop(); context.getMessageDispatcher().removeMessageDispatcherListener(this); } private final Object lock = new Object(); private Painter painter; private int painterIndex = 0; private final List<Painter> painters = new ArrayList<Painter>(); public ArcsVisualizer(Context context, KUID nodeId) { this.context = context; addPainter(new SnowMan(nodeId)); addPainter(new PlasmaLamp(nodeId)); addPainter(new DartBoard(nodeId)); painter = painters.get(painterIndex); Runnable runner = new Runnable() { @Override public void run() { timer = new Timer(SLEEP, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { repaint(); } }); // Make sure the JPanel is focusable or it won't fire // FocusEvent and Component's hasFocus() method returns // always false setFocusable(true); addMouseListener(new MouseAdapter() { private volatile boolean hadFocus = false; @Override public void mousePressed(MouseEvent e) { hadFocus = e.getComponent().isFocusOwner(); // Request focus if not focused. This can occur if the // window is newly activated. if (!hadFocus) { ArcsVisualizer.this.requestFocusInWindow(); } } @Override public void mouseClicked(MouseEvent e) { if (!hadFocus) { return; } synchronized (lock) { painter.clear(); painterIndex = (painterIndex + 1) % painters.size(); painter = painters.get(painterIndex); } } }); } }; if(SwingUtilities.isEventDispatchThread()) { runner.run(); } else { try { SwingUtilities.invokeAndWait(runner); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } } } public void addPainter(Painter painter) { synchronized (lock) { painters.add(painter); } } @Override public void paint(Graphics g) { float width = getWidth(); float height = getHeight(); Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(Color.black); g2.fill(new Rectangle2D.Float(0, 0, width, height)); g2.setFont(getFont().deriveFont(FONT_SIZE)); g2.setColor(Color.green); FontMetrics fm = g2.getFontMetrics(); g2.drawString("Input", width/4f - fm.stringWidth("Input")/2f, 30); g2.setColor(Color.red); g2.drawString("Output", width-width/4f - fm.stringWidth("Output")/2f, 30); g2.setFont(getFont().deriveFont(FONT_SIZE/2)); fm = g2.getFontMetrics(); g2.setColor(new Color(0, 255, 0)); g2.drawString("response", width/4f - fm.stringWidth("response")/2f, 48); g2.setColor(new Color(0, 255, 255)); g2.drawString("request", width/4f - fm.stringWidth("request")/2f, 60); g2.setColor(new Color(255, 0, 0)); g2.drawString("request", width-width/4f - fm.stringWidth("request")/2f, 48); g2.setColor(new Color(255, 0, 255)); g2.drawString("response", width-width/4f - fm.stringWidth("response")/2f, 60); synchronized (lock) { painter.paint(this, g2); } } /* * (non-Javadoc) * @see org.limewire.mojito.io.MessageDispatcher.MessageDispatcherListener#handleMessageDispatcherEvent(org.limewire.mojito.io.MessageDispatcher.MessageDispatcherEvent) */ public void handleMessageDispatcherEvent(MessageDispatcherEvent evt) { EventType type = evt.getEventType(); KUID nodeId = null; SocketAddress addr = null; if (type.equals(EventType.MESSAGE_RECEIVED)) { nodeId = evt.getMessage().getContact().getNodeID(); addr = evt.getMessage().getContact().getContactAddress(); } else if (type.equals(EventType.MESSAGE_SENT)) { nodeId = evt.getNodeID(); addr = evt.getSocketAddress(); } else { return; } OpCode opcode = evt.getMessage().getOpCode(); boolean request = (evt.getMessage() instanceof RequestMessage); synchronized (lock) { painter.handle(type, nodeId, addr, opcode, request); } } @SuppressWarnings({"InfiniteLoopStatement"}) public static void main(String[] args) throws Exception { ArcsVisualizer arcs = new ArcsVisualizer(null, KUID.createRandomID()); JFrame frame = new JFrame(); frame.getContentPane().add(arcs); frame.setBounds(20, 30, 640, 480); frame.setVisible(true); Random generator = new Random(); EventType type = null; KUID nodeId = null; final int sleep = 100; while(true) { // Simulate an received or sent message nodeId = KUID.createRandomID(); if (generator.nextBoolean()) { type = EventType.MESSAGE_RECEIVED; } else { type = EventType.MESSAGE_SENT; } OpCode opcode = OpCode.valueOf(1 + generator.nextInt(OpCode.values().length-1)); //OpCode opcode = OpCode.FIND_NODE_REQUEST; arcs.painter.handle(type, nodeId, null, opcode, true); // Sleep a bit... //Thread.sleep(sleep); Thread.sleep(generator.nextInt(sleep)); // Send every now an then a response if (generator.nextBoolean()) { if (type.equals(EventType.MESSAGE_SENT)) { arcs.painter.handle(EventType.MESSAGE_RECEIVED, nodeId, null, opcode, false); } else { arcs.painter.handle(EventType.MESSAGE_SENT, nodeId, null, opcode, false); } } } } }