├── .gitignore ├── README.md ├── bin ├── run.bat ├── run.sh └── zkSetting.txt ├── docs ├── readme1.jpg ├── readme2.jpg └── readme3.jpg ├── pom.xml └── src └── main ├── java ├── com │ └── xin │ │ ├── ZkClientWrap.java │ │ ├── ZkConfService.java │ │ ├── ZkNode.java │ │ ├── ZkNodeInfo.java │ │ ├── ZookeeperVisualizer.java │ │ ├── controller │ │ ├── ConfSettingController.java │ │ ├── NodeAddController.java │ │ └── RootController.java │ │ ├── util │ │ ├── AlertUtils.java │ │ ├── CommandUtils.java │ │ ├── FuzzyMatchUtils.java │ │ ├── StringUtil.java │ │ └── ZkUtils.java │ │ └── view │ │ ├── AboutDialog.java │ │ ├── FXMLDialog.java │ │ ├── NodeInfoEditProxy.java │ │ ├── ZkConnectionTask.java │ │ ├── ZkExceptionDialog.java │ │ ├── ZkNodeTreeItem.java │ │ ├── conf │ │ ├── ProgressDialog.java │ │ ├── SearchFilterObservalbeList.java │ │ └── ZkConfListView.java │ │ ├── tab │ │ └── ZkTab.java │ │ └── zktreeview │ │ ├── ArrowChangeListener.java │ │ ├── ChangeSelectDataChangeListener.java │ │ ├── SearchTextField.java │ │ ├── TreeCellSkin.java │ │ ├── ZkNodeTreeCell.java │ │ └── ZkTreeView.java └── org │ └── I0Itec │ └── zkclient │ ├── MyZkClient.java │ └── MyZkPathUtil.java └── resources ├── about.txt ├── css └── style.css ├── fxml ├── confSetting.fxml ├── connectTab.fxml ├── nodeAdd.fxml ├── root.fxml └── root_back.fxml ├── icons ├── add.png └── root.png ├── log4j2.xml └── welcomeInfo.txt /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | .project 4 | .DS_Store 5 | 6 | ### STS ### 7 | .apt_generated 8 | .classpath 9 | .factorypath 10 | .project 11 | .settings 12 | .springBeans 13 | .sts4-cache 14 | 15 | ### IntelliJ IDEA ### 16 | .idea 17 | *.iws 18 | *.iml 19 | *.ipr 20 | 21 | ### NetBeans ### 22 | nbproject/private/ 23 | build/ 24 | nbbuild/ 25 | dist/ 26 | nbdist/ 27 | .nb-gradle/ 28 | log 29 | /bin/zkSetting.txt 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zookeeper-visualizer 2 | 3 | zookeeper的可视化管理工具 4 | 5 | ## 简介 6 | 7 | zookeeper-visualizer是zookeeper的可视化管理工具, 用Java8编写, 使用JAVAFX 8 | 2.0进行ui设计 9 | 10 | 11 | 会持续更新, 欢迎提需求 12 | ![image](https://raw.githubusercontent.com/xin497668869/zookeeper-visualizer/master/docs/readme1.jpg) 13 | 14 | ## 环境 15 | 16 | - jdk 1.8.0_60+ 17 | 18 | 19 | ## 功能 20 | 21 | 1. 树形箭头往下即可监听对应节点 22 | 23 | 2. 可通过search框进行节点搜索, 搜索到会对节点进行 24 | 3. 右键节点即可新增节点或者删除节点(递归删除) 25 | 26 | 27 | 连接断开时: 28 | ![image](https://raw.githubusercontent.com/xin497668869/zookeeper-visualizer/master/docs/readme2.jpg) 29 | 30 | 31 | zk新增节点 32 | ![image](https://raw.githubusercontent.com/xin497668869/zookeeper-visualizer/master/docs/readme3.jpg) 33 | 34 | # zookeeper-visualizer 35 | 36 | zookeeper的可视化管理工具 37 | 38 | ## 简介 39 | 40 | zookeeper-visualizer是zookeeper的可视化管理工具, 用Java8编写, 使用JAVAFX 41 | 2.0进行ui设计 42 | 43 | 44 | 会持续更新, 欢迎提需求 45 | ![image](https://raw.githubusercontent.com/xin497668869/zookeeper-visualizer/master/docs/readme1.jpg) 46 | 47 | ## 环境 48 | 49 | - jdk 1.8.0_60+ 50 | 51 | 52 | ## 功能 53 | 54 | 1. 树形箭头往下即可监听对应节点 55 | 56 | 2. 可通过search框进行节点搜索, 搜索到会对节点进行 57 | 3. 右键节点即可新增节点或者删除节点(递归删除) 58 | 59 | 60 | 连接断开时: 61 | ![image](https://raw.githubusercontent.com/xin497668869/zookeeper-visualizer/master/docs/readme2.jpg) 62 | 63 | 64 | zk新增节点 65 | ![image](https://raw.githubusercontent.com/xin497668869/zookeeper-visualizer/master/docs/readme3.jpg) 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | asdfdfasdfo 77 | `laksdjflkjsadf` 78 | 79 | 80 | 81 | alksdjflkjasdf 82 | 83 | -------------------------------------------------------------------------------- /bin/run.bat: -------------------------------------------------------------------------------- 1 | java -jar .\zookeeper-visualizer.jar -------------------------------------------------------------------------------- /bin/run.sh: -------------------------------------------------------------------------------- 1 | java -jar .\zookeeper-visualizer.jar -------------------------------------------------------------------------------- /bin/zkSetting.txt: -------------------------------------------------------------------------------- 1 | [{"address":"localhost:2181","connectTimeout":5000,"id":"8cbdc5529f96437681b6a42dc69bb380","name":"aaa本机asdf","sessionTimeout":5000},{"address":"172.18.80.56:2181","connectTimeout":1,"id":"d0d5dcdee3bb49e29fe19e5776cf6112","name":"本机","sessionTimeout":1},{"address":"sr-dev-zk-cluster-1.gz.cvte.cn:2181","connectTimeout":1000,"id":"a0f7cd5f220c411982dfb7aeae3a3c0a","name":"本机","sessionTimeout":1000},{"address":"sr-test-zk-cluster-1.gz.cvte.cn:2181","connectTimeout":5000,"id":"9b1c9966305f464891512e3ed79f7972","name":"新连接","sessionTimeout":5000}] -------------------------------------------------------------------------------- /docs/readme1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xin497668869/zookeeper-visualizer/41d69c56dac5d665b6ed4f7f48d024568f1b5374/docs/readme1.jpg -------------------------------------------------------------------------------- /docs/readme2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xin497668869/zookeeper-visualizer/41d69c56dac5d665b6ed4f7f48d024568f1b5374/docs/readme2.jpg -------------------------------------------------------------------------------- /docs/readme3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xin497668869/zookeeper-visualizer/41d69c56dac5d665b6ed4f7f48d024568f1b5374/docs/readme3.jpg -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.xin 8 | zookeeper-visualizer 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | 13 | UTF-8 14 | UTF-8 15 | 1.8 16 | 17 | 18 | 19 | 20 | commons-io 21 | commons-io 22 | 2.5 23 | 24 | 25 | org.projectlombok 26 | lombok 27 | 1.14.8 28 | 29 | 30 | com.alibaba 31 | fastjson 32 | 1.2.29 33 | 34 | 35 | 36 | com.101tec 37 | zkclient 38 | 0.11 39 | 40 | 41 | log4j 42 | log4j 43 | 44 | 45 | slf4j-log4j12 46 | org.slf4j 47 | 48 | 49 | slf4j-api 50 | org.slf4j 51 | 52 | 53 | 54 | 55 | 56 | org.slf4j 57 | slf4j-api 58 | 1.7.28 59 | 60 | 61 | org.apache.logging.log4j 62 | log4j-core 63 | 2.12.1 64 | 65 | 66 | org.apache.logging.log4j 67 | log4j-api 68 | 2.12.1 69 | 70 | 71 | org.apache.logging.log4j 72 | log4j-slf4j-impl 73 | 2.12.1 74 | 75 | 76 | org.controlsfx 77 | controlsfx 78 | 8.40.14 79 | 80 | 81 | 82 | 83 | 84 | zookeeper-visualizer 85 | 86 | 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-assembly-plugin 91 | 2.5.5 92 | 93 | 94 | 95 | com.xin.ZookeeperVisualizer 96 | 97 | false 98 | true 99 | lib/ 100 | 101 | 102 | . 103 | 104 | 105 | 106 | jar-with-dependencies 107 | 108 | 109 | 110 | 111 | make-assembly 112 | package 113 | 114 | single 115 | 116 | 117 | 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-antrun-plugin 122 | 123 | 124 | package 125 | 126 | run 127 | 128 | 129 | 130 | 131 | 132 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | org.apache.maven.plugins 143 | maven-source-plugin 144 | 2.2.1 145 | 146 | 147 | attach-sources 148 | 149 | jar-no-fork 150 | 151 | 152 | 153 | 154 | 155 | org.apache.maven.plugins 156 | maven-compiler-plugin 157 | 3.8.0 158 | 159 | ${java.version} 160 | ${java.version} 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /src/main/java/com/xin/ZkClientWrap.java: -------------------------------------------------------------------------------- 1 | package com.xin; 2 | 3 | import com.xin.ZkConfService.ZkConf; 4 | import com.xin.util.AlertUtils; 5 | import com.xin.view.ZkExceptionDialog; 6 | import com.xin.view.zktreeview.ArrowChangeListener; 7 | import javafx.application.Platform; 8 | import lombok.Getter; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.I0Itec.zkclient.IZkDataListener; 11 | import org.I0Itec.zkclient.IZkStateListener; 12 | import org.I0Itec.zkclient.MyZkClient; 13 | import org.I0Itec.zkclient.exception.ZkInterruptedException; 14 | import org.I0Itec.zkclient.exception.ZkNodeExistsException; 15 | import org.apache.zookeeper.CreateMode; 16 | import org.apache.zookeeper.KeeperException; 17 | import org.apache.zookeeper.data.Stat; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.concurrent.ExecutorService; 22 | import java.util.concurrent.Executors; 23 | 24 | /** 25 | * @author 497668869@qq.com 26 | * @since 1.0 27 | */ 28 | @Slf4j 29 | public class ZkClientWrap { 30 | @Getter 31 | private final MyZkClient zkClient; 32 | @Getter 33 | private ZkConf zkConf; 34 | private ExecutorService async = Executors.newFixedThreadPool(2); 35 | 36 | public ZkClientWrap(MyZkClient zkClient, ZkConf zkConf) { 37 | this.zkClient = zkClient; 38 | this.zkConf = zkConf; 39 | } 40 | 41 | public synchronized void unsubscribeDataChanges(String path, IZkDataListener listener) { 42 | try { 43 | zkClient.unsubscribeDataChanges(path, listener); 44 | } catch (Exception e) { 45 | log.error("取消监听节点数据变化信息异常", e); 46 | new ZkExceptionDialog("取消监听节点数据变化信息异常", e).showUi(); 47 | } 48 | } 49 | 50 | public synchronized void subscribeDataChanges(String path, IZkDataListener listener) { 51 | try { 52 | zkClient.subscribeDataChanges(path, listener); 53 | } catch (Exception e) { 54 | new ZkExceptionDialog("新增监听节点数据变化信息异常", e).showUi(); 55 | log.error("新增监听节点数据变化信息异常", e); 56 | 57 | } 58 | } 59 | 60 | public synchronized String readData(String nodePath, Stat stat) { 61 | try { 62 | return zkClient.readData(nodePath, stat); 63 | } catch (Exception e) { 64 | new ZkExceptionDialog("读取节点数据异常", e).showUi(); 65 | log.error("读取节点数据异常", e); 66 | } 67 | return ""; 68 | } 69 | 70 | public synchronized void create(String path, String value, CreateMode zkNodeType) { 71 | try { 72 | zkClient.create(path, value, zkNodeType); 73 | } catch (ZkNodeExistsException e) { 74 | AlertUtils.showErrorAlert("创建节点异常, 节点已存在", e.getMessage()); 75 | log.error("创建节点异常, 节点已存在", e); 76 | new ZkExceptionDialog("读取节点数据异常", e).showUi(); 77 | } catch (IllegalArgumentException e) { 78 | AlertUtils.showErrorAlert("创建节点异常", e.getMessage()); 79 | log.error("创建节点异常", e); 80 | new ZkExceptionDialog("读取节点数据异常", e).showUi(); 81 | } catch (ZkInterruptedException e) { 82 | AlertUtils.showErrorAlert("创建节点异常, 操作被打断", e.getMessage()); 83 | log.error("创建节点异常, 操作被打断", e); 84 | new ZkExceptionDialog("读取节点数据异常", e).showUi(); 85 | } catch (Exception e) { 86 | if (e.getCause() instanceof KeeperException.NoChildrenForEphemeralsException) { 87 | AlertUtils.showErrorAlert("创建节点异常, 临时节点不能有子节点", e.getMessage()); 88 | new ZkExceptionDialog("读取节点数据异常", e).showUi(); 89 | } else { 90 | AlertUtils.showErrorAlert("创建节点异常", e.getMessage()); 91 | new ZkExceptionDialog("读取节点数据异常", e).showUi(); 92 | } 93 | 94 | log.error("zkClient执行异常", e); 95 | } 96 | } 97 | 98 | public synchronized boolean deleteRecursive(String path) { 99 | try { 100 | return zkClient.deleteRecursive(path); 101 | } catch (Exception e) { 102 | AlertUtils.showErrorAlert("删除节点异常", e.getMessage()); 103 | log.error("zkClient执行异常", e); 104 | new ZkExceptionDialog("读取节点数据异常", e).showUi(); 105 | } 106 | return false; 107 | } 108 | 109 | public synchronized void subscribeChildChanges(String path, ArrowChangeListener arrowChangeListener) { 110 | try { 111 | zkClient.subscribeChildChanges(path, arrowChangeListener); 112 | } catch (Exception e) { 113 | AlertUtils.showErrorAlert("新增监听节点值变化异常", e.getMessage()); 114 | log.error("zkClient执行异常", e); 115 | } 116 | } 117 | 118 | public synchronized List getChildren(String path) { 119 | try { 120 | return zkClient.getChildren(path); 121 | } catch (Exception e) { 122 | AlertUtils.showErrorAlert("获取子节点数据异常", e.getMessage()); 123 | log.error("zkClient执行异常", e); 124 | } 125 | return new ArrayList<>(); 126 | } 127 | 128 | public synchronized void unsubscribeChildChanges(String path, ArrowChangeListener arrowChangeListener) { 129 | try { 130 | zkClient.unsubscribeChildChanges(path, arrowChangeListener); 131 | } catch (Exception e) { 132 | AlertUtils.showErrorAlert("取消监听节点值变化异常", e.getMessage()); 133 | log.error("zkClient执行异常", e); 134 | } 135 | } 136 | 137 | public synchronized void unsubscribeChildChanges(String path) { 138 | try { 139 | zkClient.unsubscribeChildChanges(path); 140 | } catch (Exception e) { 141 | AlertUtils.showErrorAlert("取消监听节点值变化异常", e.getMessage()); 142 | log.error("zkClient执行异常", e); 143 | } 144 | } 145 | 146 | public void subscribeStateChanges(IZkStateListener iZkStateListener) { 147 | zkClient.subscribeStateChanges(iZkStateListener); 148 | 149 | } 150 | 151 | public void writeData(String path, String value) { 152 | async.submit(() -> { 153 | try { 154 | zkClient.writeData(path, value); 155 | } catch (Exception e) { 156 | log.error("写入zk异常 " + path + " " + value + " ", e); 157 | Platform.runLater(() -> AlertUtils.showErrorAlert("提示", "写入zk异常 " + path + " " + value + " " + e.toString())); 158 | } 159 | }); 160 | } 161 | 162 | public void close() { 163 | zkClient.close(); 164 | } 165 | 166 | public void unsubscribeDataChanges(String path) { 167 | try { 168 | zkClient.unsubscribeDataChanges(path); 169 | } catch (Exception e) { 170 | AlertUtils.showErrorAlert("取消监听节点值变化异常", e.getMessage()); 171 | log.error("zkClient执行异常", e); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/com/xin/ZkConfService.java: -------------------------------------------------------------------------------- 1 | package com.xin; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.xin.controller.ConfSettingController; 5 | import com.xin.view.FXMLDialog; 6 | import com.xin.view.conf.ZkConfListView; 7 | import javafx.scene.control.ListView; 8 | import lombok.Data; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.commons.io.FileUtils; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | import java.io.Serializable; 15 | import java.nio.charset.StandardCharsets; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.Objects; 19 | import java.util.UUID; 20 | 21 | /** 22 | * @author 497668869@qq.com 23 | * @since 1.0 24 | */ 25 | @Slf4j 26 | public class ZkConfService { 27 | public static final String CONF_FILE_NAME = "zkSetting.txt"; 28 | public static ZkConfService zkConfService = new ZkConfService(); 29 | private static List zkConfs; 30 | 31 | public static ZkConfService getService() { 32 | return zkConfService; 33 | } 34 | 35 | public static void createSaveUi(ZkConf zkConf, ZkConfListView zkConfListView) { 36 | FXMLDialog fxmlDialog = new FXMLDialog<>("/fxml/confSetting.fxml"); 37 | fxmlDialog.init(); 38 | ConfSettingController controller = fxmlDialog.getController(); 39 | if (zkConf == null) { 40 | fxmlDialog.setTitle("创建连接"); 41 | controller.init(zkConfListView); 42 | } else { 43 | controller.init(zkConf, zkConfListView); 44 | fxmlDialog.setTitle("修改连接"); 45 | } 46 | fxmlDialog.show(); 47 | 48 | } 49 | 50 | public int saveZkConf(ZkConf zkConf, ListView confListView) { 51 | for (ZkConf zkConf1 : zkConfs) { 52 | if (zkConf1.id.equalsIgnoreCase(zkConf.getId())) { 53 | zkConf1.updateConf(zkConf); 54 | saveInFile(); 55 | confListView.getItems() 56 | .stream() 57 | .filter(conf2 -> conf2.getId() 58 | .equals(zkConf.getId())) 59 | .forEach(c -> c.updateConf(zkConf)); 60 | confListView.refresh(); 61 | return 0; 62 | } 63 | } 64 | zkConfs.add(zkConf); 65 | saveInFile(); 66 | confListView.getItems() 67 | .add(zkConf); 68 | confListView.refresh(); 69 | return 1; 70 | } 71 | 72 | public void removeZkConf(ZkConf zkConf, ListView confListView) { 73 | zkConfs.remove(zkConf); 74 | saveInFile(); 75 | confListView.getItems() 76 | .removeIf(conf2 -> { 77 | return conf2.getId() 78 | .equals(zkConf.getId()); 79 | }); 80 | confListView.refresh(); 81 | } 82 | 83 | public List getZkConf() { 84 | try { 85 | File file = new File(CONF_FILE_NAME); 86 | if (file.exists()) { 87 | String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8); 88 | zkConfs = JSON.parseArray(content, ZkConf.class); 89 | for (ZkConf zkConf : zkConfs) { 90 | if (zkConf.getConnectTimeout() == null) { 91 | zkConf.setConnectTimeout(5000); 92 | } 93 | if (zkConf.getSessionTimeout() == null) { 94 | zkConf.setSessionTimeout(5000); 95 | } 96 | } 97 | } else { 98 | zkConfs = new ArrayList<>(); 99 | } 100 | } catch (IOException e) { 101 | throw new RuntimeException(e); 102 | } 103 | return zkConfs; 104 | } 105 | 106 | private void saveInFile() { 107 | try { 108 | FileUtils.writeByteArrayToFile(new File(CONF_FILE_NAME), JSON.toJSONBytes(zkConfs), false); 109 | } catch (IOException e) { 110 | log.error("保存配置异常", e); 111 | } 112 | } 113 | 114 | @Data 115 | public static class ZkConf implements Serializable { 116 | private String id; 117 | private String name; 118 | private Integer connectTimeout; 119 | private Integer sessionTimeout; 120 | private String address; 121 | 122 | public ZkConf() { 123 | } 124 | 125 | public ZkConf copy() { 126 | return new ZkConf(id, name, address, connectTimeout, sessionTimeout); 127 | } 128 | 129 | public ZkConf(String id, 130 | String name, 131 | String address, 132 | Integer connectTimeout, 133 | Integer sessionTimeout) { 134 | if (id == null || id.isEmpty()) { 135 | this.id = UUID.randomUUID() 136 | .toString() 137 | .replace("-", ""); 138 | } else { 139 | this.id = id; 140 | } 141 | this.name = name; 142 | this.address = address; 143 | this.connectTimeout = connectTimeout; 144 | this.sessionTimeout = sessionTimeout; 145 | } 146 | 147 | public void updateConf(ZkConf zkConf) { 148 | name = zkConf.name; 149 | address = zkConf.address; 150 | this.connectTimeout = zkConf.connectTimeout; 151 | this.sessionTimeout = zkConf.sessionTimeout; 152 | } 153 | 154 | @Override 155 | public int hashCode() { 156 | return Objects.hash(super.hashCode(), id); 157 | } 158 | 159 | @Override 160 | public boolean equals(Object o) { 161 | if (this == o) { 162 | return true; 163 | } 164 | if (o == null || getClass() != o.getClass()) { 165 | return false; 166 | } 167 | if (!super.equals(o)) { 168 | return false; 169 | } 170 | ZkConf zkConf = (ZkConf) o; 171 | return Objects.equals(id, zkConf.id); 172 | } 173 | 174 | @Override 175 | public String toString() { 176 | return name + "(" + address + ")"; 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/com/xin/ZkNode.java: -------------------------------------------------------------------------------- 1 | package com.xin; 2 | 3 | import com.xin.view.ZkNodeTreeItem; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.Getter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * @author 497668869@qq.com 13 | * @since 1.0 14 | */ 15 | @Data 16 | @AllArgsConstructor 17 | public class ZkNode { 18 | @Getter 19 | private String path; 20 | private String name; 21 | private boolean childExpand; 22 | private List children; 23 | private ZkNode parent; 24 | private ZkNodeTreeItem treeItem; 25 | 26 | public ZkNode(String path, String name) { 27 | this.path = path; 28 | this.name = name; 29 | childExpand = false; 30 | } 31 | 32 | public ZkNode getParent() { 33 | return parent; 34 | } 35 | 36 | public void setParent(ZkNode parent) { 37 | this.parent = parent; 38 | } 39 | 40 | 41 | 42 | public void addChild(ZkNode zkNode) { 43 | if (children == null) { 44 | children = new ArrayList<>(); 45 | } 46 | children.add(zkNode); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return name; 52 | } 53 | 54 | public ZkNodeTreeItem getTreeItem() { 55 | return treeItem; 56 | } 57 | 58 | public void setTreeItem(ZkNodeTreeItem treeItem) { 59 | this.treeItem = treeItem; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/xin/ZkNodeInfo.java: -------------------------------------------------------------------------------- 1 | package com.xin; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | /** 10 | * @author jurnlee 11 | * @date 2018/11/23 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | public class ZkNodeInfo implements Serializable { 16 | 17 | private String path; 18 | 19 | private String name; 20 | 21 | private String data; 22 | 23 | private List children; 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/xin/ZookeeperVisualizer.java: -------------------------------------------------------------------------------- 1 | package com.xin; /** 2 | * @author 497668869@qq.com 3 | * @since 1.0 4 | */ 5 | 6 | import javafx.application.Application; 7 | import javafx.fxml.FXMLLoader; 8 | import javafx.scene.Scene; 9 | import javafx.scene.image.Image; 10 | import javafx.scene.layout.BorderPane; 11 | import javafx.stage.Stage; 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | import java.io.IOException; 15 | 16 | @Slf4j 17 | public class ZookeeperVisualizer extends Application { 18 | 19 | public static void main(String[] args) { 20 | launch(args); 21 | } 22 | 23 | @Override 24 | public void start(Stage primaryStage) { 25 | System.setProperty("jute.maxbuffer", String.valueOf(5120 * 1024)); 26 | try { 27 | BorderPane root = FXMLLoader.load(getClass().getResource("/fxml/root.fxml")); 28 | 29 | primaryStage.setTitle("zookeeper-visualizer"); 30 | primaryStage.getIcons() 31 | .add(new Image(ZookeeperVisualizer.class.getClassLoader() 32 | .getResourceAsStream("icons/root.png"))); 33 | Scene scene = new Scene(root); 34 | 35 | primaryStage.setScene(scene); 36 | primaryStage.show(); 37 | } catch (IOException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/xin/controller/ConfSettingController.java: -------------------------------------------------------------------------------- 1 | package com.xin.controller; 2 | 3 | import com.xin.ZkConfService; 4 | import com.xin.ZkConfService.ZkConf; 5 | import com.xin.view.conf.ZkConfListView; 6 | import javafx.application.Platform; 7 | import javafx.fxml.Initializable; 8 | import javafx.scene.control.Button; 9 | import javafx.scene.control.TextField; 10 | import javafx.scene.input.MouseEvent; 11 | import javafx.scene.layout.BorderPane; 12 | import javafx.scene.text.Text; 13 | import javafx.stage.Stage; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.I0Itec.zkclient.MyZkClient; 16 | 17 | import java.net.URL; 18 | import java.util.ResourceBundle; 19 | 20 | /** 21 | * @author 497668869@qq.com 22 | * @since 1.0 23 | */ 24 | @Slf4j 25 | public class ConfSettingController implements Initializable { 26 | 27 | public TextField nameTextField; 28 | public TextField addressTextField; 29 | public TextField connectTimeout; 30 | public TextField sessionTimeout; 31 | 32 | public BorderPane root; 33 | public Button createConfButton; 34 | public Button testConnectButton; 35 | public Text testResultMsg; 36 | private ZkConfListView zkConfListView; 37 | private String currentZkConfId; 38 | 39 | @Override 40 | public void initialize(URL location, ResourceBundle resources) { 41 | 42 | } 43 | 44 | public ZkConf getCurrentUiZkConf() { 45 | return new ZkConf(currentZkConfId, 46 | nameTextField.getText(), 47 | addressTextField.getText(), 48 | Integer.valueOf(sessionTimeout.getText()), 49 | Integer.valueOf(connectTimeout.getText())); 50 | } 51 | 52 | public void onClickToCreateConf(MouseEvent mouseEvent) { 53 | 54 | ZkConfService.getService() 55 | .saveZkConf(getCurrentUiZkConf(), zkConfListView); 56 | Stage window = (Stage) createConfButton.getScene() 57 | .getWindow(); 58 | window.close(); 59 | } 60 | 61 | public void onClickToTestConnect(MouseEvent mouseEvent) { 62 | testConnectButton.setDisable(true); 63 | 64 | testResultMsg.setText("连接中..."); 65 | 66 | new Thread(() -> { 67 | MyZkClient zkClient = null; 68 | try { 69 | ZkConf zkConf = getCurrentUiZkConf(); 70 | zkClient = new MyZkClient(zkConf.getAddress(), 71 | zkConf.getSessionTimeout(), 72 | zkConf.getConnectTimeout()); 73 | Platform.runLater(() -> testResultMsg.setText("连接成功!")); 74 | } catch (Exception e) { 75 | log.warn("连接异常", e); 76 | Platform.runLater(() -> { 77 | testResultMsg.setText("连接失败!\n" + e.getMessage()); 78 | 79 | }); 80 | } finally { 81 | if (zkClient != null) { 82 | zkClient.close(); 83 | } 84 | } 85 | if (testConnectButton.isDisabled()) { 86 | testConnectButton.setDisable(false); 87 | } 88 | }).start(); 89 | } 90 | 91 | public void init(ZkConfListView zkConfListView) { 92 | this.zkConfListView = zkConfListView; 93 | currentZkConfId = null; 94 | initComponent(new ZkConf(null, 95 | "", 96 | "localhost:2181", 97 | 5000, 98 | 5000)); 99 | } 100 | 101 | public void init(ZkConf zkConf, ZkConfListView zkConfListView) { 102 | this.zkConfListView = zkConfListView; 103 | currentZkConfId = zkConf.getId(); 104 | initComponent(zkConf); 105 | } 106 | 107 | private void initComponent(ZkConf zkConf) { 108 | this.nameTextField.setText(zkConf.getName()); 109 | this.connectTimeout.setText(String.valueOf(zkConf.getConnectTimeout())); 110 | this.sessionTimeout.setText(String.valueOf(zkConf.getSessionTimeout())); 111 | this.addressTextField.setText(zkConf.getAddress()); 112 | this.connectTimeout.setText(String.valueOf(zkConf.getConnectTimeout())); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/xin/controller/NodeAddController.java: -------------------------------------------------------------------------------- 1 | package com.xin.controller; 2 | 3 | import javafx.fxml.Initializable; 4 | import javafx.scene.control.*; 5 | import javafx.scene.input.MouseEvent; 6 | import javafx.stage.Stage; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | import org.apache.zookeeper.CreateMode; 10 | 11 | import java.net.URL; 12 | import java.util.ResourceBundle; 13 | 14 | /** 15 | * @author 497668869@qq.com 16 | * @since 1.0 17 | */ 18 | public class NodeAddController implements Initializable { 19 | public Label parentPathLabel; 20 | public RadioButton persistentRadioButton; 21 | public RadioButton ephemeralRadioButton; 22 | public TextArea nodeValueTextArea; 23 | public TextField nodeNameTextArea; 24 | public Button okButton; 25 | public RadioButton persistentSequentialRadioButton; 26 | public RadioButton ephemeralSequentialRadioButton; 27 | 28 | private ToggleGroup toggleGroup = new ToggleGroup(); 29 | 30 | @Override 31 | public void initialize(URL location, ResourceBundle resources) { 32 | nodeValueTextArea.setText(""); 33 | 34 | persistentRadioButton.setToggleGroup(toggleGroup); 35 | ephemeralRadioButton.setToggleGroup(toggleGroup); 36 | persistentSequentialRadioButton.setToggleGroup(toggleGroup); 37 | ephemeralSequentialRadioButton.setToggleGroup(toggleGroup); 38 | persistentRadioButton.setSelected(true); 39 | 40 | } 41 | 42 | public CreateMode getZkNodeTypeEnum() { 43 | if (persistentRadioButton.isSelected()) { 44 | return CreateMode.PERSISTENT; 45 | } else if (ephemeralRadioButton.isSelected()) { 46 | return CreateMode.EPHEMERAL; 47 | } else if (persistentSequentialRadioButton.isSelected()) { 48 | return CreateMode.PERSISTENT_SEQUENTIAL; 49 | } else if (ephemeralSequentialRadioButton.isSelected()) { 50 | return CreateMode.EPHEMERAL_SEQUENTIAL; 51 | } else { 52 | return CreateMode.PERSISTENT; 53 | } 54 | } 55 | 56 | public void mouseClickedToCreateNode(MouseEvent mouseEvent) { 57 | if (getZkNodeTypeEnum() != null) { 58 | String parentPath = parentPathLabel.getText(); 59 | okButton.getScene() 60 | .setUserData( 61 | new NodeAddConf(parentPath + nodeNameTextArea.getText(), nodeValueTextArea.getText(), getZkNodeTypeEnum())); 62 | Stage window = (Stage) okButton.getScene() 63 | .getWindow(); 64 | window.close(); 65 | } 66 | } 67 | 68 | @Data 69 | @AllArgsConstructor 70 | public static class NodeAddConf { 71 | 72 | private String path; 73 | private String value; 74 | private CreateMode zkNodeType; 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/xin/controller/RootController.java: -------------------------------------------------------------------------------- 1 | package com.xin.controller; 2 | 3 | import com.xin.ZkConfService; 4 | import com.xin.util.FuzzyMatchUtils; 5 | import com.xin.util.StringUtil; 6 | import com.xin.view.ZkExceptionDialog; 7 | import com.xin.view.conf.SearchFilterObservalbeList; 8 | import com.xin.view.conf.ZkConfListView; 9 | import javafx.application.Platform; 10 | import javafx.collections.FXCollections; 11 | import javafx.event.ActionEvent; 12 | import javafx.fxml.Initializable; 13 | import javafx.scene.control.Hyperlink; 14 | import javafx.scene.control.MenuItem; 15 | import javafx.scene.control.TabPane; 16 | import javafx.scene.control.TextField; 17 | import lombok.extern.slf4j.Slf4j; 18 | import org.apache.commons.io.IOUtils; 19 | import org.controlsfx.control.HyperlinkLabel; 20 | 21 | import java.awt.*; 22 | import java.io.File; 23 | import java.net.URI; 24 | import java.net.URL; 25 | import java.nio.charset.StandardCharsets; 26 | import java.util.ResourceBundle; 27 | 28 | /** 29 | * @author 497668869@qq.com 30 | * @since 1.0 31 | */ 32 | @Slf4j 33 | public class RootController implements Initializable { 34 | 35 | public MenuItem exitBtn; 36 | public MenuItem newConnBtn; 37 | /** 38 | * 配置列表 39 | */ 40 | public ZkConfListView zkConfListView; 41 | public TextField filterTextField; 42 | public TabPane connectTabPane; 43 | public HyperlinkLabel welcomeInfo; 44 | 45 | @Override 46 | public void initialize(URL location, ResourceBundle resources) { 47 | try { 48 | welcomeInfo.setText(IOUtils.toString(RootController.class.getClassLoader() 49 | .getResource("welcomeInfo.txt") 50 | .toURI(), StandardCharsets.UTF_8)); 51 | welcomeInfo.setOnAction(event -> { 52 | Hyperlink link = (Hyperlink) event.getSource(); 53 | final String str = link.getText(); 54 | try { 55 | Desktop.getDesktop() 56 | .browse(new URI(str)); 57 | } catch (Exception e) { 58 | log.error("打开github网页失败", e); 59 | } 60 | }); 61 | 62 | zkConfListView.installConnectTrigger(connectTabPane); 63 | 64 | installConfSearchFilter(); 65 | 66 | } catch (Exception e) { 67 | log.info("初始化异常", e); 68 | new ZkExceptionDialog("初始化异常", e) 69 | .showUi(); 70 | } 71 | 72 | } 73 | 74 | public void exitBtnAction() { 75 | Platform.exit(); 76 | } 77 | 78 | public void openLogFiles(ActionEvent actionEvent) { 79 | File file = new File("logs/zookeeper-visualizer.log"); 80 | try { 81 | Desktop.getDesktop() 82 | .open(new File(file.getAbsolutePath())); 83 | } catch (Exception e) { 84 | log.error("打开日志文件异常", e); 85 | try { 86 | Desktop.getDesktop() 87 | .open(file.getParentFile()); 88 | } catch (Exception ex) { 89 | log.error("打开日志目录异常", ex); 90 | new ZkExceptionDialog("打开日志目录异常 日志目录:" + file.getPath(), e).showUi(); 91 | } 92 | } 93 | } 94 | 95 | public void openIssues(ActionEvent actionEvent) { 96 | try { 97 | Desktop.getDesktop() 98 | .browse(new URI("https://github.com/xin497668869/zookeeper-visualizer/issues/new")); 99 | } catch (Exception e) { 100 | log.error("打开issues失败", e); 101 | } 102 | } 103 | 104 | public void addNewConnect(ActionEvent actionEvent) { 105 | ZkConfService.createSaveUi(null, zkConfListView); 106 | } 107 | 108 | private void installConfSearchFilter() { 109 | zkConfListView.setItems(new SearchFilterObservalbeList<>(FXCollections.observableArrayList(ZkConfService.getService() 110 | .getZkConf()), zkConf -> { 111 | if (!StringUtil.isEmpty(filterTextField.getText())) { 112 | return FuzzyMatchUtils.match(zkConf.toString(), 113 | filterTextField.getText()); 114 | } else { 115 | return true; 116 | } 117 | })); 118 | filterTextField.textProperty() 119 | .addListener(observable -> { 120 | ((SearchFilterObservalbeList) zkConfListView.getItems()).refilter(); 121 | }); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/xin/util/AlertUtils.java: -------------------------------------------------------------------------------- 1 | package com.xin.util; 2 | 3 | import javafx.scene.control.Alert; 4 | import javafx.scene.control.Alert.AlertType; 5 | import javafx.scene.control.TextArea; 6 | import javafx.scene.layout.GridPane; 7 | 8 | /** 9 | * @author 497668869@qq.com 10 | * @since 1.0 11 | */ 12 | public class AlertUtils { 13 | 14 | public static void showErrorAlert(String title, String contentText) { 15 | Alert errorAlert = new Alert(Alert.AlertType.ERROR); 16 | errorAlert.setTitle(title); 17 | errorAlert.setHeaderText(null); 18 | errorAlert.setContentText(contentText); 19 | errorAlert.showAndWait(); 20 | } 21 | 22 | public static void showInfoAlert(String title, String contentText) { 23 | Alert errorAlert = new Alert(AlertType.INFORMATION); 24 | errorAlert.setTitle(title); 25 | errorAlert.setContentText(contentText); 26 | TextArea textArea = new TextArea(contentText); 27 | textArea.setEditable(false); 28 | textArea.setWrapText(true); 29 | GridPane gridPane = new GridPane(); 30 | gridPane.setMaxWidth(Double.MAX_VALUE); 31 | gridPane.add(textArea, 0, 0); 32 | errorAlert.getDialogPane() 33 | .setContent(gridPane); 34 | errorAlert.showAndWait(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/xin/util/CommandUtils.java: -------------------------------------------------------------------------------- 1 | package com.xin.util; 2 | 3 | import com.xin.ZkConfService.ZkConf; 4 | import org.apache.commons.io.IOUtils; 5 | 6 | import java.io.IOException; 7 | import java.net.Socket; 8 | import java.nio.charset.StandardCharsets; 9 | 10 | /** 11 | * @author 497668869@qq.com 12 | * @since 1.0 13 | */ 14 | public class CommandUtils { 15 | public static String result(String command, ZkConf zkConf) throws IOException { 16 | String[] split = zkConf.getAddress() 17 | .split(":"); 18 | Socket socket = new Socket(split[0], Integer.parseInt(split[1])); 19 | socket.getOutputStream() 20 | .write(command.getBytes(StandardCharsets.UTF_8)); 21 | String s = IOUtils.toString(socket.getInputStream(), StandardCharsets.UTF_8); 22 | socket.close(); 23 | return s; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/xin/util/FuzzyMatchUtils.java: -------------------------------------------------------------------------------- 1 | package com.xin.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | /** 6 | * @author 497668869@qq.com 7 | * @since 1.0 8 | */ 9 | @Slf4j 10 | public class FuzzyMatchUtils { 11 | 12 | 13 | public static boolean match(String name, String keyword) { 14 | return match(name.toLowerCase(), keyword.toLowerCase(), 0, 0); 15 | } 16 | 17 | private static boolean match(String name, String keyword, int nameIndex, int keywordIndex) { 18 | if (keyword.length() > 10) { 19 | //当key长度较大是, 使用contains可以加快匹配速度 20 | return name.contains(keyword); 21 | } 22 | if (nameIndex > name.length() 23 | || name.length() - nameIndex < keyword.length() - keywordIndex) { 24 | return false; 25 | } 26 | if (keyword.length() == keywordIndex) { 27 | return true; 28 | } 29 | for (int i = nameIndex; i < name.length(); i++) { 30 | if (i == 0 31 | || (Character.isUpperCase(name.charAt(i))) 32 | || (name.charAt(i) > 'z' || name.charAt(i) < 'a') 33 | || ((name.charAt(i - 1) > 'z' || name.charAt(i - 1) < 'a') && (name.charAt(i - 1) > 'Z' || name.charAt(i - 1) < 'A'))) { 34 | 35 | if (keyword.charAt(keywordIndex) == Character.toLowerCase(name.charAt(i))) { 36 | if (match(name, keyword, i + 1, keywordIndex + 1)) { 37 | return true; 38 | } 39 | } 40 | } 41 | } 42 | if (nameIndex != 0) { 43 | if (keyword.charAt(keywordIndex) == Character.toLowerCase(name.charAt(nameIndex))) { 44 | if (match(name, keyword, nameIndex + 1, keywordIndex + 1)) { 45 | return true; 46 | } 47 | } 48 | } 49 | 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/xin/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package com.xin.util; 2 | 3 | /** 4 | * @author 497668869@qq.com 5 | * @since 1.0 6 | */ 7 | public class StringUtil { 8 | public static boolean isEmpty(String str) { 9 | return str == null || str.isEmpty(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/xin/util/ZkUtils.java: -------------------------------------------------------------------------------- 1 | package com.xin.util; 2 | 3 | import com.xin.ZkConfService.ZkConf; 4 | import org.I0Itec.zkclient.MyZkClient; 5 | import org.I0Itec.zkclient.exception.ZkMarshallingError; 6 | import org.I0Itec.zkclient.serialize.ZkSerializer; 7 | 8 | import java.nio.charset.StandardCharsets; 9 | 10 | /** 11 | * @author 497668869@qq.com 12 | * @since 1.0 13 | */ 14 | public class ZkUtils { 15 | public static void init() { 16 | } 17 | 18 | public static MyZkClient getConnection(ZkConf zkConf) { 19 | return new MyZkClient(zkConf.getAddress(), 20 | zkConf.getSessionTimeout(), 21 | zkConf.getConnectTimeout(), 22 | new ZkSerializer() { 23 | @Override 24 | public byte[] serialize(Object data) throws ZkMarshallingError { 25 | if (data == null) { 26 | return new byte[]{}; 27 | } else if (data instanceof String) { 28 | return ((String) data).getBytes(); 29 | } else { 30 | throw new RuntimeException("不支持的序列化类型 " + data.getClass()); 31 | } 32 | } 33 | 34 | @Override 35 | public Object deserialize(byte[] bytes) throws ZkMarshallingError { 36 | return new String(bytes, StandardCharsets.UTF_8); 37 | } 38 | }, 1000); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/xin/view/AboutDialog.java: -------------------------------------------------------------------------------- 1 | package com.xin.view; 2 | 3 | import javafx.scene.control.Dialog; 4 | import javafx.stage.Modality; 5 | import javafx.stage.StageStyle; 6 | import org.apache.commons.io.IOUtils; 7 | 8 | import java.nio.charset.StandardCharsets; 9 | 10 | /** 11 | * @author jurnlee 12 | * @date 2018/11/22 13 | */ 14 | public class AboutDialog extends Dialog { 15 | 16 | 17 | public AboutDialog() { 18 | getDialogPane().getScene().getWindow().setOnCloseRequest(event -> { 19 | close(); 20 | }); 21 | try { 22 | this.setContentText(IOUtils.toString(this.getClass().getClassLoader().getResource("about.txt").toURI(), StandardCharsets.UTF_8)); 23 | } catch (Exception e) { 24 | this.setContentText("欢迎使用zookeeper-visualizer!"); 25 | } 26 | this.setTitle("关于我们"); 27 | this.setHeight(400d); 28 | this.initModality(Modality.WINDOW_MODAL); 29 | this.initStyle(StageStyle.UTILITY); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/xin/view/FXMLDialog.java: -------------------------------------------------------------------------------- 1 | package com.xin.view; 2 | 3 | import javafx.fxml.FXMLLoader; 4 | import javafx.scene.Scene; 5 | import javafx.stage.Modality; 6 | import javafx.stage.Stage; 7 | import lombok.Getter; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * @author 497668869@qq.com 13 | * @since 1.0 14 | */ 15 | public class FXMLDialog { 16 | 17 | @Getter 18 | private Stage stage; 19 | @Getter 20 | private Scene scene; 21 | @Getter 22 | private T controller; 23 | private String xmlLocation; 24 | 25 | public FXMLDialog(String xmlLocation) { 26 | this.xmlLocation = xmlLocation; 27 | } 28 | 29 | public void init() { 30 | FXMLLoader loader = new FXMLLoader(); 31 | try { 32 | loader.setLocation(getClass().getResource(xmlLocation)); 33 | loader.load(); 34 | controller = loader.getController(); 35 | scene = new Scene(loader.getRoot()); 36 | stage = new Stage(); 37 | stage.setScene(scene); 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | 41 | } 42 | } 43 | 44 | public void show() { 45 | stage.initModality(Modality.APPLICATION_MODAL); 46 | stage.showAndWait(); 47 | } 48 | 49 | public void setTitle(String title) { 50 | stage.setTitle(title); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/xin/view/NodeInfoEditProxy.java: -------------------------------------------------------------------------------- 1 | package com.xin.view; 2 | 3 | import com.xin.ZkClientWrap; 4 | import com.xin.ZkNode; 5 | import com.xin.util.AlertUtils; 6 | import com.xin.util.CommandUtils; 7 | import javafx.application.Platform; 8 | import javafx.scene.control.Button; 9 | import javafx.scene.control.TextArea; 10 | import javafx.scene.control.TextField; 11 | import javafx.scene.control.TreeItem; 12 | import lombok.Getter; 13 | import lombok.extern.slf4j.Slf4j; 14 | import org.apache.zookeeper.data.Stat; 15 | 16 | import java.text.SimpleDateFormat; 17 | import java.util.Date; 18 | import java.util.List; 19 | 20 | /** 21 | * @author 497668869@qq.com 22 | * @since 1.0 23 | */ 24 | @Slf4j 25 | public class NodeInfoEditProxy { 26 | private final TextArea zkNodeStatTextArea; 27 | private final TextArea zkNodeDataTextArea; 28 | private final TextField zkPathTextField; 29 | private final Button reloadNodeValueButton; 30 | private final Button saveNodeValueButton; 31 | private final ZkClientWrap zkClientWrap; 32 | private final List