/**
* 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.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;
import org.apache.commons.lang.time.FastDateFormat;
public class CircuitFrame extends JFrame implements ActionListener
{
private static final long serialVersionUID = 1L;
private static final String appTitle = "Circuit";
private static final Dimension defaultSize = new Dimension(550, 600);
private static final FastDateFormat DATE_FORMATTER = FastDateFormat.getInstance("HH:mm:ss");
private static final Lock verifyLock = new ReentrantLock();
private RingModel ringModel;
private RingPanel ringPanel;
private JTextArea statusOutput;
private JMenuBar menuBar;
private JMenuItem quitMI, verifyMI, aboutMI;
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] ", DATE_FORMATTER.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,
}