You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ee...@apache.org on 2009/10/22 00:22:26 UTC
svn commit: r828238 - in /incubator/cassandra/trunk/contrib/circuit: ./ bin/
resources/ resources/java/ resources/java/images/ src/ src/org/
src/org/apache/ src/org/apache/cassandra/ src/org/apache/cassandra/contrib/
src/org/apache/cassandra/contrib/ci...
Author: eevans
Date: Wed Oct 21 22:22:26 2009
New Revision: 828238
URL: http://svn.apache.org/viewvc?rev=828238&view=rev
Log:
initial commit of visualization and diagnostics app
Patch by eevans and gdusbabek; reviewed by gdusbabek for CASSANDRA-252
Added:
incubator/cassandra/trunk/contrib/circuit/
incubator/cassandra/trunk/contrib/circuit/bin/
incubator/cassandra/trunk/contrib/circuit/bin/circuit (with props)
incubator/cassandra/trunk/contrib/circuit/build.xml (with props)
incubator/cassandra/trunk/contrib/circuit/resources/
incubator/cassandra/trunk/contrib/circuit/resources/java/
incubator/cassandra/trunk/contrib/circuit/resources/java/images/
incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_blue30x30.png
incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_green30x30.png
incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_red30x30.png
incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_yellow30x30.png
incubator/cassandra/trunk/contrib/circuit/resources/node.xcf
incubator/cassandra/trunk/contrib/circuit/src/
incubator/cassandra/trunk/contrib/circuit/src/org/
incubator/cassandra/trunk/contrib/circuit/src/org/apache/
incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/
incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/
incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/
incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/AboutDialog.java (with props)
incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/CircuitFrame.java (with props)
incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingModel.java (with props)
incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingPanel.java (with props)
Added: incubator/cassandra/trunk/contrib/circuit/bin/circuit
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/bin/circuit?rev=828238&view=auto
==============================================================================
--- incubator/cassandra/trunk/contrib/circuit/bin/circuit (added)
+++ incubator/cassandra/trunk/contrib/circuit/bin/circuit Wed Oct 21 22:22:26 2009
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DEFAULT_PORT=8080
+
+if [ "x$CLASSPATH" = "x" ]; then
+ # Cassandra class files.
+ if [ ! -d `dirname $0`/../../../build/classes ]; then
+ echo "Unable to locate cassandra class files" >&2
+ exit 1
+ fi
+
+ # Circuit class files.
+ if [ ! -d `dirname $0`/../build/classes ]; then
+ echo "Unable to locate circuit class files" >&2
+ exit 1
+ fi
+
+ CLASSPATH=`dirname $0`/../../../build/classes:`dirname $0`/../build/classes
+ for jar in `dirname $0`/../../../lib/*.jar; do
+ CLASSPATH=$CLASSPATH:$jar
+ done
+fi
+
+if [ -x $JAVA_HOME/bin/java ]; then
+ JAVA=$JAVA_HOME/bin/java
+else
+ JAVA=`which java`
+fi
+
+if [ "x$JAVA" = "x" ]; then
+ echo "Java executable not found (hint: set JAVA_HOME)" >&2
+ exit 1
+fi
+
+if [ "x$1" != "x" ]; then
+ HOST=$1
+else
+ echo "Usage: $0 <host> [port]" >&2
+ exit 1
+fi
+
+if [ "x$2" != "x" ]; then
+ PORT=$2
+else
+ PORT=$DEFAULT_PORT
+fi
+
+$JAVA -cp $CLASSPATH org.apache.cassandra.contrib.circuit.CircuitFrame $HOST $PORT
Propchange: incubator/cassandra/trunk/contrib/circuit/bin/circuit
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/cassandra/trunk/contrib/circuit/build.xml
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/build.xml?rev=828238&view=auto
==============================================================================
--- incubator/cassandra/trunk/contrib/circuit/build.xml (added)
+++ incubator/cassandra/trunk/contrib/circuit/build.xml Wed Oct 21 22:22:26 2009
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ -->
+<project basedir="." default="build" name="circuit">
+ <property name="cassandra.dir" value="../.." />
+ <property name="cassandra.lib" value="${cassandra.dir}/lib" />
+ <property name="cassandra.classes" value="${cassandra.dir}/build/classes" />
+ <property name="build.src" value="${basedir}/src" />
+ <property name="build.out" value="${basedir}/build" />
+ <property name="build.classes" value="${build.out}/classes" />
+ <property name="final.name" value="circuit" />
+
+ <target name="init">
+ <mkdir dir="${build.classes}" />
+ </target>
+
+ <target depends="init" name="build">
+ <javac destdir="${build.classes}">
+ <src path="${build.src}" />
+ <classpath>
+ <path>
+ <fileset dir="${cassandra.lib}">
+ <include name="**/*.jar" />
+ </fileset>
+ <pathelement location="${cassandra.classes}" />
+ </path>
+ </classpath>
+ </javac>
+ <copy todir="${build.classes}">
+ <fileset dir="resources/java" />
+ </copy>
+ </target>
+
+ <target name="jar" depends="build">
+ <mkdir dir="${build.classes}/META-INF" />
+ <jar jarfile="${build.out}/${final.name}.jar"
+ basedir="${build.classes}" />
+ </target>
+
+ <target name="clean">
+ <delete dir="${build.out}" />
+ </target>
+</project>
Propchange: incubator/cassandra/trunk/contrib/circuit/build.xml
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_blue30x30.png
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_blue30x30.png?rev=828238&view=auto
==============================================================================
Files incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_blue30x30.png (added) and incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_blue30x30.png Wed Oct 21 22:22:26 2009 differ
Added: incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_green30x30.png
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_green30x30.png?rev=828238&view=auto
==============================================================================
Files incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_green30x30.png (added) and incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_green30x30.png Wed Oct 21 22:22:26 2009 differ
Added: incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_red30x30.png
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_red30x30.png?rev=828238&view=auto
==============================================================================
Files incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_red30x30.png (added) and incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_red30x30.png Wed Oct 21 22:22:26 2009 differ
Added: incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_yellow30x30.png
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_yellow30x30.png?rev=828238&view=auto
==============================================================================
Files incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_yellow30x30.png (added) and incubator/cassandra/trunk/contrib/circuit/resources/java/images/node_yellow30x30.png Wed Oct 21 22:22:26 2009 differ
Added: incubator/cassandra/trunk/contrib/circuit/resources/node.xcf
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/resources/node.xcf?rev=828238&view=auto
==============================================================================
Files incubator/cassandra/trunk/contrib/circuit/resources/node.xcf (added) and incubator/cassandra/trunk/contrib/circuit/resources/node.xcf Wed Oct 21 22:22:26 2009 differ
Added: incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/AboutDialog.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/AboutDialog.java?rev=828238&view=auto
==============================================================================
--- incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/AboutDialog.java (added)
+++ incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/AboutDialog.java Wed Oct 21 22:22:26 2009
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.contrib.circuit;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JTextPane;
+
+public class AboutDialog extends JDialog
+{
+ private static final long serialVersionUID = 1L;
+
+ public AboutDialog(JFrame parent)
+ {
+ super(parent, "About " + parent.getTitle(), true);
+
+ Box vbox = Box.createVerticalBox();
+ vbox.add(Box.createGlue());
+ JTextPane textPane = new JTextPane();
+ textPane.setContentType("text/html");
+ textPane.setEditable(false);
+ textPane.setText(getHtmlMarkup());
+ vbox.add(textPane);
+ vbox.add(Box.createGlue());
+ getContentPane().add(vbox, "Center");
+
+ JPanel bottomPanel = new JPanel();
+ JButton closeButton = new JButton("Close");
+ bottomPanel.add(closeButton);
+ getContentPane().add(bottomPanel, "South");
+
+ closeButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent evt)
+ {
+ setVisible(false);
+ }
+ });
+
+ setSize(350, 220);
+ setResizable(false);
+ setModalityType(ModalityType.APPLICATION_MODAL);
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ setLocationRelativeTo(parent);
+ }
+
+ public static String getHtmlMarkup()
+ {
+ return String.format("<html><body bgcolor=#aaaab9><center><br>" +
+ "<font size=+2><b>Circuit</b></font><br><br>" +
+ "Visualization and diagnostics for Cassandra clusters.<br><br>" +
+ "<font size=-2 color=#333355>© 2009 The Apache Software Foundation</font>" +
+ "</center></body></html>");
+ }
+
+ public static void main(String[] args)
+ {
+ JDialog f = new AboutDialog(new JFrame("Phony"));
+ f.setVisible(true);
+ }
+}
Propchange: incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/AboutDialog.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/CircuitFrame.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/CircuitFrame.java?rev=828238&view=auto
==============================================================================
--- incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/CircuitFrame.java (added)
+++ incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/CircuitFrame.java Wed Oct 21 22:22:26 2009
@@ -0,0 +1,251 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.contrib.circuit;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Event;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTextArea;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import com.google.common.collect.Sets;
+
+public class CircuitFrame extends JFrame implements ActionListener
+{
+ private static final long serialVersionUID = 1L;
+ private static final String appTitle = "CircuitFrame";
+ private static final Dimension defaultSize = new Dimension(550, 600);
+ private static final SimpleDateFormat dateFormatter;
+ private static final Lock verifyLock = new ReentrantLock();
+
+ private RingModel ringModel;
+ private RingPanel ringPanel;
+ private JTextArea statusOutput;
+
+ private JMenuBar menuBar;
+ private JMenuItem quitMI, verifyMI, aboutMI;
+
+ static
+ {
+ dateFormatter = new SimpleDateFormat("HH:mm:ss");
+ }
+
+ public CircuitFrame(String hostname, int port)
+ {
+ super(appTitle);
+ setSize(defaultSize);
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ // The menu bar w/ items.
+ menuBar = new JMenuBar();
+ setJMenuBar(menuBar);
+
+ JMenu fileMenu = new JMenu("File");
+ fileMenu.setMnemonic(KeyEvent.VK_F);
+ menuBar.add(fileMenu);
+
+ quitMI = new JMenuItem("Quit");
+ quitMI.setMnemonic(KeyEvent.VK_Q);
+ quitMI.setAccelerator(
+ KeyStroke.getKeyStroke(KeyEvent.VK_Q, Event.CTRL_MASK));
+ quitMI.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) { System.exit(0); }
+ });
+ fileMenu.add(quitMI);
+
+ JMenu toolsMenu = new JMenu("Tools");
+ toolsMenu.setMnemonic(KeyEvent.VK_T);
+ menuBar.add(toolsMenu);
+
+ verifyMI = new JMenuItem("Verify Ring");
+ verifyMI.addActionListener(this);
+ toolsMenu.add(verifyMI);
+
+ JMenu helpMenu = new JMenu("Help");
+ helpMenu.setMnemonic(KeyEvent.VK_H);
+ menuBar.add(helpMenu);
+
+ aboutMI = new JMenuItem("About");
+ aboutMI.setMnemonic(KeyEvent.VK_A);
+ aboutMI.addActionListener(this);
+ helpMenu.add(aboutMI);
+
+ // FIXME: a progress dialog should be up while instantiating RingPanel
+ ringModel = new RingModel(hostname, port);
+ ringPanel = new RingPanel(ringModel);
+
+ statusOutput = new JTextArea();
+ statusOutput.setEditable(false);
+ Component logPanel = new JScrollPane(statusOutput);
+
+ JSplitPane contentPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, ringPanel, logPanel);
+ setContentPane(contentPane);
+
+ // Order matters here...
+ ringPanel.setPreferredSize(getSize());
+ setVisible(true);
+ contentPane.setDividerLocation(0.8);
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ Object src = e.getSource();
+
+ if (src == verifyMI)
+ {
+ verifyRing();
+ }
+ else if(src == aboutMI)
+ {
+ new AboutDialog(this).setVisible(true);
+ }
+ }
+
+ /**
+ * For each node, retrieve that nodes list and compare it to ours. If the
+ * list of remote nodes doesn't match (it's long or short), then the node is
+ * flagged accordingly and an error message is written to the status display.
+ */
+ private void verifyRing()
+ {
+ new Thread("VERIFY-RING")
+ {
+ public void run()
+ {
+ verifyLock.lock();
+ ringPanel.setVerifying(true);
+ try {
+ writeStatusOutput("Beginning ring verification...");
+ for (Node node : ringModel.getNodes())
+ {
+ // Skip the node we already queried at startup
+ if (node.isSeed())
+ continue;
+
+ writeStatusOutput("Verifying %s (ring) against reference node", node);
+ node.setSelected(true);
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ ringPanel.repaint();
+ }
+ });
+
+ // uncomment this to simulate a slow running verification process
+// try {Thread.currentThread().sleep(2000L); } catch (Exception ex) { }
+
+ Set<Node> othersSet, nodesSet;
+ try {
+ othersSet = new HashSet<Node>(ringModel.getRemoteNodes(node.getHost()));
+ } catch (IOException e) {
+ e.printStackTrace();
+ writeStatusOutput("Error retrieving node list from %s", node.getHost());
+ continue;
+ }
+
+ nodesSet = new HashSet<Node>(ringModel.getNodes());
+
+ for (Node upShort : Sets.difference(nodesSet, othersSet))
+ {
+ node.setStatus(NodeStatus.SHORT);
+ writeStatusOutput("%s: missing node %s", node, upShort);
+ }
+
+ for (Node upLong : Sets.difference(othersSet, nodesSet))
+ {
+ node.setStatus(NodeStatus.LONG);
+ writeStatusOutput("%s: contains node %s missing from reference list", node, upLong);
+ }
+
+ node.setSelected(false);
+ }
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ ringPanel.repaint();
+ }
+ });
+ writeStatusOutput("Ring verification complete.");
+ } finally
+ {
+ verifyLock.unlock();
+ ringPanel.setVerifying(false);
+ }
+ }
+ }.start();
+ }
+
+ // TODO: use StatusLevel to distinguish message priorities.
+ private void writeStatusOutput(String msg, StatusLevel level, Object...args)
+ {
+ String pref = String.format("[%s] ", dateFormatter.format(new Date()));
+ statusOutput.append(String.format(pref + msg + "\n", args));
+ statusOutput.setCaretPosition(statusOutput.getDocument().getLength());
+ }
+
+ private void writeStatusOutput(String msg, Object...args)
+ {
+ writeStatusOutput(msg, StatusLevel.INFO, args);
+ }
+
+ public static void main(final String[] args) throws IOException
+ {
+ if (args.length != 2)
+ {
+ System.err.println("Usage: java " + CircuitFrame.class.getName() + " <host> <port>");
+ System.exit(1);
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable() { public void run() {
+ CircuitFrame app = new CircuitFrame(args[0], Integer.parseInt(args[1]));
+ app.setVisible(true);
+ }});
+ }
+ catch (Exception ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ }
+}
+
+enum StatusLevel
+{
+ INFO,
+ WARN,
+ ERROR,
+ CRITICAL,
+ DEBUG,
+}
Propchange: incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/CircuitFrame.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingModel.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingModel.java?rev=828238&view=auto
==============================================================================
--- incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingModel.java (added)
+++ incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingModel.java Wed Oct 21 22:22:26 2009
@@ -0,0 +1,249 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.contrib.circuit;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.management.JMX;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import org.apache.cassandra.dht.Range;
+import org.apache.cassandra.service.StorageServiceMBean;
+
+/**
+ * This class provides data abstraction for the JMX instrumentation
+ * of Cassandra nodes.
+ */
+public class RingModel
+{
+ public static final int defaultPort = 8080;
+ private static final String fmtUrl = "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi";
+ private static final String ssObjName = "org.apache.cassandra.service:type=StorageService";
+
+ private String seedName;
+ private String seedAddr;
+ private int seedPort;
+ private List<Node> nodes;
+
+ /**
+ * Constructs a RingModel using the named reference host and port number.
+ *
+ * @param seedName the hostname or IP of the startup node
+ * @param seedPort JMX port number
+ * @throws IOException if unable to setup the JMX connection
+ */
+ public RingModel(String seedName, int seedPort)
+ {
+ this.seedName = seedName;
+ this.seedPort = seedPort;
+
+ try
+ {
+ seedAddr = InetAddress.getByName(seedName).getHostAddress();
+ }
+ catch (UnknownHostException e)
+ {
+ System.err.println("Error unknown host: " + seedName);
+ seedAddr = seedName;
+ }
+ }
+
+ /**
+ * Constructs a RingModel using the named reference host .
+ *
+ * @param seedName the hostname or IP of the startup node
+ */
+ public RingModel(String seedName) throws IOException
+ {
+ this(seedName, defaultPort);
+ }
+
+ // @throws IOException if unable to setup the JMX connection
+ private static List<Node> retrieveRingData(String seedAddress, String remoteHost, int port) throws IOException
+ {
+ JMXServiceURL jmxUrl = new JMXServiceURL(String.format(fmtUrl, remoteHost, port));
+ JMXConnector jmxc = JMXConnectorFactory.connect(jmxUrl, null);
+ StorageServiceMBean ssProxy;
+ MBeanServerConnection mbeanServerConn = jmxc.getMBeanServerConnection();
+
+ try
+ {
+ ObjectName name = new ObjectName(ssObjName);
+ ssProxy = JMX.newMBeanProxy(mbeanServerConn, name, StorageServiceMBean.class);
+ } catch (MalformedObjectNameException e)
+ {
+ throw new RuntimeException(
+ "Invalid ObjectName? Please report this as a bug.", e);
+ }
+
+ Map<Range, List<String>> rangeMap = ssProxy.getRangeToEndPointMap();
+ List<Range> ranges = new ArrayList<Range>(rangeMap.keySet());
+ Collections.sort(ranges);
+
+ List<Node> nodes = new ArrayList<Node>();
+
+ for (Range r : ranges)
+ {
+ String host = rangeMap.get(r).get(0);
+
+ NodeStatus status;
+ if (host.equals(seedAddress))
+ status = NodeStatus.ISSEED;
+ else
+ status = NodeStatus.OK;
+
+ String token = r.left().toString();
+ nodes.add(new Node(host, status, token));
+ }
+
+ return nodes;
+ }
+
+ private List<Node> retrieveRingData(String remoteHost) throws IOException
+ {
+ return retrieveRingData(seedAddr, remoteHost, seedPort);
+ }
+
+ /**
+ * Retrieves the nodes that are known to the reference host.
+ * @return the list of nodes seen by the reference host.
+ */
+ public List<Node> getNodes()
+ {
+ if (this.nodes == null)
+ {
+ List<Node> nodes = new ArrayList<Node>();
+
+ try
+ {
+ nodes = retrieveRingData(seedAddr, seedName, seedPort);
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ nodes.add(new Node(seedName, NodeStatus.UNKNOWN, null));
+ }
+
+ this.nodes = nodes;
+ }
+ return nodes;
+ }
+
+ /**
+ * Query the specified host for a list of nodes.
+ *
+ * @param remoteHost the hostname or IP address of a node
+ * @return the list of nodes seen by the specified host
+ * @throws IOException if an error is encountered communicating with the node
+ */
+ public List<Node> getRemoteNodes(String remoteHost) throws IOException
+ {
+ return retrieveRingData(remoteHost);
+ }
+}
+
+/**
+ * Represents a node in the cluster.
+ */
+class Node
+{
+ public String host;
+ public volatile NodeStatus nodeStatus;
+ public String startToken;
+ public volatile boolean isSelected;
+
+ public Node(String host, NodeStatus status, String startToken)
+ {
+ this.host = host;
+ this.nodeStatus = status;
+ this.startToken = startToken;
+ }
+
+ public String getStartToken()
+ {
+ return startToken;
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public NodeStatus getStatus()
+ {
+ return nodeStatus;
+ }
+
+ public String toString()
+ {
+ return host;
+ }
+
+ public boolean isSelected()
+ {
+ return isSelected;
+ }
+
+ public void setSelected(boolean isSelected)
+ {
+ this.isSelected = isSelected;
+ }
+
+ public boolean isSeed()
+ {
+ return nodeStatus == NodeStatus.ISSEED ? true : false;
+ }
+
+ public void setStatus(NodeStatus status)
+ {
+ nodeStatus = status;
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof Node))
+ return false;
+
+ Node other = (Node)o;
+ return other.getHost().equals(host);
+ }
+
+ public int hashCode()
+ {
+ return (startToken + host).hashCode();
+ }
+}
+
+enum NodeStatus
+{
+ OK,
+ ISSEED,
+ SHORT,
+ LONG,
+ UNKNOWN,
+}
Propchange: incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingModel.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingPanel.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingPanel.java?rev=828238&view=auto
==============================================================================
--- incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingPanel.java (added)
+++ incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingPanel.java Wed Oct 21 22:22:26 2009
@@ -0,0 +1,168 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.contrib.circuit;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.RenderingHints;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.RoundRectangle2D;
+import javax.swing.*;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+
+public class RingPanel extends JPanel
+{
+ private static final long serialVersionUID = 1L;
+ private static final Color bgColor = Color.white;
+ private static final String nodeImageFileOk = "images/node_green30x30.png";
+ private static final String nodeImageFileSeed = "images/node_blue30x30.png";
+ private static final String nodeImageFileShort = "images/node_red30x30.png";
+ private static final String nodeImageFileUnknown = "images/node_yellow30x30.png";
+ private static final Color ringColor = Color.blue;
+ private static final Color fontColor = Color.black;
+ private static final String fontName = "Helvetica";
+ private static final int fontSize = 12;
+ private static final int padding = 70;
+ private static final int nodeDiameter = 30;
+ private static final char[] verificationMessage = "Verifying ring...".toCharArray();
+
+ private static RenderingHints defaultHints;
+ private static Image nodeImageOk, nodeImageSeed, nodeImageShort, nodeImageUnknown;
+ private RingModel ringModel;
+ private boolean isVerifying = false;
+
+ static
+ {
+ defaultHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ defaultHints.put(RenderingHints.KEY_RENDERING,
+ RenderingHints.VALUE_RENDER_QUALITY);
+ }
+
+ public RingPanel(RingModel ringModel)
+ {
+ setBackground(bgColor);
+ setFont(new Font(fontName, Font.PLAIN, fontSize));
+ this.ringModel = ringModel;
+
+ nodeImageOk = new ImageIcon(getClass().getClassLoader().getResource(nodeImageFileOk)).getImage();
+ nodeImageSeed = new ImageIcon(getClass().getClassLoader().getResource(nodeImageFileSeed)).getImage();
+ nodeImageShort = new ImageIcon(getClass().getClassLoader().getResource(nodeImageFileShort)).getImage();
+ nodeImageUnknown = new ImageIcon(getClass().getClassLoader().getResource(nodeImageFileUnknown)).getImage();
+ }
+
+ public void paintComponent(Graphics g)
+ {
+ clear(g);
+ Graphics2D g2d = (Graphics2D)g;
+ g2d.setRenderingHints(defaultHints);
+
+ // Draw the ring
+ g2d.setPaint(ringColor);
+ g2d.setStroke(new BasicStroke(1));
+ int dia = getWidth() > getHeight() ? getHeight() - padding : getWidth() - padding;
+ int radius = dia / 2;
+ double ringX = (getWidth() / 2) - radius;
+ double ringY = (getHeight() / 2) - radius;
+ Ellipse2D.Double ring = new Ellipse2D.Double(ringX, ringY, dia, dia);
+ g2d.draw(ring);
+
+ // Obtain the ring according to our seed
+ List<Node> nodes = ringModel.getNodes();
+
+ // Place the nodes around the ring
+ double current = 0;
+ double increment = (2 * Math.PI) / nodes.size();
+
+ // Draw each node
+ for (Node node : nodes)
+ {
+ Image nodeImage = null;
+ switch (node.nodeStatus)
+ {
+ case ISSEED:
+ nodeImage = nodeImageSeed;
+ break;
+ case OK:
+ nodeImage = nodeImageOk;
+ break;
+ case SHORT: case LONG:
+ nodeImage = nodeImageShort;
+ break;
+ case UNKNOWN:
+ nodeImage = nodeImageUnknown;
+ break;
+ }
+
+ double x = Math.cos(current) * radius + (ring.getCenterX() - (nodeDiameter / 2));
+ double y = Math.sin(current) * radius + (ring.getCenterY() - (nodeDiameter / 2));
+ current = current + increment;
+
+ g2d.drawImage(nodeImage, (int)x, (int)y, null);
+
+ // Draw a square with rounded corners around the node to indicate
+ // when it is "selected", or active.
+ if (node.isSelected())
+ {
+ g2d.setPaint(ringColor);
+ RoundRectangle2D.Double outline = new RoundRectangle2D.Double(
+ (x - 2), (y - 2), (nodeDiameter + 4), (nodeDiameter + 4), 10, 10);
+ g2d.draw(outline);
+ }
+
+ // Write the label;
+ g2d.setPaint(fontColor);
+ g2d.drawString(node.getHost(), (int)x - (nodeDiameter / 2), (int)y - 5);
+ }
+
+ // if we're in the middle of verifying, draw something to indicate we are doing that.
+ if (isVerifying)
+ {
+ int msgWidth = g2d.getFontMetrics().charsWidth(verificationMessage, 0, verificationMessage.length);
+ g2d.setColor(new Color(128, 128, 128, 128));
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+ g2d.setColor(fontColor);
+ g2d.drawChars(verificationMessage, 0, verificationMessage.length, getWidth()/2 - msgWidth/2, getHeight()/2);
+ }
+ }
+
+ public void setVerifying(boolean b)
+ {
+ if (b != isVerifying) {
+ isVerifying = b;
+ SwingUtilities.invokeLater(new Runnable() {public void run() {
+ repaint();
+ }});
+ }
+ }
+
+ // super.paintComponent clears offscreen pixmap,
+ // since we're using double buffering by default.
+ protected void clear(Graphics g)
+ {
+ super.paintComponent(g);
+ }
+}
Propchange: incubator/cassandra/trunk/contrib/circuit/src/org/apache/cassandra/contrib/circuit/RingPanel.java
------------------------------------------------------------------------------
svn:eol-style = native