package com.kostbot.zoodirector.ui;
import com.kostbot.zoodirector.zookeepersync.ZookeeperSync;
import com.netflix.curator.framework.CuratorFramework;
import com.netflix.curator.framework.CuratorFrameworkFactory;
import com.netflix.curator.framework.state.ConnectionState;
import com.netflix.curator.framework.state.ConnectionStateListener;
import com.netflix.curator.retry.RetryOneTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.*;
public class ZooDirectorPanel extends JPanel {
private static final Logger logger = LoggerFactory.getLogger(ZooDirectorPanel.class);
private ZookeeperSync zookeeperSync;
private CuratorFramework client;
private final String connectionString;
private final int connectionRetryPeriod;
private volatile boolean online; // prevent operations if offline.
// Main UI
private final JPanel mainPanel;
// Zookeeper View UI
private final JSplitPane splitPane;
private final ZooDirectorNavPanel zooDirectorNavPanel;
private final ZooDirectorAddressPanel zooDirectorAddressPanel;
private final JTabbedPane tabbedPane;
private final ZooDirectorNodeEditPanel nodeEditPanel;
private final ZooDirectorWatchPanel watchPanel;
private final SwingWorker<Void, Void> connectionWorker;
/**
* Return the currently set connection string.
*
* @return connection string
*/
public String getConnectionString() {
return connectionString;
}
public ZookeeperSync getZookeeperSync() {
return zookeeperSync;
}
/**
* Panel used for editing specified zookeeper node
*
* @param connectionString zookeeper connection string
* @param connectionRetryPeriod time to sleep between retries
*/
public ZooDirectorPanel(String connectionString, int connectionRetryPeriod) {
this.connectionString = connectionString;
this.connectionRetryPeriod = connectionRetryPeriod;
this.setLayout(new BorderLayout());
mainPanel = new JPanel(new BorderLayout());
this.add(mainPanel, BorderLayout.CENTER);
// Logging UI Setup
ZooDirectorLogDialog zooDirectorLogDialog = new ZooDirectorLogDialog();
this.add(zooDirectorLogDialog.getLastLogPanel(), BorderLayout.SOUTH);
// Zookeeper View UI Setup
zooDirectorAddressPanel = new ZooDirectorAddressPanel(this);
zooDirectorNavPanel = new ZooDirectorNavPanel(this);
tabbedPane = new JTabbedPane();
nodeEditPanel = new ZooDirectorNodeEditPanel();
tabbedPane.add(nodeEditPanel, "View/Edit");
watchPanel = new ZooDirectorWatchPanel(this);
tabbedPane.add(watchPanel, "Watches");
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, zooDirectorNavPanel, tabbedPane);
splitPane.setOneTouchExpandable(true);
splitPane.setDividerLocation(200);
connectionWorker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() {
client = CuratorFrameworkFactory.newClient(
ZooDirectorPanel.this.connectionString,
new RetryOneTime(ZooDirectorPanel.this.connectionRetryPeriod)
);
client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
@Override
public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) {
switch (connectionState) {
case LOST:
case SUSPENDED:
online = false;
nodeEditPanel.setOffline();
logger.warn("connection to {} has been " + (connectionState == ConnectionState.LOST ? "lost" : "suspended") +
". Attempts will be made to reestablish the connection", ZooDirectorPanel.this.connectionString);
break;
case RECONNECTED:
logger.info("connection to {} has been reestablished", ZooDirectorPanel.this.connectionString);
default:
online = true;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
load();
}
});
}
}
});
// Responsible for managing all tree additions and removals.
zookeeperSync = new ZookeeperSync(client);
zookeeperSync.addListener(new ZookeeperSync.Listener() {
@Override
public void process(final ZookeeperSync.Event e) {
final boolean created = zooDirectorNavPanel.wasCreated(e.path);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
switch (e.type) {
case add:
zooDirectorNavPanel.addNodeToTree(e.path, created);
break;
case delete:
zooDirectorNavPanel.removeNodeFromTree(e.path);
break;
}
}
});
}
});
watchPanel.setZookeeperSync(zookeeperSync);
nodeEditPanel.setZookeeperSync(zookeeperSync);
logger.info("connecting to cluster {}", ZooDirectorPanel.this.connectionString);
client.start();
return null;
}
@Override
public void done() {
if (this.isCancelled()) {
logger.info("connection request cancelled");
close();
return;
}
}
};
}
public boolean isOnline() {
return online;
}
public boolean hasWatch(String path) {
return watchPanel.hasWatch(path);
}
public void addWatch(String path) {
watchPanel.addWatch(path);
}
public void removeWatch(String path) {
watchPanel.removeWatch(path);
}
public void viewEditTreeNode(String path) {
DefaultMutableTreeNode target = zooDirectorNavPanel.selectTreeNode(path);
if (target == null) {
logger.error("view/edit {} failed [path does not exist]", path);
} else {
nodeEditPanel.setZookeeperPath(path);
zooDirectorAddressPanel.setPath(path);
tabbedPane.setSelectedIndex(0);
zooDirectorNavPanel.grabFocus();
}
}
/**
* Load tree from zookeeper and display panel.
*/
private void load() {
if (online) {
logger.info("loading zookeeper nodes");
mainPanel.removeAll();
zooDirectorNavPanel.removeAll();
SwingWorker<Void, Void> swingWorker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() {
try {
zookeeperSync.watch();
} catch (Exception e) {
logger.error("Failed to execute ZookeeperSync watch [{}]", e);
}
return null;
}
@Override
protected void done() {
logger.info("loading zookeeper nodes complete");
}
};
swingWorker.execute();
mainPanel.add(zooDirectorAddressPanel, BorderLayout.NORTH);
mainPanel.add(splitPane, BorderLayout.CENTER);
refresh();
zooDirectorNavPanel.grabFocus();
}
}
/**
* Refresh UI
*/
private void refresh() {
this.revalidate();
this.repaint();
}
/**
* Start curator client
*/
public void connect() {
// Loading UI Setup
JPanel loadingPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = 1;
c.insets.top = c.insets.bottom = 5;
c.gridy = 0;
loadingPanel.add(new JLabel("Establishing connection to zookeeper @ " + connectionString), c);
c.gridy += 1;
JButton cancelConnectionButton = new JButton("Cancel");
cancelConnectionButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
connectionWorker.cancel(true);
close();
}
});
loadingPanel.add(cancelConnectionButton, c);
mainPanel.add(loadingPanel, BorderLayout.CENTER);
refresh();
connectionWorker.execute();
}
/**
* Close curator client
*/
public void close() {
connectionWorker.cancel(true);
client.close();
mainPanel.removeAll();
refresh();
}
}